#include "ustring.h" #include "ustring_asm.h" #include #include /** * Конструктор по умолчанию */ ustring::ustring(): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { _pData = new char32_t(0); _len = 0; _capacity = 0; } /** * Копирующий конструктор * @param str Исходная строка */ ustring::ustring(const ustring& str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { std::cout << "ustring copy constructor" << std::endl; _len = str.length(); _capacity = _len; _pData = new char32_t[_len + 1]; std::memcpy(_pData, str._pData, _len*sizeof(char32_t)); _pData[_len] = char32_t(0); } /** * Конструктор перемещения * @param str Исходная строка * @detailed Данные перемещаются (не копируются!) из исходной строки */ ustring::ustring(ustring&& str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { std::cout << "ustring move constructor" << std::endl; _pData = std::move(str._pData); _len = str._len; _capacity = _len; str._pData = nullptr; str._len = 0; } /** * Конструктор из UTF-8 строки * @param str Исходная строка в UTF-8 (null terminated) * @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX) * @detailed Так как символы ASCII являются подмножеством UTF-8, при создании строки * из массива ASCII символов нужно применять именно этот конструктор */ ustring::ustring(const char* str, bool needUnescape /* = false */): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { init_from_utf8_str(str, needUnescape); } /** * Конструктор из UTF-16 строки * @param str Исходная строка в UTF-16 (null terminated) * @param byteOrder Порядок байт */ ustring::ustring(const char16_t* str, byte_order byteOrder /* = BYTE_ORDER_LITTLE_ENDIAN */): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { init_from_utf16_str(str, byteOrder); } /** * Конструктор из UTF-32 строки * @param str Исходная строка в UTF-32 (null terminated) * @param byteOrder Порядок байт */ ustring::ustring(const char32_t* str, byte_order byteOrder /* = BYTE_ORDER_LITTLE_ENDIAN */): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { init_from_utf32_str(str, byteOrder); } /** * Конструктор из std::string * @param str Исходная строка в UTF-8 * @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX) */ ustring::ustring(const std::string& str, bool needUnescape /* = false */): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { init_from_utf8_str(str.c_str(), needUnescape); } /** * Конструктор из std::wstring * @param str Исходная строка в UTF-16/UTF-32 (в зависимости от платформы) */ ustring::ustring(const std::wstring& str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { #ifdef __GNUC__ init_from_utf32_str((const char32_t*)str.c_str()); #else init_from_utf16_str((const char16_t*)str.c_str()); #endif } ustring::ustring( std::size_t n ): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) { _pData = new char32_t[n + 1]; _len = 0; _capacity = n; } void ustring::init_from_utf8_str(const char* str, bool needUnescape) { const char* strData = utf8_skip_bom(str); _len = utf8_str_len(strData, needUnescape); _capacity = _len; _pData = new char32_t[_len + 1]; utf8_to_ucs4(strData, _pData, _len, needUnescape); _pData[_len] = char32_t(0); } inline void ustring::init_from_utf16_str( const char16_t* str, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ ) { const char16_t* strData = utf16_skip_bom(str); _len = utf16_str_len(strData); _capacity = _len; _pData = new char32_t[_len + 1]; utf16_to_ucs4(strData, _pData, _len, byteOrder); _pData[_len] = char32_t(0); } inline void ustring::init_from_utf32_str( const char32_t* str, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ ) { const char32_t* strData = utf32_skip_bom(str); _len = utf32_str_len(strData); _capacity = _len; _pData = new char32_t[_len + 1]; utf32_strcpy_with_convert_byteorder(str, _pData, _len, byteOrder); _pData[_len] = char32_t(0); } ustring::operator std::string() const { return utf8_str(); } ustring::operator std::wstring() const { #ifdef __GNUC__ return (wchar_t*)utf32_str(); #else return (wchar_t*)utf16_str(); #endif } /** * Возвращает указатель на временный буфер с UTF-8 строкой * @param addBom Указывает, добавлять ли BOM в возвращаемую строку * @param needEscape Определяет, нужно ли экранировать не-ASCII символы * @return Указатель на буфер со строкой, закодированной в UTF-8 * @detailed Этот буфер является временным и будет высвобожден при любом изменении строки */ const char* ustring::utf8_str(bool addBom /*= false*/ , bool needEscape /*= false*/) const { std::size_t bomSize = (addBom ? sizeof(g_utf8_bom) : 0); std::size_t len = ucs4_get_utf8_str_bytes(_pData, needEscape); _pUtf8 = new char[bomSize + len + 1]; ucs4_to_utf8(_pData, _pUtf8 + bomSize, length(), needEscape); _pUtf8[len + bomSize] = char(0); if(addBom) { memcpy(_pUtf8, g_utf8_bom, bomSize); } return _pUtf8; } /** * @param addBom Указывает, добавлять ли BOM в возвращаемую строку * @param byteOrder Задает порядок байт в возвращаемой строке * @return Указатель на буфер со строкой, закодированной в UTF-16 */ const char16_t* ustring::utf16_str( bool addBom /*= false*/, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ ) const { std::size_t bomSize = (addBom ? sizeof(g_utf16le_bom)/2 : 0); std::size_t len = ucs4_get_utf16_str_bytes(_pData)/2; _pUtf16 = new char16_t[bomSize + len + 1]; ucs4_to_utf16(_pData, _pUtf16 + bomSize, length(), byteOrder); _pUtf16[len + bomSize] = char16_t(0); if(addBom) { memcpy(_pUtf16, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf16le_bom : g_utf16be_bom), sizeof(g_utf16le_bom)); } return _pUtf16; } /** * @param addBom Указывает, добавлять ли BOM в возвращаемую строку * @param byteOrder Задает порядок байт в возвращаемой строке * @return Указатель на буфер со строкой, закодированной в UTF-32 */ const char32_t* ustring::utf32_str( bool addBom /*= false*/, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ ) const { std::size_t bomSize = (addBom ? sizeof(g_utf32le_bom)/sizeof(char32_t) : 0); _pUtf32 = new char32_t[bomSize + _len + 1]; utf32_strcpy_with_convert_byteorder(_pData, _pUtf32 + bomSize, _len, byteOrder); _pUtf32[_len + bomSize] = char16_t(0); if(addBom) { memcpy(_pUtf32, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf32le_bom : g_utf32be_bom), sizeof(g_utf32le_bom)); } return _pUtf32; } ustring::~ustring() { delete[] _pData; delete[] _pUtf8; delete[] _pUtf16; delete[] _pUtf32; } void ustring::clear_internal_buffers() const { if(_pUtf8) { delete[] _pUtf8; _pUtf8 = nullptr; } if(_pUtf16) { delete[] _pUtf16; _pUtf16 = nullptr; } if(_pUtf32) { delete[] _pUtf32; _pUtf32 = nullptr; } } std::ostream& operator<<(std::ostream& os, const ustring& str) { os << str.utf8_str(); return os; } std::istream& operator>>(std::istream& is, ustring& ustr) { std::string str; is >> str; ustr = str; return is; } /** * @brief Возвращает длину строки * @return Длина строки в символах (не вы байтах!) */ std::size_t ustring::length() const { return _len; } ustring& ustring::operator=(const ustring& str) { std::cout << "ustring copy assign operator" << std::endl; if(this == &str) { return *this; } delete _pData; clear_internal_buffers(); _len = str.length(); _capacity = _len; _pData = new char32_t[_len + 1]; std::memcpy(_pData, str._pData, _len*sizeof(char32_t)); _pData[_len] = char32_t(0); return *this; } ustring& ustring::operator=(ustring&& str) { std::cout << "ustring move assign operator" << std::endl; if(this == &str) { return *this; } delete _pData; clear_internal_buffers(); _pData = std::move(str._pData); _len = str._len; _capacity = _len; str._pData = nullptr; str._len = 0; return *this; } ustring& ustring::operator+=(const ustring& str) { clear_internal_buffers(); std::size_t curLength = length(), strLength = str.length(); std::size_t totalLength = curLength + strLength; char32_t* pBuffer = new char32_t[totalLength + 1]; std::memcpy(pBuffer, _pData, curLength*sizeof(char32_t)); std::memcpy(pBuffer + curLength, str._pData, strLength*sizeof(char32_t)); pBuffer[totalLength] = char32_t(0); delete _pData; _pData = pBuffer; _len = totalLength; return *this; } ustring& ustring::operator+(const ustring& str) { return (*this += str); } char32_t& ustring::operator[](std::size_t index) { return _pData[index]; } bool ustring::operator==( const ustring& str ) const { return ( (_len == str._len) && (!memcmp(_pData, str._pData, _len*sizeof(char32_t))) ); } bool ustring::operator!=( const ustring& str ) const { return !(*this == str); } /** * Поиск символа в строке * @param symbol Символ, который требуется найти * @param pos Начальная позиция, с которой начинается поиск * @return индекс символа в строке или ustring::npos, если символ не найден */ std::size_t ustring::find(char32_t symbol, std::size_t pos /* = 0 */) const { char32_t* pBuf = _pData + pos; do { if(*pBuf == symbol) { return (pBuf - _pData); } } while(*pBuf++ != 0); return npos; } /** * Поиск подстроки в строке * @param строка, которую требуется найти * @param pos Начальная позиция, с которой начинается поиск * @return индекс символа в строке или ustring::npos, если строка не найдена */ std::size_t ustring::find(const ustring& str, std::size_t pos /* = 0 */) const { if(str.length() > (_len - pos)) { return npos; } #if defined(__GNUC__) // GCC and clang /* char32_t* fpos = reinterpret_cast(memmem(_pData + pos, (_len - pos)*sizeof(char32_t), str._pData, str._len*sizeof(char32_t))); if(fpos) { return (fpos - _pData); } */ const char32_t* fpos = _asm_ucs4_strstr(_pData + pos, (_len - pos), str._pData, str._len); if(fpos) { return (fpos - _pData); } #else const char32_t* pStr = str._pData; const std::size_t len = _len - str.length(); for(std::size_t i = pos; i <= len; ++i) { if(_pData[i] == *pStr) { if(!memcmp(&_pData[i], pStr, str.length()*sizeof(char32_t))) { return i; } } } #endif return npos; } /** * Загружает содержимое текстового файла в строку * @param fileName Имя файла * @param enc Кодировка файла * @return Количество считанных байт */ std::size_t ustring::load_from_file( const ustring& fileName, encoding enc ) // TODO: this terrible function needs refactoring { std::ifstream file; #ifdef WIN32 file.open(fileName.utf16_str(), std::ios::binary); #else file.open(fileName.utf8_str(), std::ios::binary); #endif // get file size file.seekg(0, std::ios::end); std::size_t fileLength = static_cast(file.tellg()); file.seekg(0, std::ios::beg); char* fileData = new char[fileLength + sizeof(char32_t)]; file.read(fileData, fileLength); std::memset(fileData + fileLength, 0, sizeof(char32_t)); switch(enc) { case ustring::UTF8: *this = ustring(fileData); break; case ustring::UTF16LE: *this = ustring(reinterpret_cast(fileData)); break; case ustring::UTF32LE: *this = ustring(reinterpret_cast(fileData)); break; default: break; } return fileLength; } /** * Сохраняет содержимое строки в текстовый файл * @param fileName Имя файла * @param enc Кодировка файла * @return Количество записанных байт */ std::size_t ustring::store_to_file( const ustring& fileName, encoding enc ) const // TODO: this terrible function needs refactoring { std::ofstream file; const char* data = nullptr; std::size_t dataSize = 0; const char utf16leBom[] = { char(0xFF), char(0xFE) }; const char utf8Bom[] = { char(0xEF), char(0xBB), char(0xBF) }; const char utf32leBom[] = { char(0xFF), char(0xFE), char(0x00), char(0x00)}; #ifdef WIN32 file.open(fileName.utf16_str(), std::ios::binary); #else file.open(fileName.utf8_str(), std::ios::binary); #endif switch(enc) { case ustring::UTF8: data = utf8_str(); dataSize = ucs4_get_utf8_str_bytes(_pData, false); file.write(utf8Bom, sizeof(utf8Bom)); break; case ustring::UTF16LE: data = reinterpret_cast(utf16_str()); dataSize = ucs4_get_utf16_str_bytes(_pData); file.write(utf16leBom, sizeof(utf16leBom)); break; case ustring::UTF32LE: data = reinterpret_cast(utf32_str()); dataSize = utf32_str_len(utf32_str())*sizeof(char32_t); file.write(utf32leBom, sizeof(utf32leBom)); break; default: break; } file.write(data, dataSize); return dataSize; } /** * Очищает строку */ void ustring::clear() { *_pData = char32_t(0); _len = 0; clear_internal_buffers(); } /** * Проверяет строку на пустоту * @return true, если строка имеет ненулевую длину, иначе false */ bool ustring::empty() const { return (_len == 0); } /** * Выделяет подстроку из строки * @param pos Позиция требуемой подстроки * @param len Длина подстроки в символах * @return Возвращает искомую подстроку */ ustring ustring::substr( size_t pos /*= 0*/, size_t len /*= npos*/ ) const { std::size_t bufLength = (len == npos ? (_len - pos) : len); ustring retVal(bufLength); retVal.assign(_pData + pos, bufLength); return retVal; } /** * Формирует строку из массива символов заданного размера * @param str Массив символов UCS-4 * @param n Количество символов * @return *this */ ustring& ustring::assign( const char32_t* str, std::size_t n ) { clear_internal_buffers(); if(n == 0) { clear(); return *this; } if(n > _capacity) { delete[] _pData; _pData = new char32_t[n + 1]; _capacity = n; } memcpy(_pData, str, n*sizeof(char32_t)); _pData[n] = char32_t(0); _len = n; return *this; } /** * Вырезает часть строки * @param pos Начало вырезаемой части строки * @param len Длина вырезаемой части строки * @return *this */ ustring& ustring::erase( std::size_t pos /*= 0*/, std::size_t len /*= npos*/ ) { if((pos >= _len) || (len == 0)) { return *this; } std::size_t bufLength = (len == npos ? (_len - pos) : len); if(len == npos || (pos + len == _len)) { _pData[pos] = char32_t(0); _len = pos; } else { memmove(_pData + pos, _pData + pos + bufLength, (_len - pos - len)*sizeof(char32_t)); _pData[_len - bufLength] = char32_t(0); _len -= bufLength; } return *this; } /** * Вставляет строку в произвольное место исходной строки * @param pos Позиция для вставки строки * @param str Вставляемая строка * @return *this */ ustring& ustring::insert( std::size_t pos, const ustring& str ) { std::size_t destLength = _len + str.length(); clear_internal_buffers(); if(pos > _len) { pos = _len; } if(_capacity < destLength) { const char32_t* oldData = _pData; _pData = new char32_t[destLength + 1]; _capacity = destLength; memcpy(_pData, oldData, pos*sizeof(char32_t)); memcpy(_pData + pos, str._pData, str.length()*sizeof(char32_t)); memcpy(_pData + pos + str.length(), oldData + pos, (_len - pos)*sizeof(char32_t)); delete[] oldData; } else { memmove(_pData + pos + str.length(), _pData + pos, (_len - pos)*sizeof(char32_t)); memcpy(_pData + pos, str._pData, str.length()*sizeof(char32_t)); } _len = destLength; return *this; } /** * Заменяет все вхождения подстроки "what" на строку "by" * @param what Подстрока, которую нужно заменить * @param by Строка, которая замещает исходную * @return *this */ ustring& ustring::replace( const ustring& what, const ustring& by ) { std::ptrdiff_t diff = by.length() - what.length(); std::size_t pos = 0, newLength = _len; clear_internal_buffers(); while((pos = find(what, pos)) != npos) { if((diff > 0) && (_len + diff > _capacity)) { newLength = _len + diff*5; const char32_t* oldData = _pData; _pData = new char32_t[newLength + 1]; _capacity = newLength; memcpy(_pData, oldData, pos*sizeof(char32_t)); memcpy(_pData + pos, by._pData, by.length()*sizeof(char32_t)); memcpy(_pData + pos + by.length(), oldData + pos + what.length(), (_len - pos - what.length())*sizeof(char32_t)); delete[] oldData; } else { memmove(_pData + pos + by.length(), _pData + pos + what.length(), (_len - pos - what.length())*sizeof(char32_t)); memcpy(_pData + pos, by._pData, by.length()*sizeof(char32_t)); } _len += diff; } return *this; }