<commit_before>/*
*
*   Copyright © 2017 Luxon Jean-Pierre
*   https://gumichan01.github.io/
*
*   This library is under the MIT license
*
*   Luxon Jean-Pierre (Gumichan01)
*   luxon.jean.pierre@gmail.com
*
*/

#include <map>
#include <stdexcept>
#include "utf8_string.hpp"

typedef char byte_t;

UTF8string::UTF8string() noexcept : utf8length(0) {}

UTF8string::UTF8string(const std::string &str)
    : utf8data(str)
{
    if(!utf8_is_valid_())
        throw std::invalid_argument("Invalid UTF-8 string\n");

    utf8length = utf8_length_();
}

UTF8string::UTF8string(const UTF8string &u8str) noexcept
    : utf8data(u8str.utf8data), utf8length(u8str.utf8length) {}

const UTF8string& UTF8string::operator =(const char * str)
{
    utf8data = str;

    if(!utf8_is_valid_())
        throw std::invalid_argument("Invalid UTF-8 string\n");

    utf8length = utf8_length_();
    return *this;
}

const UTF8string& UTF8string::operator =(const std::string &str)
{
    utf8data = str;

    if(!utf8_is_valid_())
        throw std::invalid_argument("Invalid UTF-8 string\n");

    utf8length = utf8_length_();
    return *this;
}

UTF8string& UTF8string::operator =(const UTF8string &u8str) noexcept
{
    utf8data = u8str.utf8data;
    utf8length = u8str.utf8length;
    return *this;
}

const UTF8string& UTF8string::operator +=(const std::string &str)
{
    std::string s = utf8data;
    utf8data += str;

    if(!utf8_is_valid_())
    {
        utf8data = s;
        throw std::invalid_argument("Invalid UTF-8 string\n");
    }

    utf8length = utf8_length_();
    return *this;
}

const UTF8string& UTF8string::operator +=(const UTF8string &u8str)
{
    utf8data += u8str.utf8data;
    utf8length = utf8_length_();
    return *this;
}

const UTF8string& UTF8string::operator +=(const char * str)
{
    std::string s = utf8data;
    utf8data += str;

    if(!utf8_is_valid_())
    {
        utf8data = s;
        throw std::invalid_argument("Invalid UTF-8 string\n");
    }

    utf8length = utf8_length_();
    return *this;
}

// Check the validity of the string (utf-8 encoding)
bool UTF8string::utf8_is_valid_() const noexcept
{
    auto it = utf8data.begin();
    const auto itend = utf8data.end();

    while(it < itend)
    {
        if((0xF8 & *it) == 0xF0)
        {
            // The UTF-8 codepoint begin with 0b11110xxx -> 4-byte codepoint
            // If the iterator reach the end of the string before the
            // end of the 4-byte codepoint -> invalid string
            if((it + 1) == itend || (it + 2) == itend || (it + 3) == itend)
                return false;

            // Each of the following bytes is a value
            // between 0x80 and 0xBF
            if(((0xC0 & *(it + 1)) != 0x80) || ((0xC0 & *(it + 2)) != 0x80)
                    || ((0xC0 & *(it + 3)) != 0x80))
            {
                return false;
            }

            // If the first byte of the sequence is 0xF0
            // then the first continuation byte must be between 0x90 and 0xBF
            // otherwise, if the byte is 0xF4
            // then the first continuation byte must be between 0x80 and 0x8F
            if(*it == '\xF0')
            {
                if(*(it + 1) < '\x90' || *(it + 1) > '\xBF')
                    return false;
            }
            else if(*it == '\xF4')
            {
                if(*(it + 1) < '\x80' || *(it + 1) > '\x8F')
                    return false;
            }

            it += 4;    // Jump to the next codepoint
        }
        else if((0xF0 & *it) == 0xE0)
        {
            // The UTF-8 codepoint begin with 0b1110xxxx -> 3-byte codepoint
            if((it + 1) == itend || (it + 2) == itend)
                return false;

            // Each of the following bytes starts with
            // 0b10xxxxxx in a valid string
            if(((0xC0 & *(it + 1)) != 0x80) || ((0xC0 & *(it + 2)) != 0x80))
                return false;

            // If the first byte of the sequence is 0xE0
            // then the first continuation byte must be between 0xA0 and 0xBF
            // otherwise, if the byte is 0xF4
            // then the first continuation byte must be between 0x80 and 0x9F
            if(*it == '\xE0')
            {
                if(*(it + 1) < '\xA0' || *(it + 1) > '\xBF')
                    return false;
            }
            else if(*it == '\xED')
            {
                if(*(it + 1) < '\x80' || *(it + 1) > '\x9F')
                    return false;
            }

            it += 3;
        }
        else if((0xE0 & *it) == 0xC0)
        {
            // The UTF-8 codepoint begin with 0b110xxxxx -> 2-byte codepoint
            if((it + 1) == itend)
                return false;

            // The following byte starts with 0b10xxxxxx in a valid string
            if((0xC0 & *(it + 1)) != 0x80)
                return false;

            it += 2;
        }
        else if((0x80 & *it) == 0x00)
        {
            // The UTF-8 codepoint begin with 0b0xxxxxxx -> 1-byte codepoint
            it += 1;
        }
        else
        {
            // Invalid codepoint
            return false;
        }
    }

    return true;
}

// Compute the length of the utf-8 string (in number of codepoints)
size_t UTF8string::utf8_length_() const noexcept
{
    auto end_data = utf8data.end();
    auto it = utf8data.begin();
    size_t len = 0;

    while(it != end_data)
    {
        byte_t byte = *it;

        if (0xf0 == (0xf8 & byte))
        {
            // 4-byte utf8 character
            // (0b11110xxx 0bxxxxxxxx 0bxxxxxxxx 0bxxxxxxxx)
            it += 4;
        }
        else if (0xe0 == (0xf0 & byte))
        {
            // 3-byte utf8 code point (0b110xxxxx 0bxxxxxxxx 0bxxxxxxxx)
            it += 3;
        }
        else if (0xc0 == (0xe0 & byte))
        {
            // 2-byte utf8 code point (0b110xxxxx 0bxxxxxxxx)
            it += 2;
        }
        else
        {
            // 1-byte utf8 code point (0b0xxxxxxx)
            it += 1;
        }

        // We want the number of characters (utf-8 code point)
        len += 1;
    }

    return len;
}

// Compute the memory size of a codepoint in the string (in byte)
size_t UTF8string::utf8_codepoint_len_(const size_t j) const noexcept
{
    if (0xf0 == (0xf8 & utf8data[j]))
    {
        return 4;
    }
    else if (0xe0 == (0xf0 & utf8data[j]))
    {
        return 3;
    }
    else if (0xc0 == (0xe0 & utf8data[j]))
    {
        return 2;
    }
    else
        return 1;
}

void UTF8string::utf8_clear() noexcept
{
    utf8data.clear();
    utf8length = 0;
}

bool UTF8string::utf8_empty() const noexcept
{
    return utf8length == 0;
}

/*
    Get the memory position of a codepoint according
    to its position in the utf-8 string
*/
size_t UTF8string::utf8_bpos_at_(const size_t cpos) const noexcept
{
    size_t bpos = 0;
    const size_t u8size = utf8_size();

    for(size_t i = 0; bpos < u8size && i < cpos; i++)
    {
        bpos += utf8_codepoint_len_(bpos);
    }
    return bpos;
}

void UTF8string::utf8_at_(const size_t index, std::string& s) const noexcept
{
    size_t bpos = utf8_bpos_at_(index);
    const size_t n = utf8_codepoint_len_(bpos);

    for(size_t i = 0; i < n; i++)
    {
        s += utf8data[bpos + i];
    }
}

std::string UTF8string::utf8_at(const size_t index) const
{
    if(index >= utf8length)
        throw std::out_of_range("index value greater than the size of the string");

    std::string s;
    utf8_at_(index,s);
    return s;
}

std::string UTF8string::operator [](const size_t index) const noexcept
{
    std::string s;
    utf8_at_(index,s);
    return s;
}

void UTF8string::utf8_pop()
{
    if(utf8length == 0)
        throw std::length_error("Cannot remove the last element from an empty string");

    size_t bpos = utf8_bpos_at_(utf8length - 1);
    utf8data.erase(bpos);
    utf8length -= 1;
}

UTF8string UTF8string::utf8_substr(size_t pos,size_t len) const
{
    if(pos > utf8length)
        return std::string();

    // Length of the substring (number of code points)
    const size_t n = (len == npos || (pos + len) > utf8length) ?
                     (utf8length - pos) : len;

    UTF8iterator it = utf8_iterator_() + pos;
    const UTF8iterator end = (it + n);
    std::string s;

    while(it != end)
    {
        s += *(it++);
    }

    return s;
}

// This function implements the Boyer-Moore string search algorithm
size_t UTF8string::utf8_find(const UTF8string& str, size_t pos) const
{
    if(str.utf8length == 0)
        return npos;

    std::map<std::string,size_t> u8map;
    const size_t n = str.utf8_length();
    size_t index = pos;

    // Preprocessing
    if(str.utf8length > 1)
    {
        for(size_t i = n - 2; ; i--)
        {
            std::string s = str.utf8_at(i);

            if(u8map.find(s) == u8map.end())
                u8map[s] = n - 1 - i;

            if(i == 0)
                break;
        }
    }

    // Look for the subtring
    while(index <= utf8length - n)
    {
        size_t j = n - 1;
        bool found = false;

        while((str.utf8_at(j) == utf8_at(index + j)))
        {
            if(j == 0)
            {
                found = true;
                break;
            }

            j--;
        }

        if(!found)
        {
            std::string ss = utf8_at(index + j);
            index += (u8map.find(ss) == u8map.end()) ? n : u8map[ss];
        }
        else
            return index;
    }

    return npos;
}

// Terminal recursive function that reverse the string
UTF8string UTF8string::utf8_reverse_aux_(UTF8iterator& it,
                                         const UTF8iterator& end,
                                         UTF8string& res)
{
    if(it == end)
        return res;

    res += *(--it);
    return utf8_reverse_aux_(it, end, res);
}

UTF8string& UTF8string::utf8_reverse()
{
    UTF8iterator it = utf8_end();
    UTF8string rev;
    utf8data = (utf8_reverse_aux_(it, utf8_iterator_(), rev)).utf8data;
    return *this;
}

size_t UTF8string::utf8_size() const noexcept
{
    return utf8data.size();
}

size_t UTF8string::utf8_length() const noexcept
{
    return utf8length;
}

const char * UTF8string::utf8_str() const noexcept
{
    return utf8data.c_str();
}

// Internal function that creates an iterator of the current string
UTF8iterator UTF8string::utf8_iterator_() const noexcept
{
    return UTF8iterator(*this);
}

UTF8iterator UTF8string::utf8_begin() const noexcept
{
    return utf8_iterator_();
}

UTF8iterator UTF8string::utf8_end() const noexcept
{
    return utf8_begin() + utf8length;
}

bool operator ==(const UTF8string &str1, const UTF8string &str2) noexcept
{
    return std::string(str1.utf8_str()) == std::string(str2.utf8_str());
}

bool operator !=(const UTF8string &str1, const UTF8string &str2) noexcept
{
    return !(str1 == str2);
}

bool operator <=(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 <= s2;
}

bool operator >=(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 >= s2;
}

bool operator <(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 < s2;
}

bool operator >(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 > s2;
}

UTF8string operator +(const UTF8string &str1, const UTF8string &str2)
{
    return str1 + std::string(str2.utf8_str());
}

UTF8string operator +(const UTF8string &str1, const std::string &str2)
{
    return UTF8string(std::string(str1.utf8_str()) + str2);
}

UTF8string operator +(const std::string &str1, const UTF8string &str2)
{
    return UTF8string(std::string(str1 + str2.utf8_str()));
}

UTF8string operator +(const UTF8string &str1, const char * str2)
{
    return str1 + std::string(str2);
}

UTF8string operator +(const char * str1, const UTF8string &str2)
{
    return std::string(str1) + str2;
}

std::ostream & operator <<(std::ostream &os, const UTF8string &str)
{
    os << str.utf8_str();
    return os;
}

std::istream & operator >>(std::istream &is, UTF8string &str)
{
    std::string tmp;
    std::getline(is,tmp);
    str = tmp;
    return is;
}
<commit_msg>utf8_find(): refactoring<commit_after>/*
*
*   Copyright © 2017 Luxon Jean-Pierre
*   https://gumichan01.github.io/
*
*   This library is under the MIT license
*
*   Luxon Jean-Pierre (Gumichan01)
*   luxon.jean.pierre@gmail.com
*
*/

#include <map>
#include <stdexcept>
#include "utf8_string.hpp"

typedef char byte_t;

UTF8string::UTF8string() noexcept : utf8length(0) {}

UTF8string::UTF8string(const std::string &str)
    : utf8data(str)
{
    if(!utf8_is_valid_())
        throw std::invalid_argument("Invalid UTF-8 string\n");

    utf8length = utf8_length_();
}

UTF8string::UTF8string(const UTF8string &u8str) noexcept
    : utf8data(u8str.utf8data), utf8length(u8str.utf8length) {}

const UTF8string& UTF8string::operator =(const char * str)
{
    utf8data = str;

    if(!utf8_is_valid_())
        throw std::invalid_argument("Invalid UTF-8 string\n");

    utf8length = utf8_length_();
    return *this;
}

const UTF8string& UTF8string::operator =(const std::string &str)
{
    utf8data = str;

    if(!utf8_is_valid_())
        throw std::invalid_argument("Invalid UTF-8 string\n");

    utf8length = utf8_length_();
    return *this;
}

UTF8string& UTF8string::operator =(const UTF8string &u8str) noexcept
{
    utf8data = u8str.utf8data;
    utf8length = u8str.utf8length;
    return *this;
}

const UTF8string& UTF8string::operator +=(const std::string &str)
{
    std::string s = utf8data;
    utf8data += str;

    if(!utf8_is_valid_())
    {
        utf8data = s;
        throw std::invalid_argument("Invalid UTF-8 string\n");
    }

    utf8length = utf8_length_();
    return *this;
}

const UTF8string& UTF8string::operator +=(const UTF8string &u8str)
{
    utf8data += u8str.utf8data;
    utf8length = utf8_length_();
    return *this;
}

const UTF8string& UTF8string::operator +=(const char * str)
{
    std::string s = utf8data;
    utf8data += str;

    if(!utf8_is_valid_())
    {
        utf8data = s;
        throw std::invalid_argument("Invalid UTF-8 string\n");
    }

    utf8length = utf8_length_();
    return *this;
}

// Check the validity of the string (utf-8 encoding)
bool UTF8string::utf8_is_valid_() const noexcept
{
    auto it = utf8data.begin();
    const auto itend = utf8data.end();

    while(it < itend)
    {
        if((0xF8 & *it) == 0xF0)
        {
            // The UTF-8 codepoint begin with 0b11110xxx -> 4-byte codepoint
            // If the iterator reach the end of the string before the
            // end of the 4-byte codepoint -> invalid string
            if((it + 1) == itend || (it + 2) == itend || (it + 3) == itend)
                return false;

            // Each of the following bytes is a value
            // between 0x80 and 0xBF
            if(((0xC0 & *(it + 1)) != 0x80) || ((0xC0 & *(it + 2)) != 0x80)
                    || ((0xC0 & *(it + 3)) != 0x80))
            {
                return false;
            }

            // If the first byte of the sequence is 0xF0
            // then the first continuation byte must be between 0x90 and 0xBF
            // otherwise, if the byte is 0xF4
            // then the first continuation byte must be between 0x80 and 0x8F
            if(*it == '\xF0')
            {
                if(*(it + 1) < '\x90' || *(it + 1) > '\xBF')
                    return false;
            }
            else if(*it == '\xF4')
            {
                if(*(it + 1) < '\x80' || *(it + 1) > '\x8F')
                    return false;
            }

            it += 4;    // Jump to the next codepoint
        }
        else if((0xF0 & *it) == 0xE0)
        {
            // The UTF-8 codepoint begin with 0b1110xxxx -> 3-byte codepoint
            if((it + 1) == itend || (it + 2) == itend)
                return false;

            // Each of the following bytes starts with
            // 0b10xxxxxx in a valid string
            if(((0xC0 & *(it + 1)) != 0x80) || ((0xC0 & *(it + 2)) != 0x80))
                return false;

            // If the first byte of the sequence is 0xE0
            // then the first continuation byte must be between 0xA0 and 0xBF
            // otherwise, if the byte is 0xF4
            // then the first continuation byte must be between 0x80 and 0x9F
            if(*it == '\xE0')
            {
                if(*(it + 1) < '\xA0' || *(it + 1) > '\xBF')
                    return false;
            }
            else if(*it == '\xED')
            {
                if(*(it + 1) < '\x80' || *(it + 1) > '\x9F')
                    return false;
            }

            it += 3;
        }
        else if((0xE0 & *it) == 0xC0)
        {
            // The UTF-8 codepoint begin with 0b110xxxxx -> 2-byte codepoint
            if((it + 1) == itend)
                return false;

            // The following byte starts with 0b10xxxxxx in a valid string
            if((0xC0 & *(it + 1)) != 0x80)
                return false;

            it += 2;
        }
        else if((0x80 & *it) == 0x00)
        {
            // The UTF-8 codepoint begin with 0b0xxxxxxx -> 1-byte codepoint
            it += 1;
        }
        else
        {
            // Invalid codepoint
            return false;
        }
    }

    return true;
}

// Compute the length of the utf-8 string (in number of codepoints)
size_t UTF8string::utf8_length_() const noexcept
{
    auto end_data = utf8data.end();
    auto it = utf8data.begin();
    size_t len = 0;

    while(it != end_data)
    {
        byte_t byte = *it;

        if (0xf0 == (0xf8 & byte))
        {
            // 4-byte utf8 character
            // (0b11110xxx 0bxxxxxxxx 0bxxxxxxxx 0bxxxxxxxx)
            it += 4;
        }
        else if (0xe0 == (0xf0 & byte))
        {
            // 3-byte utf8 code point (0b110xxxxx 0bxxxxxxxx 0bxxxxxxxx)
            it += 3;
        }
        else if (0xc0 == (0xe0 & byte))
        {
            // 2-byte utf8 code point (0b110xxxxx 0bxxxxxxxx)
            it += 2;
        }
        else
        {
            // 1-byte utf8 code point (0b0xxxxxxx)
            it += 1;
        }

        // We want the number of characters (utf-8 code point)
        len += 1;
    }

    return len;
}

// Compute the memory size of a codepoint in the string (in byte)
size_t UTF8string::utf8_codepoint_len_(const size_t j) const noexcept
{
    if (0xf0 == (0xf8 & utf8data[j]))
    {
        return 4;
    }
    else if (0xe0 == (0xf0 & utf8data[j]))
    {
        return 3;
    }
    else if (0xc0 == (0xe0 & utf8data[j]))
    {
        return 2;
    }
    else
        return 1;
}

void UTF8string::utf8_clear() noexcept
{
    utf8data.clear();
    utf8length = 0;
}

bool UTF8string::utf8_empty() const noexcept
{
    return utf8length == 0;
}

/*
    Get the memory position of a codepoint according
    to its position in the utf-8 string
*/
size_t UTF8string::utf8_bpos_at_(const size_t cpos) const noexcept
{
    size_t bpos = 0;
    const size_t u8size = utf8_size();

    for(size_t i = 0; bpos < u8size && i < cpos; i++)
    {
        bpos += utf8_codepoint_len_(bpos);
    }
    return bpos;
}

void UTF8string::utf8_at_(const size_t index, std::string& s) const noexcept
{
    size_t bpos = utf8_bpos_at_(index);
    const size_t n = utf8_codepoint_len_(bpos);

    for(size_t i = 0; i < n; i++)
    {
        s += utf8data[bpos + i];
    }
}

std::string UTF8string::utf8_at(const size_t index) const
{
    if(index >= utf8length)
        throw std::out_of_range("index value greater than the size of the string");

    std::string s;
    utf8_at_(index,s);
    return s;
}

std::string UTF8string::operator [](const size_t index) const noexcept
{
    std::string s;
    utf8_at_(index,s);
    return s;
}

void UTF8string::utf8_pop()
{
    if(utf8length == 0)
        throw std::length_error("Cannot remove the last element from an empty string");

    size_t bpos = utf8_bpos_at_(utf8length - 1);
    utf8data.erase(bpos);
    utf8length -= 1;
}

UTF8string UTF8string::utf8_substr(size_t pos,size_t len) const
{
    if(pos > utf8length)
        return std::string();

    // Length of the substring (number of code points)
    const size_t n = (len == npos || (pos + len) > utf8length) ?
                     (utf8length - pos) : len;

    UTF8iterator it = utf8_iterator_() + pos;
    const UTF8iterator end = (it + n);
    std::string s;

    while(it != end)
    {
        s += *(it++);
    }

    return s;
}

// This function implements the Boyer-Moore string search algorithm
size_t UTF8string::utf8_find(const UTF8string& str, size_t pos) const
{
    if(str.utf8length == 0)
        return npos;

    std::map<std::string, size_t> u8map;
    const size_t U8LEN = str.utf8length;
    size_t index = pos;

    // Preprocessing
    if(str.utf8length > 1)
    {
        for(size_t i = U8LEN - 2; ; i--)
        {
            std::string s = str.utf8_at(i);

            if(u8map.find(s) == u8map.end())
                u8map[s] = U8LEN - 1 - i;

            if(i == 0)
                break;
        }
    }

    // Look for the subtring
    while(index <= utf8length - U8LEN)
    {
        size_t j = U8LEN - 1;
        bool found = false;

        while((str.utf8_at(j) == utf8_at(index + j)))
        {
            if(j == 0)
            {
                found = true;
                break;
            }

            j--;
        }

        if(!found)
        {
            std::string ss = utf8_at(index + j);
            index += (u8map.find(ss) == u8map.end()) ? U8LEN : u8map[ss];
        }
        else
            return index;
    }

    return npos;
}

// Terminal recursive function that reverse the string
UTF8string UTF8string::utf8_reverse_aux_(UTF8iterator& it,
                                         const UTF8iterator& end,
                                         UTF8string& res)
{
    if(it == end)
        return res;

    res += *(--it);
    return utf8_reverse_aux_(it, end, res);
}

UTF8string& UTF8string::utf8_reverse()
{
    UTF8iterator it = utf8_end();
    UTF8string rev;
    utf8data = (utf8_reverse_aux_(it, utf8_iterator_(), rev)).utf8data;
    return *this;
}

size_t UTF8string::utf8_size() const noexcept
{
    return utf8data.size();
}

size_t UTF8string::utf8_length() const noexcept
{
    return utf8length;
}

const char * UTF8string::utf8_str() const noexcept
{
    return utf8data.c_str();
}

// Internal function that creates an iterator of the current string
UTF8iterator UTF8string::utf8_iterator_() const noexcept
{
    return UTF8iterator(*this);
}

UTF8iterator UTF8string::utf8_begin() const noexcept
{
    return utf8_iterator_();
}

UTF8iterator UTF8string::utf8_end() const noexcept
{
    return utf8_begin() + utf8length;
}

bool operator ==(const UTF8string &str1, const UTF8string &str2) noexcept
{
    return std::string(str1.utf8_str()) == std::string(str2.utf8_str());
}

bool operator !=(const UTF8string &str1, const UTF8string &str2) noexcept
{
    return !(str1 == str2);
}

bool operator <=(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 <= s2;
}

bool operator >=(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 >= s2;
}

bool operator <(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 < s2;
}

bool operator >(const UTF8string &str1, const UTF8string &str2) noexcept
{
    const std::string s1 = str1.utf8_str();
    const std::string s2 = str2.utf8_str();

    return s1 > s2;
}

UTF8string operator +(const UTF8string &str1, const UTF8string &str2)
{
    return str1 + std::string(str2.utf8_str());
}

UTF8string operator +(const UTF8string &str1, const std::string &str2)
{
    return UTF8string(std::string(str1.utf8_str()) + str2);
}

UTF8string operator +(const std::string &str1, const UTF8string &str2)
{
    return UTF8string(std::string(str1 + str2.utf8_str()));
}

UTF8string operator +(const UTF8string &str1, const char * str2)
{
    return str1 + std::string(str2);
}

UTF8string operator +(const char * str1, const UTF8string &str2)
{
    return std::string(str1) + str2;
}

std::ostream & operator <<(std::ostream &os, const UTF8string &str)
{
    os << str.utf8_str();
    return os;
}

std::istream & operator >>(std::istream &is, UTF8string &str)
{
    std::string tmp;
    std::getline(is,tmp);
    str = tmp;
    return is;
}
<|endoftext|>


<commit_before>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/common_runtime/optimization_registry.h"
#include "tensorflow/core/graph/node_builder.h"

namespace tensorflow {
namespace {

Tensor make_zeros(const DataType& dtype, const TensorShapeProto& shape) {
  Tensor tensor(dtype, TensorShape(shape));

  // Conveniently, all numeric data types have 0x0 == zero.  Otherwise we would
  // need a giant switch statement here.
  memset(const_cast<char*>(tensor.tensor_data().data()), 0,
         tensor.tensor_data().size());

  return tensor;
}

// Replaces occurrences of the "AccumulateNV2" stub operator with a graph of
// lower-level ops. The graph is equivalent (modulo certain corner cases)
// to the semantics of the original accumulate_n() Python op in math_ops.py.
// Implementing the op with a rewrite allows this new variant of accumulate_n
// to be differentiable.
//
// The binary code that generates AccumulateNV2 stub ops is located in a
// dynamic library built out of tensorflow/contrib/framework. Ideally, this
// class would also be in contrib, but calls to REGISTER_OPTIMIZATION() from
// third-party libraries aren't currently supported.
class AccumulateNV2RemovePass : public GraphOptimizationPass {
 public:
  Status Run(const GraphOptimizationPassOptions& options) override {
    // TODO(freiss.oss@gmail.com): Substantial shared code with
    // ParallelConcatRemovePass::Run(). Consider refactoring if someone makes
    // a third similar rewrite.
    if (options.graph == nullptr) {
      // TODO(apassos) returning OK feels weird here as we can't do anything
      // without a graph, but some tests require this.
      return Status::OK();
    }

    Graph* g = options.graph->get();
    if (g == nullptr) {
      return errors::Internal(
          "AccumulateNV2 removal should happen before partitioning and a "
          "graph should be available.");
    }

    // Build up a todo list of ops to replace, *then* modify the graph
    gtl::InlinedVector<Node*, 2> matches;
    for (Node* n : g->op_nodes()) {
      if (n->type_string() == "AccumulateNV2") {
        matches.push_back(n);
      }
    }
    for (Node* n : matches) {
      TF_RETURN_IF_ERROR(rewriteNode(n, g));
    }
    return Status::OK();
  }

  Status rewriteNode(Node* n, Graph* g) {
    AttrSlice n_attrs = n->attrs();
    auto base_make_node = [n, &n_attrs](const string& op, const string& name) {
      NodeDebugInfo debug_info(*n);
      NodeBuilder node_builder(name, op, OpRegistry::Global(), &debug_info);

      // The pieces of AccumulateNV2 should all be on the same node.
      node_builder.Device(n->requested_device());
      string colo;
      if (GetNodeAttr(n_attrs, kColocationAttrName, &colo).ok()) {
        node_builder.Attr(kColocationAttrName, colo);
      }
      return node_builder;
    };
    auto make_node = [n, g, &base_make_node](string op) {
      return base_make_node(
          op, g->NewName(strings::StrCat(n->name(), "/Internal")));
    };

    DataType dtype;
    TF_RETURN_IF_ERROR(GetNodeAttr(n_attrs, "T", &dtype));
    TensorShapeProto shape;
    TF_RETURN_IF_ERROR(GetNodeAttr(n_attrs, "shape", &shape));

    std::vector<const Edge*> data_edges, control_edges;
    for (const Edge* input_edge : n->in_edges()) {
      if (input_edge->IsControlEdge()) {
        control_edges.push_back(input_edge);
      } else {
        data_edges.push_back(input_edge);
      }
    }

    // Create the following ops to replace the AccumulateNV2 placeholder:
    Node* create_accumulator = nullptr;            // TemporaryVariable op
    Node* initial_val = nullptr;                   // Const op
    Node* initialize_accumulator = nullptr;        // Assign op
    std::vector<Node*> add_values_to_accumulator;  // AssignAdd ops
    Node* clean_up_accumulator = nullptr;          // DestroyTemporaryVariable

    const string accumulator_name =
        strings::StrCat(n->name(), "/Internal/Accumulator");
    TensorShapeProto variable_shape;
    variable_shape.add_dim()->set_size(0);
    TF_RETURN_IF_ERROR(make_node("TemporaryVariable")
                           .Attr("shape", variable_shape)
                           .Attr("dtype", dtype)
                           .Attr("var_name", accumulator_name)
                           .Finalize(g, &create_accumulator));
    if (PartialTensorShape(shape).IsFullyDefined()) {
      // For fully defined shapes make a constant zero tensor.
      TF_RETURN_IF_ERROR(make_node("Const")
                             .Attr("value", make_zeros(dtype, shape))
                             .Attr("dtype", dtype)
                             .Finalize(g, &initial_val));
    } else {
      // For partial shapes make a Fill operation to make a zero tensor with the
      // shape of the first input.
      Node* shape_node;
      TF_RETURN_IF_ERROR(
          make_node("Shape")
              .Input(data_edges[0]->src(), data_edges[0]->src_output())
              .Finalize(g, &shape_node));
      Node* zero;
      TF_RETURN_IF_ERROR(
          make_node("Const")
              .Attr("value", make_zeros(dtype, TensorShapeProto()))
              .Attr("dtype", dtype)
              .Finalize(g, &zero));
      TF_RETURN_IF_ERROR(make_node("Fill")
                             .Input(shape_node)
                             .Input(zero)
                             .Finalize(g, &initial_val));
    }
    TF_RETURN_IF_ERROR(make_node("Assign")
                           .Attr("T", dtype)
                           .Input(create_accumulator)  // ref: Ref(T)
                           .Input(initial_val)         // value: T
                           .Attr("validate_shape", false)
                           .Finalize(g, &initialize_accumulator));
    for (int i = 0; i < data_edges.size(); ++i) {
      Node* assignAdd;
      TF_RETURN_IF_ERROR(make_node("AssignAdd")
                             .Attr("T", dtype)
                             .Attr("use_locking", true)
                             .Input(initialize_accumulator)  // ref: Ref(T)
                             .Input(data_edges[i]->src(),
                                    data_edges[i]->src_output())  // value: T
                             .Finalize(g, &assignAdd));

      add_values_to_accumulator.push_back(assignAdd);
    }

    // Note that we use the original placeholder op's name here
    TF_RETURN_IF_ERROR(base_make_node("DestroyTemporaryVariable", n->name())
                           .Attr("T", dtype)
                           .Attr("var_name", accumulator_name)
                           .Input(initialize_accumulator)
                           .Finalize(g, &clean_up_accumulator));

    // Add edges to the graph to ensure that operations occur in the right
    // order:
    // 1. Do anything that had a control edge to the AccumulateNV2 placeholder
    // 2. Initialize accumulator
    // 3. Add input values to accumulator (already handled by data edges
    //    added above)
    // 4. Reclaim the buffer that held the accumulator
    // 5. Do anything that depended on the AccumulateNV2 placeholder
    for (const Edge* control_edge : control_edges) {
      g->AddControlEdge(control_edge->src(), initialize_accumulator);
    }

    for (Node* assign_add : add_values_to_accumulator) {
      g->AddControlEdge(assign_add, clean_up_accumulator);
    }

    for (const Edge* out_edge : n->out_edges()) {
      if (out_edge->IsControlEdge()) {
        g->AddControlEdge(clean_up_accumulator, out_edge->dst());
      } else {
        g->AddEdge(clean_up_accumulator, 0, out_edge->dst(),
                   out_edge->dst_input());
      }
    }

    // Remove the original AccumulateNV2 placeholder op.
    // This removal modifies the op and must happen after we have finished
    // using its incoming/outgoing edge sets.
    g->RemoveNode(n);

    return Status::OK();
  }
};
REGISTER_OPTIMIZATION(OptimizationPassRegistry::PRE_PLACEMENT, 0,
                      AccumulateNV2RemovePass);

}  // namespace
}  // namespace tensorflow
<commit_msg>Avoid creating large constants in accumulate_n_optimizer.<commit_after>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/common_runtime/optimization_registry.h"
#include "tensorflow/core/graph/node_builder.h"

namespace tensorflow {
namespace {

Tensor make_zeros(const DataType& dtype, const TensorShapeProto& shape) {
  Tensor tensor(dtype, TensorShape(shape));

  // Conveniently, all numeric data types have 0x0 == zero.  Otherwise we would
  // need a giant switch statement here.
  memset(const_cast<char*>(tensor.tensor_data().data()), 0,
         tensor.tensor_data().size());

  return tensor;
}

// Replaces occurrences of the "AccumulateNV2" stub operator with a graph of
// lower-level ops. The graph is equivalent (modulo certain corner cases)
// to the semantics of the original accumulate_n() Python op in math_ops.py.
// Implementing the op with a rewrite allows this new variant of accumulate_n
// to be differentiable.
//
// The binary code that generates AccumulateNV2 stub ops is located in a
// dynamic library built out of tensorflow/contrib/framework. Ideally, this
// class would also be in contrib, but calls to REGISTER_OPTIMIZATION() from
// third-party libraries aren't currently supported.
class AccumulateNV2RemovePass : public GraphOptimizationPass {
 public:
  Status Run(const GraphOptimizationPassOptions& options) override {
    // TODO(freiss.oss@gmail.com): Substantial shared code with
    // ParallelConcatRemovePass::Run(). Consider refactoring if someone makes
    // a third similar rewrite.
    if (options.graph == nullptr) {
      // TODO(apassos) returning OK feels weird here as we can't do anything
      // without a graph, but some tests require this.
      return Status::OK();
    }

    Graph* g = options.graph->get();
    if (g == nullptr) {
      return errors::Internal(
          "AccumulateNV2 removal should happen before partitioning and a "
          "graph should be available.");
    }

    // Build up a todo list of ops to replace, *then* modify the graph
    gtl::InlinedVector<Node*, 2> matches;
    for (Node* n : g->op_nodes()) {
      if (n->type_string() == "AccumulateNV2") {
        matches.push_back(n);
      }
    }
    for (Node* n : matches) {
      TF_RETURN_IF_ERROR(rewriteNode(n, g));
    }
    return Status::OK();
  }

  Status rewriteNode(Node* n, Graph* g) {
    AttrSlice n_attrs = n->attrs();
    auto base_make_node = [n, &n_attrs](const string& op, const string& name) {
      NodeDebugInfo debug_info(*n);
      NodeBuilder node_builder(name, op, OpRegistry::Global(), &debug_info);

      // The pieces of AccumulateNV2 should all be on the same node.
      node_builder.Device(n->requested_device());
      string colo;
      if (GetNodeAttr(n_attrs, kColocationAttrName, &colo).ok()) {
        node_builder.Attr(kColocationAttrName, colo);
      }
      return node_builder;
    };
    auto make_node = [n, g, &base_make_node](string op) {
      return base_make_node(
          op, g->NewName(strings::StrCat(n->name(), "/Internal")));
    };

    DataType dtype;
    TF_RETURN_IF_ERROR(GetNodeAttr(n_attrs, "T", &dtype));
    TensorShapeProto shape;
    TF_RETURN_IF_ERROR(GetNodeAttr(n_attrs, "shape", &shape));

    std::vector<const Edge*> data_edges, control_edges;
    for (const Edge* input_edge : n->in_edges()) {
      if (input_edge->IsControlEdge()) {
        control_edges.push_back(input_edge);
      } else {
        data_edges.push_back(input_edge);
      }
    }

    // Create the following ops to replace the AccumulateNV2 placeholder:
    Node* create_accumulator = nullptr;            // TemporaryVariable op
    Node* initial_val = nullptr;                   // Const op
    Node* initialize_accumulator = nullptr;        // Assign op
    std::vector<Node*> add_values_to_accumulator;  // AssignAdd ops
    Node* clean_up_accumulator = nullptr;          // DestroyTemporaryVariable

    const string accumulator_name =
        strings::StrCat(n->name(), "/Internal/Accumulator");
    TensorShapeProto variable_shape;
    variable_shape.add_dim()->set_size(0);
    TF_RETURN_IF_ERROR(make_node("TemporaryVariable")
                           .Attr("shape", variable_shape)
                           .Attr("dtype", dtype)
                           .Attr("var_name", accumulator_name)
                           .Finalize(g, &create_accumulator));
    PartialTensorShape partial_shape(shape);
    // Make a Fill operation to make a zero tensor with the shape of the first
    // input.
    Node* shape_node;
    TF_RETURN_IF_ERROR(
        make_node("Shape")
            .Input(data_edges[0]->src(), data_edges[0]->src_output())
            .Finalize(g, &shape_node));
    Node* zero;
    TF_RETURN_IF_ERROR(make_node("Const")
                           .Attr("value", make_zeros(dtype, TensorShapeProto()))
                           .Attr("dtype", dtype)
                           .Finalize(g, &zero));
    TF_RETURN_IF_ERROR(make_node("Fill")
                           .Input(shape_node)
                           .Input(zero)
                           .Finalize(g, &initial_val));
    TF_RETURN_IF_ERROR(make_node("Assign")
                           .Attr("T", dtype)
                           .Input(create_accumulator)  // ref: Ref(T)
                           .Input(initial_val)         // value: T
                           .Attr("validate_shape", false)
                           .Finalize(g, &initialize_accumulator));
    for (int i = 0; i < data_edges.size(); ++i) {
      Node* assignAdd;
      TF_RETURN_IF_ERROR(make_node("AssignAdd")
                             .Attr("T", dtype)
                             .Attr("use_locking", true)
                             .Input(initialize_accumulator)  // ref: Ref(T)
                             .Input(data_edges[i]->src(),
                                    data_edges[i]->src_output())  // value: T
                             .Finalize(g, &assignAdd));

      add_values_to_accumulator.push_back(assignAdd);
    }

    // Note that we use the original placeholder op's name here
    TF_RETURN_IF_ERROR(base_make_node("DestroyTemporaryVariable", n->name())
                           .Attr("T", dtype)
                           .Attr("var_name", accumulator_name)
                           .Input(initialize_accumulator)
                           .Finalize(g, &clean_up_accumulator));

    // Add edges to the graph to ensure that operations occur in the right
    // order:
    // 1. Do anything that had a control edge to the AccumulateNV2 placeholder
    // 2. Initialize accumulator
    // 3. Add input values to accumulator (already handled by data edges
    //    added above)
    // 4. Reclaim the buffer that held the accumulator
    // 5. Do anything that depended on the AccumulateNV2 placeholder
    for (const Edge* control_edge : control_edges) {
      g->AddControlEdge(control_edge->src(), initialize_accumulator);
    }

    for (Node* assign_add : add_values_to_accumulator) {
      g->AddControlEdge(assign_add, clean_up_accumulator);
    }

    for (const Edge* out_edge : n->out_edges()) {
      if (out_edge->IsControlEdge()) {
        g->AddControlEdge(clean_up_accumulator, out_edge->dst());
      } else {
        g->AddEdge(clean_up_accumulator, 0, out_edge->dst(),
                   out_edge->dst_input());
      }
    }

    // Remove the original AccumulateNV2 placeholder op.
    // This removal modifies the op and must happen after we have finished
    // using its incoming/outgoing edge sets.
    g->RemoveNode(n);

    return Status::OK();
  }
};
REGISTER_OPTIMIZATION(OptimizationPassRegistry::PRE_PLACEMENT, 0,
                      AccumulateNV2RemovePass);

}  // namespace
}  // namespace tensorflow
<|endoftext|>


<commit_before>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// See docs in ../ops/array_ops.cc.

#ifdef INTEL_MKL
#define EIGEN_USE_THREADS
#include <math.h>

#include "mkldnn.hpp"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/type_traits.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/kernels/meta_support.h"
#include "tensorflow/core/kernels/no_op.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/util/mkl_types.h"
#include "tensorflow/core/util/mkl_util.h"
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"

namespace tensorflow {

typedef Eigen::ThreadPoolDevice CPUDevice;

template <typename Device, typename Toutput>
class MklRequantizePerChannelOp : public OpKernel {
 public:
  explicit MklRequantizePerChannelOp(OpKernelConstruction* ctx)
      : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("out_type", &out_type_));
    OP_REQUIRES(ctx, out_type_ == DT_QINT8 || out_type_ == DT_QUINT8,
                errors::InvalidArgument(
                    "out_type must be qint8 or quint8, but got: ", out_type_));
  }
  virtual ~MklRequantizePerChannelOp() {}
  void Compute(OpKernelContext* ctx) override {
    try {
      const Tensor& input = ctx->input(kInputTensorIndex);
      const Tensor& input_min_vec = ctx->input(kInputMinVecIndex);
      float* input_min_vec_data = (float*)const_cast<void*>(
          static_cast<const void*>(input_min_vec.flat<float>().data()));
      const Tensor& input_max_vec = ctx->input(kInputMaxVecIndex);
      float* input_max_vec_data = (float*)const_cast<void*>(
          static_cast<const void*>(input_max_vec.flat<float>().data()));

      const Tensor& input_requested_min = ctx->input(this->kRequestMinIndex);
      const float input_requested_min_float =
          input_requested_min.flat<float>()(0);
      const Tensor& input_requested_max = ctx->input(this->kRequestMaxIndex);
      const float input_requested_max_float =
          input_requested_max.flat<float>()(0);

      size_t depth = input_min_vec.NumElements();
      OP_REQUIRES(
          ctx, input.dims() == 4,
          errors::InvalidArgument("Current RequantizePerChannel operator"
                                  "supports 4D tensors only."));
      OP_REQUIRES(
          ctx, input_min_vec.dim_size(0) == depth,
          errors::InvalidArgument("input_min has incorrect size, expected ",
                                  depth, " was ", input_min_vec.dim_size(0)));
      OP_REQUIRES(
          ctx, input_max_vec.dim_size(0) == depth,
          errors::InvalidArgument("input_max has incorrect size, expected ",
                                  depth, " was ", input_max_vec.dim_size(0)));

      if (out_type_ == DT_QINT8) DCHECK(input_requested_min_float < 0.0f);

      const float factor = (out_type_ == DT_QINT8) ? 127.0f : 255.0f;
      const float requested_min_max =
          std::max(std::abs(input_requested_min_float),
                   std::abs(input_requested_max_float));
      Tensor* output = nullptr;
      OP_REQUIRES_OK(ctx, ctx->allocate_output(kOutputTensorIndex,
                                               input.shape(), &output));

      std::vector<float> scales(depth);
      for (int i = 0; i < depth; ++i) {
        float min_max_from_vec = std::max(std::abs(input_min_vec_data[i]),
                                          std::abs(input_max_vec_data[i]));
        scales[i] = factor * (min_max_from_vec / requested_min_max /
                              static_cast<float>(1L << 31));
      }

      mkldnn::primitive_attr reorder_attr;
      reorder_attr.set_output_scales(2, scales);

      memory::dims dims_mkl_order =
          TFShapeToMklDnnDimsInNCHW(input.shape(), FORMAT_NHWC);
      memory::desc input_md = memory::desc(dims_mkl_order, MklDnnType<qint32>(),
                                           MEMORY_FORMAT::nhwc);
      memory::desc output_md =
          (out_type_ == DT_QINT8)
              ? memory::desc(dims_mkl_order, MklDnnType<qint8>(),
                             MEMORY_FORMAT::nhwc)
              : memory::desc(dims_mkl_order, MklDnnType<quint8>(),
                             MEMORY_FORMAT::nhwc);

      void* input_buf =
          static_cast<void*>(const_cast<qint32*>(input.flat<qint32>().data()));
      void* output_buf;
      if (out_type_ == DT_QINT8) {
        output_buf = static_cast<void*>(
            const_cast<qint8*>(output->flat<qint8>().data()));
      } else {
        output_buf = static_cast<void*>(
            const_cast<quint8*>(output->flat<quint8>().data()));
      }

      std::unique_ptr<memory> input_mem_prim(
          new MEMORY_CONSTRUCTOR_USING_MD(input_md, cpu_engine_, input_buf));
      std::unique_ptr<memory> output_mem_prim(
          new MEMORY_CONSTRUCTOR_USING_MD(output_md, cpu_engine_, output_buf));

      mkldnn::reorder::primitive_desc reorder_pd =
          REORDER_PD_CONSTRUCTOR_WITH_ATTR(
              GET_MEMORY_PRIMITIVE_DESC_FROM_MEM_PTR(input_mem_prim),
              GET_MEMORY_PRIMITIVE_DESC_FROM_MEM_PTR(output_mem_prim),
              cpu_engine_, reorder_attr);
      mkldnn::stream reorder_stream = CPU_STREAM(cpu_engine_);
#ifndef ENABLE_MKLDNN_V1
      reorder_stream.submit(
          {mkldnn::reorder(reorder_pd, *input_mem_prim, *output_mem_prim)});
#else
      std::unordered_map<int, mkldnn::memory> reorder_args = {
          {MKLDNN_ARG_FROM, *input_mem_prim},
          {MKLDNN_ARG_TO, *output_mem_prim}};
      std::unique_ptr<mkldnn::primitive> reorder_prim(
          new mkldnn::reorder(reorder_pd));
      reorder_prim->execute(reorder_stream, reorder_args);
#endif  // ENABLE_MKLDNN_V1

      Tensor* output_min = nullptr;
      Tensor* output_max = nullptr;
      OP_REQUIRES_OK(ctx,
                     ctx->allocate_output(kOutputMinIndex, {}, &output_min));
      OP_REQUIRES_OK(ctx,
                     ctx->allocate_output(kOutputMaxIndex, {}, &output_max));

      output_min->flat<float>()(0) = input_requested_min_float;
      output_max->flat<float>()(0) = input_requested_max_float;
    } catch (mkldnn::error& e) {
      string error_msg = "Status: " + std::to_string(e.status) + ", message: " +
                         std::string(e.message) + ", in file " +
                         std::string(__FILE__) + ":" + std::to_string(__LINE__);
      OP_REQUIRES_OK(
          ctx, errors::Aborted("Operation received an exception:", error_msg));
    }
  }

 private:
  const int kInputTensorIndex = 0;
  const int kInputMinVecIndex = 1;
  const int kInputMaxVecIndex = 2;
  const int kRequestMinIndex = 3;
  const int kRequestMaxIndex = 4;
  const int kOutputTensorIndex = 0;
  const int kOutputMinIndex = 1;
  const int kOutputMaxIndex = 2;
  DataType out_type_;
  engine cpu_engine_ = engine(ENGINE_CPU, 0);
};

// Registration for out_type: qint8
REGISTER_KERNEL_BUILDER(Name("RequantizePerChannel")
                            .Device(DEVICE_CPU)
                            .TypeConstraint<qint32>("T")
                            .TypeConstraint<qint8>("out_type"),
                        MklRequantizePerChannelOp<CPUDevice, qint8>);
// Registration for out_type: quint8
REGISTER_KERNEL_BUILDER(Name("RequantizePerChannel")
                            .Device(DEVICE_CPU)
                            .TypeConstraint<qint32>("T")
                            .TypeConstraint<quint8>("out_type"),
                        MklRequantizePerChannelOp<CPUDevice, quint8>);

}  // namespace tensorflow
#endif  // INTEL_MKL
<commit_msg>Update tensorflow/core/kernels/mkl_requantize_per_channel_op.cc<commit_after>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// See docs in ../ops/array_ops.cc.

#ifdef INTEL_MKL
#define EIGEN_USE_THREADS
#include <math.h>

#include "mkldnn.hpp"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/type_traits.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/kernels/meta_support.h"
#include "tensorflow/core/kernels/no_op.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/util/mkl_types.h"
#include "tensorflow/core/util/mkl_util.h"
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"

namespace tensorflow {

typedef Eigen::ThreadPoolDevice CPUDevice;

template <typename Device, typename Toutput>
class MklRequantizePerChannelOp : public OpKernel {
 public:
  explicit MklRequantizePerChannelOp(OpKernelConstruction* ctx)
      : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("out_type", &out_type_));
    OP_REQUIRES(ctx, out_type_ == DT_QINT8 || out_type_ == DT_QUINT8,
                errors::InvalidArgument(
                    "out_type must be qint8 or quint8, but got: ", out_type_));
  }
  virtual ~MklRequantizePerChannelOp() {}
  void Compute(OpKernelContext* ctx) override {
    try {
      const Tensor& input = ctx->input(kInputTensorIndex);
      const Tensor& input_min_vec = ctx->input(kInputMinVecIndex);
      float* input_min_vec_data = (float*)const_cast<void*>(
          static_cast<const void*>(input_min_vec.flat<float>().data()));
      const Tensor& input_max_vec = ctx->input(kInputMaxVecIndex);
      float* input_max_vec_data = (float*)const_cast<void*>(
          static_cast<const void*>(input_max_vec.flat<float>().data()));

      const Tensor& input_requested_min = ctx->input(this->kRequestMinIndex);
      const float input_requested_min_float =
          input_requested_min.flat<float>()(0);
      const Tensor& input_requested_max = ctx->input(this->kRequestMaxIndex);
      const float input_requested_max_float =
          input_requested_max.flat<float>()(0);

      size_t depth = input_min_vec.NumElements();
      OP_REQUIRES(
          ctx, input.dims() == 4,
          errors::InvalidArgument("Current RequantizePerChannel operator"
                                  "supports 4D tensors only."));
      OP_REQUIRES(
          ctx, input_min_vec.dim_size(0) == depth,
          errors::InvalidArgument("input_min has incorrect size, expected ",
                                  depth, " was ", input_min_vec.dim_size(0)));
      OP_REQUIRES(
          ctx, input_max_vec.dim_size(0) == depth,
          errors::InvalidArgument("input_max has incorrect size, expected ",
                                  depth, " was ", input_max_vec.dim_size(0)));

      if (out_type_ == DT_QINT8) DCHECK(input_requested_min_float < 0.0f);

      const float factor = (out_type_ == DT_QINT8) ? 127.0f : 255.0f;
      const float requested_min_max =
          std::max(std::abs(input_requested_min_float),
                   std::abs(input_requested_max_float));
      Tensor* output = nullptr;
      OP_REQUIRES_OK(ctx, ctx->allocate_output(kOutputTensorIndex,
                                               input.shape(), &output));

      std::vector<float> scales(depth);
      for (int i = 0; i < depth; ++i) {
        float min_max_from_vec = std::max(std::abs(input_min_vec_data[i]),
                                          std::abs(input_max_vec_data[i]));
        scales[i] = factor * (min_max_from_vec / requested_min_max /
                              static_cast<float>(1L << 31));
      }

      mkldnn::primitive_attr reorder_attr;
      reorder_attr.set_output_scales(2, scales);

      memory::dims dims_mkl_order =
          TFShapeToMklDnnDimsInNCHW(input.shape(), FORMAT_NHWC);
      memory::desc input_md = memory::desc(dims_mkl_order, MklDnnType<qint32>(),
                                           MEMORY_FORMAT::nhwc);
      memory::desc output_md =
          (out_type_ == DT_QINT8)
              ? memory::desc(dims_mkl_order, MklDnnType<qint8>(),
                             MEMORY_FORMAT::nhwc)
              : memory::desc(dims_mkl_order, MklDnnType<quint8>(),
                             MEMORY_FORMAT::nhwc);

      void* input_buf =
          static_cast<void*>(const_cast<qint32*>(input.flat<qint32>().data()));
      void* output_buf;
      if (out_type_ == DT_QINT8) {
        output_buf = static_cast<void*>(
            const_cast<qint8*>(output->flat<qint8>().data()));
      } else {
        output_buf = static_cast<void*>(
            const_cast<quint8*>(output->flat<quint8>().data()));
      }

      std::unique_ptr<memory> input_mem_prim(
          new MEMORY_CONSTRUCTOR_USING_MD(input_md, cpu_engine_, input_buf));
      std::unique_ptr<memory> output_mem_prim(
          new MEMORY_CONSTRUCTOR_USING_MD(output_md, cpu_engine_, output_buf));

      mkldnn::reorder::primitive_desc reorder_pd =
          REORDER_PD_CONSTRUCTOR_WITH_ATTR(
              GET_MEMORY_PRIMITIVE_DESC_FROM_MEM_PTR(input_mem_prim),
              GET_MEMORY_PRIMITIVE_DESC_FROM_MEM_PTR(output_mem_prim),
              cpu_engine_, reorder_attr);
      mkldnn::stream reorder_stream = CPU_STREAM(cpu_engine_);
#ifndef ENABLE_MKLDNN_V1
      reorder_stream.submit(
          {mkldnn::reorder(reorder_pd, *input_mem_prim, *output_mem_prim)});
#else
      std::unordered_map<int, mkldnn::memory> reorder_args = {
          {MKLDNN_ARG_FROM, *input_mem_prim},
          {MKLDNN_ARG_TO, *output_mem_prim}};
      std::unique_ptr<mkldnn::primitive> reorder_prim(
          new mkldnn::reorder(reorder_pd));
      reorder_prim->execute(reorder_stream, reorder_args);
#endif  // !ENABLE_MKLDNN_V1

      Tensor* output_min = nullptr;
      Tensor* output_max = nullptr;
      OP_REQUIRES_OK(ctx,
                     ctx->allocate_output(kOutputMinIndex, {}, &output_min));
      OP_REQUIRES_OK(ctx,
                     ctx->allocate_output(kOutputMaxIndex, {}, &output_max));

      output_min->flat<float>()(0) = input_requested_min_float;
      output_max->flat<float>()(0) = input_requested_max_float;
    } catch (mkldnn::error& e) {
      string error_msg = "Status: " + std::to_string(e.status) + ", message: " +
                         std::string(e.message) + ", in file " +
                         std::string(__FILE__) + ":" + std::to_string(__LINE__);
      OP_REQUIRES_OK(
          ctx, errors::Aborted("Operation received an exception:", error_msg));
    }
  }

 private:
  const int kInputTensorIndex = 0;
  const int kInputMinVecIndex = 1;
  const int kInputMaxVecIndex = 2;
  const int kRequestMinIndex = 3;
  const int kRequestMaxIndex = 4;
  const int kOutputTensorIndex = 0;
  const int kOutputMinIndex = 1;
  const int kOutputMaxIndex = 2;
  DataType out_type_;
  engine cpu_engine_ = engine(ENGINE_CPU, 0);
};

// Registration for out_type: qint8
REGISTER_KERNEL_BUILDER(Name("RequantizePerChannel")
                            .Device(DEVICE_CPU)
                            .TypeConstraint<qint32>("T")
                            .TypeConstraint<qint8>("out_type"),
                        MklRequantizePerChannelOp<CPUDevice, qint8>);
// Registration for out_type: quint8
REGISTER_KERNEL_BUILDER(Name("RequantizePerChannel")
                            .Device(DEVICE_CPU)
                            .TypeConstraint<qint32>("T")
                            .TypeConstraint<quint8>("out_type"),
                        MklRequantizePerChannelOp<CPUDevice, quint8>);

}  // namespace tensorflow
#endif  // INTEL_MKL
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome_frame/urlmon_bind_status_callback.h"

#include <shlguid.h>

#include "base/logging.h"
#include "base/string_util.h"

CFUrlmonBindStatusCallback::CFUrlmonBindStatusCallback()
    : only_buffer_(false), data_(new RequestData()) {
  DLOG(INFO) << __FUNCTION__ << me();
}

CFUrlmonBindStatusCallback::~CFUrlmonBindStatusCallback() {
  DLOG(INFO) << __FUNCTION__ << me();
}

std::string CFUrlmonBindStatusCallback::me() {
  return StringPrintf(" obj=0x%08X",
      static_cast<CFUrlmonBindStatusCallback*>(this));
}

HRESULT CFUrlmonBindStatusCallback::DelegateQI(void* obj, REFIID iid,
                                               void** ret, DWORD cookie) {
  CFUrlmonBindStatusCallback* me =
      reinterpret_cast<CFUrlmonBindStatusCallback*>(obj);
  HRESULT hr = me->delegate_.QueryInterface(iid, ret);
  return hr;
}

HRESULT CFUrlmonBindStatusCallback::Initialize(IBindCtx* bind_ctx,
                                               RequestHeaders* headers) {
  DLOG(INFO) << __FUNCTION__ << me();
  DCHECK(bind_ctx);
  DCHECK(!binding_delegate_.get());
  // headers may be NULL.

  data_->Initialize(headers);

  bind_ctx_ = bind_ctx;

  // Replace the bind context callback with ours.
  HRESULT hr = ::RegisterBindStatusCallback(bind_ctx, this,
                                            delegate_.Receive(), 0);
  if (!delegate_) {
    NOTREACHED();
    hr = E_UNEXPECTED;
  }

  return hr;
}

HRESULT CFUrlmonBindStatusCallback::QueryService(REFGUID service, REFIID iid,
                                                 void** object) {
  HRESULT hr = E_NOINTERFACE;
  if (delegate_) {
    ScopedComPtr<IServiceProvider> svc;
    svc.QueryFrom(delegate_);
    if (svc) {
      hr = svc->QueryService(service, iid, object);
    }
  }
  return hr;
}

// IBindStatusCallback
HRESULT CFUrlmonBindStatusCallback::OnStartBinding(DWORD reserved,
                                                   IBinding* binding) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  DCHECK(!binding_delegate_.get());

  CComObject<SimpleBindingImpl>* binding_delegate;
  HRESULT hr = CComObject<SimpleBindingImpl>::CreateInstance(&binding_delegate);
  if (FAILED(hr)) {
    NOTREACHED();
    return hr;
  }

  binding_delegate_ = binding_delegate;
  DCHECK_EQ(binding_delegate->m_dwRef, 1);
  binding_delegate_->SetDelegate(binding);

  return delegate_->OnStartBinding(reserved, binding_delegate_);
}

HRESULT CFUrlmonBindStatusCallback::GetPriority(LONG* priority) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->GetPriority(priority);
}

HRESULT CFUrlmonBindStatusCallback::OnLowResource(DWORD reserved) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->OnLowResource(reserved);
}

HRESULT CFUrlmonBindStatusCallback::OnProgress(ULONG progress,
                                               ULONG progress_max,
                                               ULONG status_code,
                                               LPCWSTR status_text) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" status=%i tid=%i %ls",
      status_code, PlatformThread::CurrentId(), status_text);
  if (status_code == BINDSTATUS_REDIRECTING && status_text) {
    redirected_url_ = status_text;
  }
  return delegate_->OnProgress(progress, progress_max, status_code,
                               status_text);
}

HRESULT CFUrlmonBindStatusCallback::OnStopBinding(HRESULT hresult,
                                                  LPCWSTR error) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" hr=0x%08X '%ls' tid=%i",
      hresult, error, PlatformThread::CurrentId());
  if (SUCCEEDED(hresult)) {
    // Notify the BHO that this is the one and only RequestData object.
    NavigationManager* mgr = NavigationManager::GetThreadInstance();
    DCHECK(mgr);
    if (mgr && data_->GetCachedContentSize()) {
      mgr->SetActiveRequestData(data_);
      if (!redirected_url_.empty()) {
        mgr->set_url(redirected_url_.c_str());
      }
    }

    if (only_buffer_) {
      hresult = INET_E_TERMINATED_BIND;
      DLOG(INFO) << " - changed to INET_E_TERMINATED_BIND";
    }
  }

  // Hold a reference to ourselves while we release the bind context
  // and disconnect the callback.
  AddRef();

  HRESULT hr = delegate_->OnStopBinding(hresult, error);

  if (bind_ctx_) {
    ::RevokeBindStatusCallback(bind_ctx_, this);
    bind_ctx_.Release();
  }

  binding_delegate_.Release();

  // After this call, this object might be gone.
  Release();

  return hr;
}

HRESULT CFUrlmonBindStatusCallback::GetBindInfo(DWORD* bindf,
                                                BINDINFO* bind_info) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->GetBindInfo(bindf, bind_info);
}

HRESULT CFUrlmonBindStatusCallback::OnDataAvailable(DWORD bscf, DWORD size,
                                                    FORMATETC* format_etc,
                                                    STGMEDIUM* stgmed) {
  DCHECK(format_etc);
#ifndef NDEBUG
  wchar_t clip_fmt_name[MAX_PATH] = {0};
  if (format_etc) {
    ::GetClipboardFormatNameW(format_etc->cfFormat, clip_fmt_name,
                              arraysize(clip_fmt_name));
  }
  DLOG(INFO) << __FUNCTION__ << me()
      << StringPrintf(" tid=%i original fmt=%ls",
                      PlatformThread::CurrentId(), clip_fmt_name);

  if (!stgmed) {
    NOTREACHED() << "Invalid STGMEDIUM received";
    return delegate_->OnDataAvailable(bscf, size, format_etc, stgmed);
  }

  if (stgmed->tymed != TYMED_ISTREAM) {
    DLOG(INFO) << "Not handling medium:" << stgmed->tymed;
    return delegate_->OnDataAvailable(bscf, size, format_etc, stgmed);
  }

  if (bscf & BSCF_FIRSTDATANOTIFICATION) {
    DLOG(INFO) << "first data notification";
  }
#endif

  HRESULT hr = S_OK;
  size_t bytes_read = 0;
  if (!only_buffer_) {
    hr = data_->DelegateDataRead(delegate_, bscf, size, format_etc, stgmed,
                                 &bytes_read);
  }

  DLOG(INFO) << __FUNCTION__ << StringPrintf(" - 0x%08x", hr);
  if (hr == INET_E_TERMINATED_BIND) {
    // Check if the content type is CF's mime type.
    UINT cf_format = ::RegisterClipboardFormatW(kChromeMimeType);
    bool override_bind_results = (format_etc->cfFormat == cf_format);
    if (!override_bind_results) {
      ScopedComPtr<IBrowserService> browser_service;
      DoQueryService(SID_SShellBrowser, delegate_, browser_service.Receive());
      override_bind_results = (browser_service != NULL) &&
                              CheckForCFNavigation(browser_service, false);
    }

    if (override_bind_results) {
      // We want to complete fetching the entire document even though the
      // delegate isn't interested in continuing.
      // This happens when we switch from mshtml to CF.
      // We take over and buffer the document and once we're done, we report
      // INET_E_TERMINATED to mshtml so that it will continue as usual.
      hr = S_OK;
      only_buffer_ = true;
      binding_delegate_->OverrideBindResults(INET_E_TERMINATED_BIND);
    }
  }

  if (only_buffer_) {
    data_->CacheAll(stgmed->pstm);
    DCHECK(hr == S_OK);
  }

  return hr;
}

HRESULT CFUrlmonBindStatusCallback::OnObjectAvailable(REFIID iid,
                                                      IUnknown* unk) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->OnObjectAvailable(iid, unk);
}

// IBindStatusCallbackEx
HRESULT CFUrlmonBindStatusCallback::GetBindInfoEx(DWORD* bindf,
                                                  BINDINFO* bind_info,
                                                  DWORD* bindf2,
                                                  DWORD* reserved) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  ScopedComPtr<IBindStatusCallbackEx> bscbex;
  bscbex.QueryFrom(delegate_);
  return bscbex->GetBindInfoEx(bindf, bind_info, bindf2, reserved);
}

HRESULT CFUrlmonBindStatusCallback::BeginningTransaction(LPCWSTR url,
    LPCWSTR headers,
    DWORD reserved,
    LPWSTR* additional_headers) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());

  ScopedComPtr<IHttpNegotiate> http_negotiate;
  HRESULT hr = http_negotiate.QueryFrom(delegate_);
  if (SUCCEEDED(hr)) {
    hr = http_negotiate->BeginningTransaction(url, headers, reserved,
                                              additional_headers);
  } else {
    hr = S_OK;
  }

  data_->headers()->OnBeginningTransaction(url, headers,
      additional_headers && *additional_headers ? *additional_headers : NULL);

  DLOG_IF(ERROR, FAILED(hr)) << __FUNCTION__;
  return hr;
}

HRESULT CFUrlmonBindStatusCallback::OnResponse(DWORD response_code,
                                               LPCWSTR response_headers,
                                               LPCWSTR request_headers,
                                               LPWSTR* additional_headers) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());

  data_->headers()->OnResponse(response_code, response_headers,
                               request_headers);

  ScopedComPtr<IHttpNegotiate> http_negotiate;
  HRESULT hr = http_negotiate.QueryFrom(delegate_);
  if (SUCCEEDED(hr)) {
    hr = http_negotiate->OnResponse(response_code, response_headers,
                                    request_headers, additional_headers);
  } else {
    hr = S_OK;
  }
  return hr;
}

HRESULT CFUrlmonBindStatusCallback::GetRootSecurityId(BYTE* security_id,
                                                      DWORD* security_id_size,
                                                      DWORD_PTR reserved) {
  ScopedComPtr<IHttpNegotiate2> http_negotiate;
  http_negotiate.QueryFrom(delegate_);
  return http_negotiate->GetRootSecurityId(security_id, security_id_size,
                                           reserved);
}

HRESULT CFUrlmonBindStatusCallback::GetSerializedClientCertContext(
    BYTE** cert,
    DWORD* cert_size) {
  ScopedComPtr<IHttpNegotiate3> http_negotiate;
  http_negotiate.QueryFrom(delegate_);
  return http_negotiate->GetSerializedClientCertContext(cert, cert_size);
}
<commit_msg>Fix a ChromeFrame crash in the bind status callback which occurs due to a NULL delegate pointer being dereferenced. It appears that there are cases where the IMoniker::BindToObject function is called with a bind context without a registered callback. In this case we should not do anything.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome_frame/urlmon_bind_status_callback.h"

#include <shlguid.h>

#include "base/logging.h"
#include "base/string_util.h"

CFUrlmonBindStatusCallback::CFUrlmonBindStatusCallback()
    : only_buffer_(false), data_(new RequestData()) {
  DLOG(INFO) << __FUNCTION__ << me();
}

CFUrlmonBindStatusCallback::~CFUrlmonBindStatusCallback() {
  DLOG(INFO) << __FUNCTION__ << me();
}

std::string CFUrlmonBindStatusCallback::me() {
  return StringPrintf(" obj=0x%08X",
      static_cast<CFUrlmonBindStatusCallback*>(this));
}

HRESULT CFUrlmonBindStatusCallback::DelegateQI(void* obj, REFIID iid,
                                               void** ret, DWORD cookie) {
  CFUrlmonBindStatusCallback* me =
      reinterpret_cast<CFUrlmonBindStatusCallback*>(obj);
  HRESULT hr = me->delegate_.QueryInterface(iid, ret);
  return hr;
}

HRESULT CFUrlmonBindStatusCallback::Initialize(IBindCtx* bind_ctx,
                                               RequestHeaders* headers) {
  DLOG(INFO) << __FUNCTION__ << me();
  DCHECK(bind_ctx);
  DCHECK(!binding_delegate_.get());
  // headers may be NULL.

  data_->Initialize(headers);

  bind_ctx_ = bind_ctx;

  // Replace the bind context callback with ours.
  HRESULT hr = ::RegisterBindStatusCallback(bind_ctx, this,
                                            delegate_.Receive(), 0);
  if (!delegate_) {
    NOTREACHED() << "Failed to find registered bind status callback";
    ::RevokeBindStatusCallback(bind_ctx_, this);
    bind_ctx_.Release();
    hr = E_UNEXPECTED;
  }

  return hr;
}

HRESULT CFUrlmonBindStatusCallback::QueryService(REFGUID service, REFIID iid,
                                                 void** object) {
  HRESULT hr = E_NOINTERFACE;
  if (delegate_) {
    ScopedComPtr<IServiceProvider> svc;
    svc.QueryFrom(delegate_);
    if (svc) {
      hr = svc->QueryService(service, iid, object);
    }
  }
  return hr;
}

// IBindStatusCallback
HRESULT CFUrlmonBindStatusCallback::OnStartBinding(DWORD reserved,
                                                   IBinding* binding) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  DCHECK(!binding_delegate_.get());

  CComObject<SimpleBindingImpl>* binding_delegate;
  HRESULT hr = CComObject<SimpleBindingImpl>::CreateInstance(&binding_delegate);
  if (FAILED(hr)) {
    NOTREACHED();
    return hr;
  }

  binding_delegate_ = binding_delegate;
  DCHECK_EQ(binding_delegate->m_dwRef, 1);
  binding_delegate_->SetDelegate(binding);

  return delegate_->OnStartBinding(reserved, binding_delegate_);
}

HRESULT CFUrlmonBindStatusCallback::GetPriority(LONG* priority) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->GetPriority(priority);
}

HRESULT CFUrlmonBindStatusCallback::OnLowResource(DWORD reserved) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->OnLowResource(reserved);
}

HRESULT CFUrlmonBindStatusCallback::OnProgress(ULONG progress,
                                               ULONG progress_max,
                                               ULONG status_code,
                                               LPCWSTR status_text) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" status=%i tid=%i %ls",
      status_code, PlatformThread::CurrentId(), status_text);
  if (status_code == BINDSTATUS_REDIRECTING && status_text) {
    redirected_url_ = status_text;
  }
  return delegate_->OnProgress(progress, progress_max, status_code,
                               status_text);
}

HRESULT CFUrlmonBindStatusCallback::OnStopBinding(HRESULT hresult,
                                                  LPCWSTR error) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" hr=0x%08X '%ls' tid=%i",
      hresult, error, PlatformThread::CurrentId());
  if (SUCCEEDED(hresult)) {
    // Notify the BHO that this is the one and only RequestData object.
    NavigationManager* mgr = NavigationManager::GetThreadInstance();
    DCHECK(mgr);
    if (mgr && data_->GetCachedContentSize()) {
      mgr->SetActiveRequestData(data_);
      if (!redirected_url_.empty()) {
        mgr->set_url(redirected_url_.c_str());
      }
    }

    if (only_buffer_) {
      hresult = INET_E_TERMINATED_BIND;
      DLOG(INFO) << " - changed to INET_E_TERMINATED_BIND";
    }
  }

  // Hold a reference to ourselves while we release the bind context
  // and disconnect the callback.
  AddRef();

  HRESULT hr = delegate_->OnStopBinding(hresult, error);

  if (bind_ctx_) {
    ::RevokeBindStatusCallback(bind_ctx_, this);
    bind_ctx_.Release();
  }

  binding_delegate_.Release();

  // After this call, this object might be gone.
  Release();

  return hr;
}

HRESULT CFUrlmonBindStatusCallback::GetBindInfo(DWORD* bindf,
                                                BINDINFO* bind_info) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->GetBindInfo(bindf, bind_info);
}

HRESULT CFUrlmonBindStatusCallback::OnDataAvailable(DWORD bscf, DWORD size,
                                                    FORMATETC* format_etc,
                                                    STGMEDIUM* stgmed) {
  DCHECK(format_etc);
#ifndef NDEBUG
  wchar_t clip_fmt_name[MAX_PATH] = {0};
  if (format_etc) {
    ::GetClipboardFormatNameW(format_etc->cfFormat, clip_fmt_name,
                              arraysize(clip_fmt_name));
  }
  DLOG(INFO) << __FUNCTION__ << me()
      << StringPrintf(" tid=%i original fmt=%ls",
                      PlatformThread::CurrentId(), clip_fmt_name);

  if (!stgmed) {
    NOTREACHED() << "Invalid STGMEDIUM received";
    return delegate_->OnDataAvailable(bscf, size, format_etc, stgmed);
  }

  if (stgmed->tymed != TYMED_ISTREAM) {
    DLOG(INFO) << "Not handling medium:" << stgmed->tymed;
    return delegate_->OnDataAvailable(bscf, size, format_etc, stgmed);
  }

  if (bscf & BSCF_FIRSTDATANOTIFICATION) {
    DLOG(INFO) << "first data notification";
  }
#endif

  HRESULT hr = S_OK;
  size_t bytes_read = 0;
  if (!only_buffer_) {
    hr = data_->DelegateDataRead(delegate_, bscf, size, format_etc, stgmed,
                                 &bytes_read);
  }

  DLOG(INFO) << __FUNCTION__ << StringPrintf(" - 0x%08x", hr);
  if (hr == INET_E_TERMINATED_BIND) {
    // Check if the content type is CF's mime type.
    UINT cf_format = ::RegisterClipboardFormatW(kChromeMimeType);
    bool override_bind_results = (format_etc->cfFormat == cf_format);
    if (!override_bind_results) {
      ScopedComPtr<IBrowserService> browser_service;
      DoQueryService(SID_SShellBrowser, delegate_, browser_service.Receive());
      override_bind_results = (browser_service != NULL) &&
                              CheckForCFNavigation(browser_service, false);
    }

    if (override_bind_results) {
      // We want to complete fetching the entire document even though the
      // delegate isn't interested in continuing.
      // This happens when we switch from mshtml to CF.
      // We take over and buffer the document and once we're done, we report
      // INET_E_TERMINATED to mshtml so that it will continue as usual.
      hr = S_OK;
      only_buffer_ = true;
      binding_delegate_->OverrideBindResults(INET_E_TERMINATED_BIND);
    }
  }

  if (only_buffer_) {
    data_->CacheAll(stgmed->pstm);
    DCHECK(hr == S_OK);
  }

  return hr;
}

HRESULT CFUrlmonBindStatusCallback::OnObjectAvailable(REFIID iid,
                                                      IUnknown* unk) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  return delegate_->OnObjectAvailable(iid, unk);
}

// IBindStatusCallbackEx
HRESULT CFUrlmonBindStatusCallback::GetBindInfoEx(DWORD* bindf,
                                                  BINDINFO* bind_info,
                                                  DWORD* bindf2,
                                                  DWORD* reserved) {
  DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());
  ScopedComPtr<IBindStatusCallbackEx> bscbex;
  bscbex.QueryFrom(delegate_);
  return bscbex->GetBindInfoEx(bindf, bind_info, bindf2, reserved);
}

HRESULT CFUrlmonBindStatusCallback::BeginningTransaction(LPCWSTR url,
    LPCWSTR headers,
    DWORD reserved,
    LPWSTR* additional_headers) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());

  ScopedComPtr<IHttpNegotiate> http_negotiate;
  HRESULT hr = http_negotiate.QueryFrom(delegate_);
  if (SUCCEEDED(hr)) {
    hr = http_negotiate->BeginningTransaction(url, headers, reserved,
                                              additional_headers);
  } else {
    hr = S_OK;
  }

  data_->headers()->OnBeginningTransaction(url, headers,
      additional_headers && *additional_headers ? *additional_headers : NULL);

  DLOG_IF(ERROR, FAILED(hr)) << __FUNCTION__;
  return hr;
}

HRESULT CFUrlmonBindStatusCallback::OnResponse(DWORD response_code,
                                               LPCWSTR response_headers,
                                               LPCWSTR request_headers,
                                               LPWSTR* additional_headers) {
  DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i",
      PlatformThread::CurrentId());

  data_->headers()->OnResponse(response_code, response_headers,
                               request_headers);

  ScopedComPtr<IHttpNegotiate> http_negotiate;
  HRESULT hr = http_negotiate.QueryFrom(delegate_);
  if (SUCCEEDED(hr)) {
    hr = http_negotiate->OnResponse(response_code, response_headers,
                                    request_headers, additional_headers);
  } else {
    hr = S_OK;
  }
  return hr;
}

HRESULT CFUrlmonBindStatusCallback::GetRootSecurityId(BYTE* security_id,
                                                      DWORD* security_id_size,
                                                      DWORD_PTR reserved) {
  ScopedComPtr<IHttpNegotiate2> http_negotiate;
  http_negotiate.QueryFrom(delegate_);
  return http_negotiate->GetRootSecurityId(security_id, security_id_size,
                                           reserved);
}

HRESULT CFUrlmonBindStatusCallback::GetSerializedClientCertContext(
    BYTE** cert,
    DWORD* cert_size) {
  ScopedComPtr<IHttpNegotiate3> http_negotiate;
  http_negotiate.QueryFrom(delegate_);
  return http_negotiate->GetSerializedClientCertContext(cert, cert_size);
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008, Massachusetts Institute of Technology              //
// All rights reserved.                                                   //
//                                                                        //
// Redistribution and use in source and binary forms, with or without     //
// modification, are permitted provided that the following conditions are //
// met:                                                                   //
//                                                                        //
//     * Redistributions of source code must retain the above copyright   //
//       notice, this list of conditions and the following disclaimer.    //
//                                                                        //
//     * Redistributions in binary form must reproduce the above          //
//       copyright notice, this list of conditions and the following      //
//       disclaimer in the documentation and/or other materials provided  //
//       with the distribution.                                           //
//                                                                        //
//     * Neither the name of the Massachusetts Institute of Technology    //
//       nor the names of its contributors may be used to endorse or      //
//       promote products derived from this software without specific     //
//       prior written permission.                                        //
//                                                                        //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    //
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      //
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  //
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   //
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  //
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT       //
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,  //
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY  //
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT    //
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  //
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   //
////////////////////////////////////////////////////////////////////////////

#include <cstdarg>
#include <cstdio>
#include "Logger.h"

namespace mitlm {

////////////////////////////////////////////////////////////////////////////////

#ifdef NDEBUG
    int  Logger::_verbosity = 0;
    bool Logger::_timestamp = false;
#else
    int  Logger::_verbosity = 1;
    bool Logger::_timestamp = true;
#endif
clock_t Logger::_startTime = clock();

void Logger::Log(int level, const char *fmt, ...) {
    if (_verbosity >= level) {
        va_list args;
        va_start(args, fmt);
        if (_timestamp)
            printf("%.3f\t", (double)(clock() - _startTime) / CLOCKS_PER_SEC);
        vprintf(fmt, args);
        va_end(args);
    }
}

void Logger::Warn(int level, const char *fmt, ...) {
    if (_verbosity >= level) {
        va_list args;
        va_start(args, fmt);
        fprintf(stderr, "\e[0;33m");
        if (_timestamp)
            printf("%.3f\t", (double)(clock() - _startTime) / CLOCKS_PER_SEC);
        vfprintf(stderr, fmt, args);
        fprintf(stderr, "\e[m");
        va_end(args);
    }
}

void Logger::Error(int level, const char *fmt, ...) {
    if (_verbosity >= level) {
        va_list args;
        va_start(args, fmt);
        fprintf(stderr, "\e[1;31m");
        if (_timestamp)
            printf("%.3f\t", (double)(clock() - _startTime) / CLOCKS_PER_SEC);
        vfprintf(stderr, fmt, args);
        fprintf(stderr, "\e[m");
        va_end(args);
    }
}

}
<commit_msg>Replaced non-standard '\e' character with '\033'.<commit_after>////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008, Massachusetts Institute of Technology              //
// All rights reserved.                                                   //
//                                                                        //
// Redistribution and use in source and binary forms, with or without     //
// modification, are permitted provided that the following conditions are //
// met:                                                                   //
//                                                                        //
//     * Redistributions of source code must retain the above copyright   //
//       notice, this list of conditions and the following disclaimer.    //
//                                                                        //
//     * Redistributions in binary form must reproduce the above          //
//       copyright notice, this list of conditions and the following      //
//       disclaimer in the documentation and/or other materials provided  //
//       with the distribution.                                           //
//                                                                        //
//     * Neither the name of the Massachusetts Institute of Technology    //
//       nor the names of its contributors may be used to endorse or      //
//       promote products derived from this software without specific     //
//       prior written permission.                                        //
//                                                                        //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    //
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      //
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  //
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   //
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  //
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT       //
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,  //
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY  //
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT    //
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  //
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   //
////////////////////////////////////////////////////////////////////////////

#include <cstdarg>
#include <cstdio>
#include "Logger.h"

namespace mitlm {

////////////////////////////////////////////////////////////////////////////////

#ifdef NDEBUG
    int  Logger::_verbosity = 0;
    bool Logger::_timestamp = false;
#else
    int  Logger::_verbosity = 1;
    bool Logger::_timestamp = true;
#endif
clock_t Logger::_startTime = clock();

void Logger::Log(int level, const char *fmt, ...) {
    if (_verbosity >= level) {
        va_list args;
        va_start(args, fmt);
        if (_timestamp)
            printf("%.3f\t", (double)(clock() - _startTime) / CLOCKS_PER_SEC);
        vprintf(fmt, args);
        va_end(args);
    }
}

void Logger::Warn(int level, const char *fmt, ...) {
    if (_verbosity >= level) {
        va_list args;
        va_start(args, fmt);
        fprintf(stderr, "\033[0;33m");
        if (_timestamp)
            printf("%.3f\t", (double)(clock() - _startTime) / CLOCKS_PER_SEC);
        vfprintf(stderr, fmt, args);
        fprintf(stderr, "\033[m");
        va_end(args);
    }
}

void Logger::Error(int level, const char *fmt, ...) {
    if (_verbosity >= level) {
        va_list args;
        va_start(args, fmt);
        fprintf(stderr, "\033[1;31m");
        if (_timestamp)
            printf("%.3f\t", (double)(clock() - _startTime) / CLOCKS_PER_SEC);
        vfprintf(stderr, fmt, args);
        fprintf(stderr, "\033[m");
        va_end(args);
    }
}

}
<|endoftext|>


<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <string>

#include "tensorflow/lite/tools/benchmark/benchmark_model.h"
#include "tensorflow/lite/tools/benchmark/delegate_provider.h"
#include "tensorflow/lite/tools/benchmark/logging.h"
#include "tensorflow/lite/tools/evaluation/utils.h"
#if defined(__ANDROID__)
#include "tensorflow/lite/delegates/gpu/delegate.h"
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
// Only enable metal delegate when using a real iPhone device.
#define REAL_IPHONE_DEVICE
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#endif
#endif

namespace tflite {
namespace benchmark {

class GpuDelegateProvider : public DelegateProvider {
 public:
  std::vector<Flag> CreateFlags(BenchmarkParams* params) const final;

  void AddParams(BenchmarkParams* params) const final;

  void LogParams(const BenchmarkParams& params) const final;

  TfLiteDelegatePtr CreateTfLiteDelegate(
      const BenchmarkParams& params) const final;

  std::string GetName() const final { return "GPU"; }
};
REGISTER_DELEGATE_PROVIDER(GpuDelegateProvider);

std::vector<Flag> GpuDelegateProvider::CreateFlags(
    BenchmarkParams* params) const {
  std::vector<Flag> flags = {
    CreateFlag<bool>("use_gpu", params, "use gpu"),
#if defined(__ANDROID__) || defined(REAL_IPHONE_DEVICE)
    CreateFlag<bool>("gpu_precision_loss_allowed", params,
                     "Allow to process computation in lower precision than "
                     "FP32 in GPU. By default, it's enabled."),
#endif
#if defined(REAL_IPHONE_DEVICE)
    CreateFlag<std::string>(
        "gpu_wait_type", params,
        "GPU wait type. Should be one of the following: passive, active, "
        "do_not_wait, aggressive"),
#endif
  };
  return flags;
}

void GpuDelegateProvider::AddParams(BenchmarkParams* params) const {
  params->AddParam("use_gpu", BenchmarkParam::Create<bool>(false));
#if defined(__ANDROID__) || defined(REAL_IPHONE_DEVICE)
  params->AddParam("gpu_precision_loss_allowed",
                   BenchmarkParam::Create<bool>(true));
#endif
#if defined(REAL_IPHONE_DEVICE)
  params->AddParam("gpu_wait_type", BenchmarkParam::Create<std::string>(""));
#endif
}

void GpuDelegateProvider::LogParams(const BenchmarkParams& params) const {
  TFLITE_LOG(INFO) << "Use gpu : [" << params.Get<bool>("use_gpu") << "]";
#if defined(__ANDROID__) || defined(REAL_IPHONE_DEVICE)
  TFLITE_LOG(INFO) << "Allow lower precision in gpu : ["
                   << params.Get<bool>("gpu_precision_loss_allowed") << "]";
#endif
#if defined(REAL_IPHONE_DEVICE)
  TFLITE_LOG(INFO) << "GPU delegate wait type : ["
                   << params.Get<std::string>("gpu_wait_type") << "]";
#endif
}

TfLiteDelegatePtr GpuDelegateProvider::CreateTfLiteDelegate(
    const BenchmarkParams& params) const {
  TfLiteDelegatePtr delegate(nullptr, [](TfLiteDelegate*) {});

  if (params.Get<bool>("use_gpu")) {
#if defined(__ANDROID__)
    TfLiteGpuDelegateOptionsV2 gpu_opts = TfLiteGpuDelegateOptionsV2Default();
    gpu_opts.inference_preference =
        TFLITE_GPU_INFERENCE_PREFERENCE_SUSTAINED_SPEED;
    if (params.Get<bool>("gpu_precision_loss_allowed")) {
      gpu_opts.inference_priority1 = TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY;
      gpu_opts.inference_priority2 =
          TFLITE_GPU_INFERENCE_PRIORITY_MIN_MEMORY_USAGE;
      gpu_opts.inference_priority3 =
          TFLITE_GPU_INFERENCE_PRIORITY_MAX_PRECISION;
    }
    delegate = evaluation::CreateGPUDelegate(&gpu_opts);
#elif defined(REAL_IPHONE_DEVICE)
    TFLGpuDelegateOptions gpu_opts = {0};
    gpu_opts.allow_precision_loss =
        params.Get<bool>("gpu_precision_loss_allowed");

    std::string string_gpu_wait_type = params.Get<std::string>("gpu_wait_type");
    if (!string_gpu_wait_type.empty()) {
      TFLGpuDelegateWaitType wait_type = TFLGpuDelegateWaitTypePassive;
      if (string_gpu_wait_type == "passive") {
        wait_type = TFLGpuDelegateWaitTypePassive;
      } else if (string_gpu_wait_type == "active") {
        wait_type = TFLGpuDelegateWaitTypeActive;
      } else if (string_gpu_wait_type == "do_not_wait") {
        wait_type = TFLGpuDelegateWaitTypeDoNotWait;
      } else if (string_gpu_wait_type == "aggressive") {
        wait_type = TFLGpuDelegateWaitTypeAggressive;
      }
      gpu_opts.wait_type = wait_type;
    }
    delegate = TfLiteDelegatePtr(TFLGpuDelegateCreate(&gpu_opts),
                                 &TFLGpuDelegateDelete);
#else
    TFLITE_LOG(WARN) << "The GPU delegate compile options are only supported "
                        "to be benchmarked on Android or iOS platforms.";
    delegate = evaluation::CreateGPUDelegate();
#endif

    if (!delegate.get()) {
      TFLITE_LOG(WARN) << "GPU acceleration is unsupported on this platform.";
    }
  }
  return delegate;
}

}  // namespace benchmark
}  // namespace tflite
<commit_msg>Restore default inference preference for GPU benchmarking<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <string>

#include "tensorflow/lite/tools/benchmark/benchmark_model.h"
#include "tensorflow/lite/tools/benchmark/delegate_provider.h"
#include "tensorflow/lite/tools/benchmark/logging.h"
#include "tensorflow/lite/tools/evaluation/utils.h"
#if defined(__ANDROID__)
#include "tensorflow/lite/delegates/gpu/delegate.h"
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
// Only enable metal delegate when using a real iPhone device.
#define REAL_IPHONE_DEVICE
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#endif
#endif

namespace tflite {
namespace benchmark {

class GpuDelegateProvider : public DelegateProvider {
 public:
  std::vector<Flag> CreateFlags(BenchmarkParams* params) const final;

  void AddParams(BenchmarkParams* params) const final;

  void LogParams(const BenchmarkParams& params) const final;

  TfLiteDelegatePtr CreateTfLiteDelegate(
      const BenchmarkParams& params) const final;

  std::string GetName() const final { return "GPU"; }
};
REGISTER_DELEGATE_PROVIDER(GpuDelegateProvider);

std::vector<Flag> GpuDelegateProvider::CreateFlags(
    BenchmarkParams* params) const {
  std::vector<Flag> flags = {
    CreateFlag<bool>("use_gpu", params, "use gpu"),
#if defined(__ANDROID__) || defined(REAL_IPHONE_DEVICE)
    CreateFlag<bool>("gpu_precision_loss_allowed", params,
                     "Allow to process computation in lower precision than "
                     "FP32 in GPU. By default, it's enabled."),
#endif
#if defined(REAL_IPHONE_DEVICE)
    CreateFlag<std::string>(
        "gpu_wait_type", params,
        "GPU wait type. Should be one of the following: passive, active, "
        "do_not_wait, aggressive"),
#endif
  };
  return flags;
}

void GpuDelegateProvider::AddParams(BenchmarkParams* params) const {
  params->AddParam("use_gpu", BenchmarkParam::Create<bool>(false));
#if defined(__ANDROID__) || defined(REAL_IPHONE_DEVICE)
  params->AddParam("gpu_precision_loss_allowed",
                   BenchmarkParam::Create<bool>(true));
#endif
#if defined(REAL_IPHONE_DEVICE)
  params->AddParam("gpu_wait_type", BenchmarkParam::Create<std::string>(""));
#endif
}

void GpuDelegateProvider::LogParams(const BenchmarkParams& params) const {
  TFLITE_LOG(INFO) << "Use gpu : [" << params.Get<bool>("use_gpu") << "]";
#if defined(__ANDROID__) || defined(REAL_IPHONE_DEVICE)
  TFLITE_LOG(INFO) << "Allow lower precision in gpu : ["
                   << params.Get<bool>("gpu_precision_loss_allowed") << "]";
#endif
#if defined(REAL_IPHONE_DEVICE)
  TFLITE_LOG(INFO) << "GPU delegate wait type : ["
                   << params.Get<std::string>("gpu_wait_type") << "]";
#endif
}

TfLiteDelegatePtr GpuDelegateProvider::CreateTfLiteDelegate(
    const BenchmarkParams& params) const {
  TfLiteDelegatePtr delegate(nullptr, [](TfLiteDelegate*) {});

  if (params.Get<bool>("use_gpu")) {
#if defined(__ANDROID__)
    TfLiteGpuDelegateOptionsV2 gpu_opts = TfLiteGpuDelegateOptionsV2Default();
    if (params.Get<bool>("gpu_precision_loss_allowed")) {
      gpu_opts.inference_priority1 = TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY;
      gpu_opts.inference_priority2 =
          TFLITE_GPU_INFERENCE_PRIORITY_MIN_MEMORY_USAGE;
      gpu_opts.inference_priority3 =
          TFLITE_GPU_INFERENCE_PRIORITY_MAX_PRECISION;
    }
    delegate = evaluation::CreateGPUDelegate(&gpu_opts);
#elif defined(REAL_IPHONE_DEVICE)
    TFLGpuDelegateOptions gpu_opts = {0};
    gpu_opts.allow_precision_loss =
        params.Get<bool>("gpu_precision_loss_allowed");

    std::string string_gpu_wait_type = params.Get<std::string>("gpu_wait_type");
    if (!string_gpu_wait_type.empty()) {
      TFLGpuDelegateWaitType wait_type = TFLGpuDelegateWaitTypePassive;
      if (string_gpu_wait_type == "passive") {
        wait_type = TFLGpuDelegateWaitTypePassive;
      } else if (string_gpu_wait_type == "active") {
        wait_type = TFLGpuDelegateWaitTypeActive;
      } else if (string_gpu_wait_type == "do_not_wait") {
        wait_type = TFLGpuDelegateWaitTypeDoNotWait;
      } else if (string_gpu_wait_type == "aggressive") {
        wait_type = TFLGpuDelegateWaitTypeAggressive;
      }
      gpu_opts.wait_type = wait_type;
    }
    delegate = TfLiteDelegatePtr(TFLGpuDelegateCreate(&gpu_opts),
                                 &TFLGpuDelegateDelete);
#else
    TFLITE_LOG(WARN) << "The GPU delegate compile options are only supported "
                        "to be benchmarked on Android or iOS platforms.";
    delegate = evaluation::CreateGPUDelegate();
#endif

    if (!delegate.get()) {
      TFLITE_LOG(WARN) << "GPU acceleration is unsupported on this platform.";
    }
  }
  return delegate;
}

}  // namespace benchmark
}  // namespace tflite
<|endoftext|>