cpputil/src/ustring.cpp
2013-06-10 21:57:42 +04:00

680 lines
17 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ustring.h"
#include <cstring>
#include <fstream>
/**
* Конструктор по умолчанию
*/
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)
* @detailed Так как символы ASCII являются подмножеством UTF-8, при создании строки
* из массива ASCII символов нужно применять именно этот конструктор
*/
ustring::ustring(const char* str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr)
{
init_from_utf8_str(str);
}
/**
* Конструктор из 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
*/
ustring::ustring(const std::string& str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr)
{
init_from_utf8_str(str.c_str());
}
/**
* Конструктор из 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)
{
const char* strData = utf8_skip_bom(str);
_len = utf8_str_len(strData);
_capacity = _len;
_pData = new char32_t[_len + 1];
utf8_to_ucs4(strData, _pData, _len);
_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 в возвращаемую строку
* @return Указатель на буфер со строкой, закодированной в UTF-8
* @detailed Этот буфер является временным и будет высвобожден при любом изменении строки
*/
const char* ustring::utf8_str( bool addBom /*= false*/ ) const
{
if(!_pUtf8)
{
std::size_t bomSize = (addBom ? sizeof(g_utf8_bom) : 0);
std::size_t len = ucs4_get_utf8_str_bytes(_pData);
_pUtf8 = new char[bomSize + len + 1];
ucs4_to_utf8(_pData, _pUtf8 + bomSize, length());
_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
{
if(!_pUtf16)
{
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
{
if(!_pUtf32)
{
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;
std::size_t result = 0;
do
{
if(*pBuf == symbol)
{
return (result + pos);
}
++result;
}
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)
{
return npos;
}
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;
}
}
}
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;
std::size_t symbolTypeSize = 0;
#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<std::size_t>(file.tellg());
file.seekg(0, std::ios::beg);
switch(enc)
{
case ustring::UTF8:
symbolTypeSize = sizeof(char);
break;
case ustring::UTF16LE:
symbolTypeSize = sizeof(char16_t);
break;
case ustring::UTF32LE:
symbolTypeSize = sizeof(char32_t);
break;
default:
break;
}
char* fileData = new char[fileLength + symbolTypeSize];
file.read(fileData, fileLength);
std::memset(fileData + fileLength, 0, symbolTypeSize);
switch(enc)
{
case ustring::UTF8:
*this = ustring(fileData);
break;
case ustring::UTF16LE:
*this = ustring(reinterpret_cast<char16_t*>(fileData));
break;
case ustring::UTF32LE:
*this = ustring(reinterpret_cast<char32_t*>(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);
file.write(utf8Bom, sizeof(utf8Bom));
break;
case ustring::UTF16LE:
data = reinterpret_cast<const char*>(utf16_str());
dataSize = ucs4_get_utf16_str_bytes(_pData);
file.write(utf16leBom, sizeof(utf16leBom));
break;
case ustring::UTF32LE:
data = reinterpret_cast<const char*>(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 ? true : false);
}
/**
* Выделяет подстроку из строки
* @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;
}