Initial uploading sources

This commit is contained in:
Selim Mustafaev 2013-06-10 21:57:42 +04:00
parent a5e64123c8
commit 9edaae6000
8 changed files with 2094 additions and 0 deletions

20
CMakeLists.txt Executable file
View File

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE DEBUG)
if(WIN32)
set(CMAKE_CXX_FLAGS " /D _VARIADIC_MAX=10")
else()
set(CMAKE_CXX_FLAGS "-std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "-O0")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
endif()
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib)
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/usr)
include_directories(include)
add_subdirectory(src)
add_subdirectory(test)

84
include/ustring.h Executable file
View File

@ -0,0 +1,84 @@
#ifndef USTRING_H
#define USTRING_H
#include "utf.h"
#include <iostream>
#include <cstdint>
/**
* Класс строк, работающий с некоторыми кодировками юникода (UTF-8, UTF-16, UTF-32)
* Хранит данные внутри себя в UCS-4 и инкапсулирует в себе кодирование/декодирование
* указанных выше кодировок.
*/
class ustring
{
private:
std::size_t _len;
char32_t* _pData;
private:
mutable std::size_t _capacity;
mutable char* _pUtf8;
mutable char16_t* _pUtf16;
mutable char32_t* _pUtf32;
private:
inline void clear_internal_buffers() const;
inline void init_from_utf8_str(const char* str);
inline void init_from_utf16_str(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
inline void init_from_utf32_str(const char32_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
public:
static const std::size_t npos = -1;
public:
enum encoding { UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE };
public:
ustring();
ustring(const ustring& str);
ustring(ustring&& str);
ustring(const char* str);
ustring(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
ustring(const char32_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
ustring(const std::string& str);
ustring(const std::wstring& str);
explicit ustring(std::size_t n);
operator std::string() const;
operator std::wstring() const;
const char* utf8_str(bool addBom = false) const;
const char16_t* utf16_str(bool addBom = false, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN) const;
const char32_t* utf32_str(bool addBom = false, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN) const;
std::size_t length() const;
std::size_t load_from_file(const ustring& fileName, encoding enc);
std::size_t store_to_file(const ustring& fileName, encoding enc) const;
ustring& operator=(const ustring& str);
ustring& operator=(ustring&& str);
ustring& operator+=(const ustring& str);
ustring& operator+(const ustring& str);
bool operator==(const ustring& str) const;
bool operator!=(const ustring& str) const;
char32_t& operator[](std::size_t index);
ustring& assign(const char32_t* str, std::size_t n);
ustring& erase(std::size_t pos = 0, std::size_t len = npos);
ustring& insert(std::size_t pos, const ustring& str);
ustring& replace(const ustring& what, const ustring& by);
void clear();
bool empty() const;
std::size_t find(char32_t symbol, std::size_t pos = 0) const;
std::size_t find(const ustring& str, std::size_t pos = 0) const;
ustring substr(std::size_t pos = 0, std::size_t len = npos) const;
friend std::ostream& operator<<(std::ostream& os, const ustring& ustr);
friend std::istream& operator>>(std::istream& is, ustring& ustr);
~ustring();
};
#endif // USTRING_H

79
include/utf.h Executable file
View File

@ -0,0 +1,79 @@
#ifndef UTF_H
#define UTF_H
#include <cstdint>
#include <string>
#include <exception>
#include <sstream>
/**
* @brief Класс исключения неправильной перекодировки
* @detailed Как правило исключение этого типа означает, что исходная строка
* не соответствует указанной кодировке
*/
class bad_conversion: public std::exception
{
private:
std::string _msg;
char32_t _badSymbol;
public:
bad_conversion(const char* msg, char32_t badData): _badSymbol(badData)
{
std::stringstream errStream;
errStream << msg << " bad symbol: " << _badSymbol;
_msg = errStream.str();
}
virtual ~bad_conversion() throw() {}
virtual const char* what() const throw()
{
return _msg.c_str();
}
};
#define UTF8_BOM_SIZE 3
#define UTF16_BOM_SIZE 2
#define UTF32_BOM_SIZE 4
extern const unsigned char g_utf8_bom[UTF8_BOM_SIZE];
extern const unsigned char g_utf16le_bom[UTF16_BOM_SIZE];
extern const unsigned char g_utf16be_bom[UTF16_BOM_SIZE];
extern const unsigned char g_utf32le_bom[UTF32_BOM_SIZE];
extern const unsigned char g_utf32be_bom[UTF32_BOM_SIZE];
enum byte_order
{
BYTE_ORDER_LITTLE_ENDIAN,
BYTE_ORDER_BIG_ENDIAN
};
byte_order current_byte_order();
inline char16_t invert_byte_order_16(char16_t val);
inline char32_t invert_byte_order_32(char32_t val);
std::size_t utf8_first_byte_mask(std::size_t symbolSize);
const char* utf8_skip_bom(const char* str);
const char16_t* utf16_skip_bom(const char16_t* str);
const char32_t* utf32_skip_bom(const char32_t* str);
std::size_t significant_bits(uint32_t v);
void utf32_strcpy_with_convert_byteorder(const char32_t* src, char32_t* dst, std::size_t num, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
void utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols);
void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols);
void utf16_to_ucs4(const char16_t* src, char32_t* dst, std::size_t symbols, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
void ucs4_to_utf16(const char32_t* src, char16_t* dst, std::size_t symbols, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
std::size_t utf8_str_len(const char* str);
std::size_t utf8_get_symbol_size(const char* str);
std::size_t utf16_str_len(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
std::size_t utf16_get_symbol_size(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
std::size_t utf32_str_len(const char32_t* str);
std::size_t ucs4_get_utf8_symbol_size(const char32_t symbol);
std::size_t ucs4_get_utf8_str_bytes(const char32_t* str);
std::size_t ucs4_get_utf16_symbol_size(const char32_t symbol);
std::size_t ucs4_get_utf16_str_bytes(const char32_t* str);
#endif // UTF_H

19
src/CMakeLists.txt Executable file
View File

@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 2.8)
set(MAIN_PROJECT_NAME "cpputil")
set(SRC_DIR ".")
set(INCLUDE_DIR "../include")
project(${MAIN_PROJECT_NAME} CXX)
aux_source_directory(${SRC_DIR} SRC_FILES)
include_directories(${INCLUDE_DIR})
add_library(${MAIN_PROJECT_NAME} STATIC ${SRC_FILES})
target_link_libraries(${MAIN_PROJECT_NAME})
install(TARGETS ${MAIN_PROJECT_NAME}
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib)
install(DIRECTORY ${INCLUDE_DIR}/
DESTINATION include/${PROJECT_NAME}
FILES_MATCHING PATTERN "*.h")

679
src/ustring.cpp Executable file
View File

@ -0,0 +1,679 @@
#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;
}

609
src/utf.cpp Executable file
View File

@ -0,0 +1,609 @@
#include "utf.h"
#include <cstring>
#ifdef __GNUC__
#include <byteswap.h>
#endif
/**
* Максимальный размер символа UTF-8 в байтах
*/
#define UTF8_MAX_SYMBOL_SIZE 4
/**
* Количество бит в символе UCS-4
*/
#define UCS4_SYMBOL_BITS 32
/**
* Маска для выделения бит, определяющих суррогатную пару
*/
#define UTF16_SP_MASK 0xFC00
/**
* Маска для установки первого слова суррогатной пары
*/
#define UTF16_SP_LEADING_WORD 0xD800
/**
* Маска для установки второго слова суррогатной пары
*/
#define UTF16_SP_TRAILING_WORD 0xDC00
/**
* Начало служебного диапазона символов, используемого для формирования суррогатных пар
*/
#define UTF16_SP_RANGE_BEGIN 0xD800
/**
* Конец служебного диапазона символов, используемого для формирования суррогатных пар
*/
#define UTF16_SP_RANGE_END 0xDFFF
/**
* Маска для выделения данных в случае суррогатной пары
*/
#define UTF16_DATA_MASK 0x3FF
/**
* Максимально возможный код символа юникода
*/
#define MAX_UNICODE_CHARACTER_CODE 0x10FFFF
/**
* Текущий порядок байт в системе. Определяется в рантайме, так как на железках с bi-endian
* (например ARM) порядок байт не задан жестко, а устанавливается операционной системой
*/
static const byte_order g_system_byte_order = current_byte_order();
const unsigned char g_utf8_bom[] = {0xef, 0xbb, 0xbf};
const unsigned char g_utf16le_bom[] = {0xff, 0xfe};
const unsigned char g_utf16be_bom[] = {0xfe, 0xff};
const unsigned char g_utf32le_bom[] = {0xff, 0xfe, 0x00, 0x00};
const unsigned char g_utf32be_bom[] = {0x00, 0x00, 0xfe, 0xff};
/**
*
* @return Порядок байт
*/
byte_order current_byte_order()
{
union {
uint32_t i;
char c[sizeof(uint32_t)];
} bint = {0x01020304};
return (bint.c[0] == 1 ? BYTE_ORDER_BIG_ENDIAN : BYTE_ORDER_LITTLE_ENDIAN);
}
/**
* Меняет порядок байт в 16-битном символе
* @param val Исходный символ
* @return Символ с инвертированным порядком байт
*/
char16_t invert_byte_order_16( char16_t val )
{
#ifdef __GNUC__
return bswap_16(val);
#elif defined(_MSC_VER)
return _byteswap_ushort(val);
#else
#error compiler not supported
#endif
}
/**
* Меняет порядок байт в 32-битном символе
* @param val Исходный символ
* @return Символ с инвертированным порядком байт
*/
char32_t invert_byte_order_32(char32_t val)
{
#ifdef __GNUC__
return bswap_32(val);
#elif defined(_MSC_VER)
return _byteswap_ulong(val);
#else
#error compiler not supported
#endif
}
/**
* Декодирует UTF-8 в UCS-4
* @param src Указатель на буфер с UTF-8 строкой (без BOM)
* @param dst Указатель на буфер для UCS-4 строки
* @param symbols Количество символов в строке
* @throw bad_conversion Генерируется в случае ошибки декодирования (строка на входе закодирована не в UTF-8)
* @detailed Функция поддерживает декодирование экранированных UCS-2 (\uXXXX)
* и UCS-4 (\UXXXXXXXX) символов
*/
void utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols)
{
std::size_t bytes = 0;
uint32_t s = 0;
for(std::size_t i = 0; i < symbols; ++i)
{
bytes = utf8_get_symbol_size(src);
s = 0;
if(bytes == 1) // ASCII
{
s = *src;
}
else if(bytes > 1 && bytes <= UTF8_MAX_SYMBOL_SIZE) // Unicode
{
std::size_t pos = 0;
for(std::size_t n = bytes; n > 1; --n)
{
if((*(src + n - 1) & 0xC0) != 0x80)
{
throw bad_conversion("error converting UTF-8 to UCS-4", *(char32_t*)src);
}
s |= (static_cast<uint32_t>(static_cast<uint8_t>(*(src + n - 1)) & 0x3F) << pos++*6);
}
s |= (static_cast<uint32_t>(static_cast<uint8_t>(*src << bytes) >> bytes) << pos*6);
}
else if(bytes == 6 || bytes == 10) // Escaped unicode character
{
if(*src == '\\')
{
if(*(src + 1) == 'u')
{
if(sscanf(src + 2, "%4X", &s) == EOF)
{
throw bad_conversion("error converting UTF-8 to UCS-4", *(char32_t*)src);
}
}
else if(*(src + 1) == 'U')
{
if(sscanf(src + 2, "%8X", &s) == EOF)
{
throw bad_conversion("error converting UTF-8 to UCS-4", *(char32_t*)src);
}
}
else
{
throw bad_conversion("error converting UTF-8 to UCS-4", *(char32_t*)src);
}
}
else
{
throw bad_conversion("error converting UTF-8 to UCS-4", *(char32_t*)src);
}
}
else
{
throw bad_conversion("error converting UTF-8 to UCS-4", *(char32_t*)src);
}
dst[i] = s;
src += bytes;
}
}
/**
* Кодирует UCS-4 в UTF-8
* @param src Указатель на буфер с UCS-4 строкой
* @param dst Указатель на буфер для UTF-8 строки
* @param symbols Количество символов в строке
* @throw bad_conversion Генерируется в случае ошибки кодирования (хотя бы один символ не входит
* в набор символов юникода (UCS-4))
*/
void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols)
{
std::size_t sBits, totalBytes;
uint32_t s;
for(std::size_t i = 0; i < symbols; ++i)
{
if(src[i] > MAX_UNICODE_CHARACTER_CODE)
{
throw bad_conversion("error converting UCS-4 to UTF-8", *src);
}
sBits = significant_bits(src[i]);
totalBytes = ucs4_get_utf8_symbol_size(src[i]);
s = static_cast<uint32_t>(src[i]);
if(totalBytes == 1)
{
*dst = static_cast<char>(src[i]);
++dst;
continue;
}
std::size_t pos = 0, shiftLeft, shiftRight;
for(std::size_t n = totalBytes; n > 1; --n)
{
shiftLeft = UCS4_SYMBOL_BITS - (pos + 1)*6;
shiftRight = pos*6;
++pos;
*(dst + n - 1) = static_cast<char>((s << shiftLeft) >> (shiftLeft + shiftRight)) | 0x80;
}
shiftLeft = UCS4_SYMBOL_BITS - sBits;
shiftRight = pos*6;
*dst = static_cast<char>((s << shiftLeft) >> (shiftLeft + shiftRight)) | utf8_first_byte_mask(totalBytes);
dst += totalBytes;
}
}
/**
* Возвращает длину UTF-8 строки
* @param str UTF-8 строка
* @return Длина втроки в символах
*/
std::size_t utf8_str_len(const char* str)
{
std::size_t len = 0;
while(*str)
{
str += utf8_get_symbol_size(str);
++len;
}
return len;
}
/**
* Возвращает количество байт, которые займет строка, будучи кодированной в UTF-8
* @param str Исходная строка в UCS-4
* @return Количество байт, необходимые для UTF-8 представления строки
*/
std::size_t ucs4_get_utf8_str_bytes(const char32_t* str)
{
std::size_t bytes = 0;
while(*str)
{
bytes += ucs4_get_utf8_symbol_size(*str);
++str;
}
return bytes;
}
/**
* Возвращает размер символа UTF-8 строки с учетом экранированных UCS-2 и UCS-4 символов
* @param str Указатель на требуемый символ в UTF-8 строке
* @return Количество байт, занимаемых символом
*/
std::size_t utf8_get_symbol_size(const char* str)
{
uint32_t s = 0;
if(*str == '\\') // UCS unescape
{
if(*(str + 1) == 'u')
{
return 6;
}
else if(*(str + 1) == 'U')
{
return 10;
}
}
if(*str > 0) return 1;
else if((*str & 0xC0) && !(*str & 0x20)) return 2;
else if((*str & 0xE0) && !(*str & 0x10)) return 3;
else if((*str & 0xF0) && !(*str & 0x8)) return 4;
else throw bad_conversion("cannot calc size of UTF-8 symbol", *(char32_t*)str);
}
/**
* Возвращает маску для установки старших бит первого байта UTF-8 символа
* Используется для кодирования в UTF-8
* @param symbolSize Размер символа в байтах
* @return Маска для старших бит первого байта символа
*/
std::size_t utf8_first_byte_mask(std::size_t symbolSize)
{
switch(symbolSize)
{
case 2: return 0xC0;
case 3: return 0xE0;
case 4: return 0xF0;
case 5: return 0xF8;
case 6: return 0xE0;
default: return 0;
}
return 0;
}
/**
* Вычисляет, какой размер будет у символа, если кодировать его в UTF-8
* @param symbol Исходный символ в UCS-4
* @return Размер UTF-8 символа в байтах
* @throw bad_conversion Генерируется если символ не входит в набор символов юникода (UCS-4)
*/
std::size_t ucs4_get_utf8_symbol_size(const char32_t symbol)
{
if(symbol > MAX_UNICODE_CHARACTER_CODE)
{
throw bad_conversion("error converting UCS-4 to UTF-8", symbol);
}
std::size_t sBits = significant_bits(symbol);
std::size_t sixBitBytes = sBits / 6;
std::size_t restBits = sBits % 6;
uint32_t s = static_cast<uint32_t>(symbol);
if(sBits <= 7)
{
return 1;
}
std::size_t totalBytes;
if(restBits <= (6 - sixBitBytes))
{
totalBytes = sixBitBytes + 1;
}
else
{
totalBytes = sixBitBytes + 2;
}
return totalBytes;
}
/**
* Вычисляет количество значимых бит в 32-х битном числе (количество бит, минимально необходимых
* для представления числа). Например в числе 18 - 5 значимых бит (10010)
* @param v Исходное число
* @return Количество значимых бит
*/
std::size_t significant_bits(uint32_t v)
{
const std::size_t lut[32] = {0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31};
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return lut[(unsigned int)(v * 0x07C4ACDDU) >> 27] + 1;
}
/**
* Декодирует UTF-16 в UCS-4
* @param src Исходная строка в UTF-16 без BOM
* @param dst Указатель на буфер для строки в UCS-4
* @param symbols Количество декодируемых символов
* @param byteOrder Порядок байт в строке
* @throw bad_conversion Генерируется в случае ошибки декодирования (строка на входе закодирована не в UTF-16)
*/
void utf16_to_ucs4( const char16_t* src, char32_t* dst, std::size_t symbols, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ )
{
char16_t leadingWord = 0, trailingWord = 0;
for(std::size_t i = 0; i < symbols; ++i)
{
leadingWord = (byteOrder == g_system_byte_order ? *src : invert_byte_order_16(*src));
if((leadingWord < UTF16_SP_RANGE_BEGIN) || (leadingWord > UTF16_SP_RANGE_END))
{
dst[i] = static_cast<char32_t>(leadingWord);
++src;
}
else if((leadingWord & UTF16_SP_MASK) == UTF16_SP_LEADING_WORD) // surrogate pair
{
trailingWord = (byteOrder == g_system_byte_order ? *(src + 1) : invert_byte_order_16(*(src + 1)));
if((trailingWord & UTF16_SP_MASK) != UTF16_SP_TRAILING_WORD)
{
throw bad_conversion("error converting UTF-16LE to UCS-4", *(char32_t*)src);
}
dst[i] = (((static_cast<char32_t>(leadingWord) & UTF16_DATA_MASK) << 10) | (static_cast<char32_t>(trailingWord) & UTF16_DATA_MASK)) + 0x10000;
src += 2;
}
else
{
throw bad_conversion("error converting UTF-16 to UCS-4", *(char32_t*)src);
}
}
}
/**
* Кодирует UCS-4 в UTF-16
* @param src Исходная строка в UCS-4
* @param dst Указатель на буфер для UTF-16 строки
* @param symbols Количество кодируемых символов
* @param byteOrder Порядок байт в строке
* @throw bad_conversion Генерируется в случае ошибки кодирования (хотя бы один символ
* строки на входе не входит в диапазон UCS-4)
*/
void ucs4_to_utf16( const char32_t* src, char16_t* dst, std::size_t symbols, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ )
{
for(std::size_t i = 0; i < symbols; ++i)
{
if(src[i] > MAX_UNICODE_CHARACTER_CODE)
{
throw bad_conversion("error converting UCS-4 to UTF-16", *src);
}
else if(src[i] < 0x10000)
{
*dst = static_cast<char16_t>(src[i]);
if(byteOrder != g_system_byte_order)
{
*dst = invert_byte_order_16(*dst);
}
++dst;
}
else // construct surrogate pair
{
*(dst) = static_cast<char16_t>(((src[i] - 0x10000) >> 10) | UTF16_SP_LEADING_WORD);
*(dst + 1) = static_cast<char16_t>(((src[i] - 0x10000) & UTF16_DATA_MASK) | UTF16_SP_TRAILING_WORD);
if(byteOrder != g_system_byte_order)
{
*dst = invert_byte_order_16(*dst);
*(dst + 1) = invert_byte_order_16(*(dst + 1));
}
dst += 2;
}
}
}
/**
* Возвращает длину UTF-16 строки
* @param str Строка, длину которой нужно посчитать (null terminated)
* @param byteOrder Порядок байт в строке
* @return Длина строки в символах
*/
std::size_t utf16_str_len( const char16_t* str, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ )
{
std::size_t len = 0;
while(*str)
{
str += utf16_get_symbol_size(str, byteOrder)/2;
++len;
}
return len;
}
/**
* Возвращает размер символа в UTF-16 строке
* @param str Указатель на начало символа в строке
* @param byteOrder Порядок байт в строке
* @return Размер символа в байтах
*/
std::size_t utf16_get_symbol_size( const char16_t* str, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ )
{
char16_t symbol = (byteOrder == g_system_byte_order ? *str : invert_byte_order_16(*str));
return ((symbol < UTF16_SP_RANGE_BEGIN) || (symbol > UTF16_SP_RANGE_END)) ? 2 : 4;
}
/**
* Рассчитывает, сколько байт займет UCS-4 символ, будучи закодированным в UTF-16
* @param symbol Исходный UCS-4 символ
* @return Размер символа в UTF-16 (в байтах)
* @throw bad_conversion Генерируется если символ не входит в диапазон UCS-4
*/
std::size_t ucs4_get_utf16_symbol_size( const char32_t symbol )
{
if(symbol > MAX_UNICODE_CHARACTER_CODE)
{
throw bad_conversion("error converting UCS-4 to UTF-16", symbol);
}
return (symbol < 0x10000) ? 2 : 4;
}
/**
* Рассчитывает, сколько байт займет UCS-4 строка, будучи закодированной в UTF-16
* @param str Исходная строка в UCS-4
* @return Размер строки в UTF-16
*/
std::size_t ucs4_get_utf16_str_bytes( const char32_t* str )
{
std::size_t bytes = 0;
while(*str)
{
bytes += ucs4_get_utf16_symbol_size(*str);
++str;
}
return bytes;
}
/**
* Пропускает метку BOM в UTF-8 строке
* @param str Исходная строка в UTF-8
* @return Указатель на начало данных в строке
*/
const char* utf8_skip_bom( const char* str )
{
if(!memcmp(str, g_utf8_bom, sizeof(g_utf8_bom)))
{
return str + 3;
}
else
{
return str;
}
}
/**
* Пропускает метку BOM в UTF-16 строке
* @param str Исходная строка в UTF-16
* @return Указатель на начало данных в строке
*/
const char16_t* utf16_skip_bom( const char16_t* str )
{
if(!memcmp(str, g_utf16le_bom, sizeof(g_utf16le_bom)) || !memcmp(str, g_utf16be_bom, sizeof(g_utf16be_bom)))
{
return str + 1;
}
else
{
return str;
}
}
/**
* Считает длину UTF-32 строки
* @param str Исходная UTF-32 строка (null terminated)
* @return Длина строки в символах
*/
std::size_t utf32_str_len( const char32_t* str )
{
std::size_t len = 0;
while(*str)
{
++str;
++len;
}
return len;
}
/**
* Пропускает метку BOM в UTF-32 строке
* @param str Исходная строка в UTF-32
* @return Указатель на начало данных в строке
*/
const char32_t* utf32_skip_bom( const char32_t* str)
{
if(!memcmp(str, g_utf32le_bom, sizeof(g_utf32le_bom)) || !memcmp(str, g_utf32be_bom, sizeof(g_utf32be_bom)))
{
return str + 1;
}
else
{
return str;
}
}
/**
* Копирует массив символов UTF-32, если byteOrder не сопадает с текущим порядком байт,
* инвертирует порядок байт в каждом символе
* @param src Исходная строка
* @param dst Строка назначения
* @param num Количество символов
* @param byteOrder Порядок байт
*/
void utf32_strcpy_with_convert_byteorder(const char32_t* src, char32_t* dst, std::size_t num, byte_order byteOrder /* = BYTE_ORDER_LITTLE_ENDIAN */)
{
if(g_system_byte_order == byteOrder)
{
memcpy(dst, src, num*sizeof(char32_t));
}
else
{
for(std::size_t i = 0; i < num; ++i)
{
dst[i] = invert_byte_order_32(src[i]);
}
}
}

50
test/CMakeLists.txt Executable file
View File

@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 2.8)
set(TEST_PROJECT_NAME "cpputil_test")
set(SRC_DIR ".")
set(REQUIRED_LIBRARIES cpputil)
if(WIN32)
INCLUDE(ExternalProject)
SET_DIRECTORY_PROPERTIES(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/ThirdParty)
ExternalProject_Add(
googletest
SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/
TIMEOUT 100
CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=Debug
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=Release
-Dgtest_force_shared_crt=ON
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON)
ExternalProject_Get_Property(googletest source_dir)
ExternalProject_Get_Property(googletest binary_dir)
INCLUDE_DIRECTORIES(${source_dir}/include)
#link_directories(${binary_dir}/Debug)
else()
FIND_PACKAGE(GTest REQUIRED)
if(NOT GTEST_FOUND)
message(SEND_ERROR "Failed to find Google Test Framework")
return()
else()
include_directories(${GTEST_INCLUDE_DIRS})
endif()
endif()
project(${TEST_PROJECT_NAME} CXX)
aux_source_directory(${SRC_DIR} TEST_SRC)
add_executable(${TEST_PROJECT_NAME} ${TEST_SRC})
SET_TARGET_PROPERTIES(${TEST_PROJECT_NAME} PROPERTIES ENABLE_EXPORTS TRUE)
ADD_DEPENDENCIES(${TEST_PROJECT_NAME} googletest)
target_link_libraries(${TEST_PROJECT_NAME} ${REQUIRED_LIBRARIES}
debug ${binary_dir}/Debug/gtest.lib
optimized ${binary_dir}/Release/gtest.lib)
install(TARGETS ${TEST_PROJECT_NAME}
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib)

554
test/main.cpp Executable file
View File

@ -0,0 +1,554 @@
#include "ustring.h"
#include "utf.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <gtest/gtest.h>
///////////////////////////////////////////////////////////////////////////////////////////
//
// Данные для тестов
//
// UTF-8
const unsigned char helloStrUtf8[] = { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0xd0, 0x9f, 0xd1,
0x80, 0xd0, 0xb8, 0xd0, 0xb2, 0xd0, 0xb5, 0xd1, 0x82, 0x2c, 0x20, 0xd0, 0x9c, 0xd0, 0xb8, 0xd1,
0x80, 0x21, 0x0a, 0xe2, 0x88, 0x86, 0x20, 0x3d, 0x20, 0xf0, 0x9d, 0x9b, 0xbb, 0xc2, 0xb2, 0x0a,
0x00 };
// UTF-16LE
const unsigned char helloStrUtf16le[] = { 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x57, 0x00, 0x6f, 0x00,
0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21, 0x00, 0x0a, 0x00, 0x1f, 0x04, 0x40, 0x04, 0x38, 0x04,
0x32, 0x04, 0x35, 0x04, 0x42, 0x04, 0x2c, 0x00, 0x20, 0x00, 0x1c, 0x04, 0x38, 0x04, 0x40, 0x04,
0x21, 0x00, 0x0a, 0x00, 0x06, 0x22, 0x20, 0x00, 0x3d, 0x00, 0x20, 0x00, 0x35, 0xd8, 0xfb, 0xde,
0xb2, 0x00, 0x0a, 0x00, 0x00, 0x00 };
// UTF-16BE
const unsigned char helloStrUtf16be[] = { 0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x57, 0x00, 0x6f,
0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21, 0x00, 0x0a, 0x04, 0x1f, 0x04, 0x40, 0x04, 0x38,
0x04, 0x32, 0x04, 0x35, 0x04, 0x42, 0x00, 0x2c, 0x00, 0x20, 0x04, 0x1c, 0x04, 0x38, 0x04, 0x40,
0x00, 0x21, 0x00, 0x0a, 0x22, 0x06, 0x00, 0x20, 0x00, 0x3d, 0x00, 0x20, 0xd8, 0x35, 0xde, 0xfb,
0x00, 0xb2, 0x00, 0x0a, 0x00, 0x00 };
// UCS-4 (UTF-32LE на x86)
const unsigned char helloStrUcs4[] = { 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
0x6f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00,
0x72, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x1f, 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00,
0x32, 0x04, 0x00, 0x00, 0x35, 0x04, 0x00, 0x00, 0x42, 0x04, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x1c, 0x04, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xfb, 0xd6, 0x01, 0x00, 0xb2, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// UTF-32BE
const unsigned char helloStrUtf32be[] = { 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c,
0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x6f,
0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x21,
0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x04, 0x1f, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x04, 0x38,
0x00, 0x00, 0x04, 0x32, 0x00, 0x00, 0x04, 0x35, 0x00, 0x00, 0x04, 0x42, 0x00, 0x00, 0x00, 0x2c,
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x04, 0x38, 0x00, 0x00, 0x04, 0x40,
0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0xd6, 0xfb, 0x00, 0x00, 0x00, 0xb2,
0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00 };
// Длина тестовой строки в символах
const std::size_t helloStrLen = 33;
// Некорректные строки для теста обработки ошибок
const unsigned char badStrUtf8[] = { 0xd0, 0xdf, 0x00 };
const unsigned char badStr2Utf8[] = { 0xf8, 0x9f, 0x00 };
const unsigned char badStrUcs4[] = { 0xf8, 0x9f, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00 };
const unsigned char badStrUtf16le[] = { 0x35, 0xc8, 0xfb, 0xde, 0x00, 0x00 };
const unsigned char badStr2Utf16le[] = { 0x35, 0xd8, 0xfb, 0xce, 0x00, 0x00 };
// Для теста конкатенации
// "Hello "
const unsigned char helloPart1Ucs4[] = { 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
0x6f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// "big"
const unsigned char helloPart2Ucs4[] = { 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// " world"
const unsigned char helloPart3Ucs4[] = { 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
0x6c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// "Hello big world"
const unsigned char helloResultUcs4[] = { 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
0x6f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00,
0x72, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
///////////////////////////////////////////////////////////////////////////////////////////
//
// Тесты
//
//
// Определение длины символа UTF-8
//
TEST(Convert, utf8_symbol_size)
{
ASSERT_EQ(1, utf8_get_symbol_size((const char*)&helloStrUtf8[0]));
ASSERT_EQ(2, utf8_get_symbol_size((const char*)&helloStrUtf8[15]));
ASSERT_EQ(4, utf8_get_symbol_size((const char*)&helloStrUtf8[41]));
}
//
// Определение длины символа UTF-8 по символу UCS-4
//
TEST(Convert, utf8_symbol_size_by_ucs4_symbol)
{
ASSERT_EQ(1, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[0]));
ASSERT_EQ(2, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[52]));
ASSERT_EQ(4, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[120]));
}
//
// Определение длины строки UTF-8
//
TEST(Convert, utf8_length)
{
ASSERT_EQ(helloStrLen, utf8_str_len((const char*)helloStrUtf8));
}
//
// Определение длины строки UTF-8 по строке UCS-4
//
TEST(Convert, utf8_str_len_by_ucs4_str)
{
ASSERT_EQ(sizeof(helloStrUtf8) - 1, ucs4_get_utf8_str_bytes((const char32_t*)helloStrUcs4));
}
//
// Определение длины символа UTF-16LE
//
TEST(Convert, utf16le_symbol_size)
{
ASSERT_EQ(2, utf16_get_symbol_size((const char16_t*)&helloStrUtf16le[0]));
ASSERT_EQ(4, utf16_get_symbol_size((const char16_t*)&helloStrUtf16le[60]));
}
//
// Определение длины символа UTF-16BE
//
TEST(Convert, utf16be_symbol_size)
{
ASSERT_EQ(2, utf16_get_symbol_size((const char16_t*)&helloStrUtf16be[0], BYTE_ORDER_BIG_ENDIAN));
ASSERT_EQ(4, utf16_get_symbol_size((const char16_t*)&helloStrUtf16be[60], BYTE_ORDER_BIG_ENDIAN));
}
//
// Определение длины символа UTF-16 по символу UCS-4
//
TEST(Convert, utf16_symbol_size_by_ucs4_symbol)
{
ASSERT_EQ(2, ucs4_get_utf16_symbol_size(*(const char32_t*)&helloStrUcs4[0]));
ASSERT_EQ(4, ucs4_get_utf16_symbol_size(*(const char32_t*)&helloStrUcs4[120]));
}
//
// Определение длины строки UTF-16LE
//
TEST(Convert, utf16le_length)
{
ASSERT_EQ(helloStrLen, utf16_str_len((const char16_t*)helloStrUtf16le));
}
//
// Определение длины строки UTF-16BE
//
TEST(Convert, utf16be_length)
{
ASSERT_EQ(helloStrLen, utf16_str_len((const char16_t*)helloStrUtf16be, BYTE_ORDER_BIG_ENDIAN));
}
TEST(Convert, utf16_str_len_by_ucs4_str)
{
ASSERT_EQ(sizeof(helloStrUtf16le) - 2, ucs4_get_utf16_str_bytes((const char32_t*)helloStrUcs4));
}
//
// Определение длины строки UTF-32
//
TEST(Convert, utf32_length)
{
ASSERT_EQ(helloStrLen, utf32_str_len((const char32_t*)helloStrUcs4));
}
//
// Конвертирование UTF-8 в UCS-4
//
TEST(Convert, utf8_to_ucs4)
{
std::size_t len = utf8_str_len((const char*)helloStrUtf8);
char32_t* data = new char32_t[len];
utf8_to_ucs4((const char*)helloStrUtf8, data, len);
EXPECT_EQ(0, memcmp(helloStrUcs4, data, len*sizeof(char32_t)));
delete[] data;
}
//
// Конвертирование UCS-4 в UTF-8
//
TEST(Convert, ucs4_to_utf8)
{
std::size_t len = ucs4_get_utf8_str_bytes((const char32_t*)helloStrUcs4);
std::size_t symbols = utf32_str_len((const char32_t*)helloStrUcs4);
char* data = new char[len];
ucs4_to_utf8((const char32_t*)helloStrUcs4, data, symbols);
EXPECT_EQ(0, memcmp(helloStrUtf8, data, len));
delete[] data;
}
//
// Конвертирование UTF-16LE в UCS-4
//
TEST(Convert, utf16le_to_ucs4)
{
std::size_t len = utf16_str_len((const char16_t*)helloStrUtf16le);
char32_t* data = new char32_t[len];
utf16_to_ucs4((const char16_t*)helloStrUtf16le, data, len);
EXPECT_EQ(0, memcmp(helloStrUcs4, data, len*sizeof(char32_t)));
delete[] data;
}
//
// Конвертирование UCS-4 в UTF-16LE
//
TEST(Convert, ucs4_to_utf16le)
{
std::size_t len = ucs4_get_utf16_str_bytes((const char32_t*)helloStrUcs4);
std::size_t symbols = utf32_str_len((const char32_t*)helloStrUcs4);
char16_t* data = new char16_t[len];
ucs4_to_utf16((const char32_t*)helloStrUcs4, data, symbols);
EXPECT_EQ(0, memcmp(helloStrUtf16le, data, len));
delete[] data;
}
//
// Конвертирование UTF-16BE в UCS-4
//
TEST(Convert, utf16be_to_ucs4)
{
std::size_t len = utf16_str_len((const char16_t*)helloStrUtf16be);
char32_t* data = new char32_t[len];
utf16_to_ucs4((const char16_t*)helloStrUtf16be, data, len, BYTE_ORDER_BIG_ENDIAN);
EXPECT_EQ(0, memcmp(helloStrUcs4, data, len*sizeof(char32_t)));
delete[] data;
}
//
// Конвертирование UCS-4 в UTF-16BE
//
TEST(Convert, ucs4_to_utf16be)
{
std::size_t len = ucs4_get_utf16_str_bytes((const char32_t*)helloStrUcs4);
std::size_t symbols = utf32_str_len((const char32_t*)helloStrUcs4);
char16_t* data = new char16_t[len];
ucs4_to_utf16((const char32_t*)helloStrUcs4, data, symbols, BYTE_ORDER_BIG_ENDIAN);
EXPECT_EQ(0, memcmp(helloStrUtf16be, data, len));
delete[] data;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Тесты на ошибки конвертирования
//
//
// Первый байт двухбайтового символа содержит единицы в пяти старших разрядах
//
TEST(ConvertError, utf8_length_error)
{
EXPECT_THROW(utf8_str_len((const char*)badStr2Utf8), bad_conversion);
}
//
// Второй байт двухбайтового символа содержит единицы в двух старших разрядах
//
TEST(ConvertError, utf8_decode_error)
{
std::size_t len = utf8_str_len((const char*)badStrUtf8);
char32_t* data = new char32_t[len];
EXPECT_THROW(utf8_to_ucs4((const char*)badStrUtf8, data, len), bad_conversion);
delete[] data;
}
//
// Некорректное перевое слово суррогатной пары
//
TEST(ConvertError, utf16_decode_error)
{
std::size_t len = utf16_str_len((const char16_t*)badStrUtf16le);
char32_t* data = new char32_t[len];
EXPECT_THROW(utf16_to_ucs4((const char16_t*)badStrUtf16le, data, len), bad_conversion);
delete[] data;
}
//
// Некорректное второе слово суррогатной пары
//
TEST(ConvertError, utf16_decode_error2)
{
std::size_t len = utf16_str_len((const char16_t*)badStr2Utf16le);
char32_t* data = new char32_t[len];
EXPECT_THROW(utf16_to_ucs4((const char16_t*)badStr2Utf16le, data, len), bad_conversion);
delete[] data;
}
//
// Код символа не входит в диапазон символов UCS-4
//
TEST(ConvertError, ucs4_wrong_symbol)
{
EXPECT_THROW(ucs4_get_utf8_symbol_size(*(const char32_t*)badStrUcs4), bad_conversion);
EXPECT_THROW(ucs4_get_utf16_symbol_size(*(const char32_t*)badStrUcs4), bad_conversion);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Тесты класса ustring
//
//
// Конструктор из UTF-8
//
TEST(Ustring, utf8_constructor)
{
ustring str((const char*)helloStrUtf8);
EXPECT_EQ(0, memcmp(helloStrUcs4, str.utf32_str(), sizeof(helloStrUcs4)));
}
//
// Конструктор из UTF-16LE
//
TEST(Ustring, utf16_constructor)
{
ustring str((const char16_t*)helloStrUtf16le);
EXPECT_EQ(0, memcmp(helloStrUcs4, str.utf32_str(), sizeof(helloStrUcs4)));
}
//
// Конструктор из UTF-32LE
//
TEST(Ustring, utf32_constructor)
{
ustring str((const char32_t*)helloStrUcs4);
EXPECT_EQ(0, memcmp(helloStrUcs4, str.utf32_str(), sizeof(helloStrUcs4)));
}
//
// Оператор UTF-8
//
TEST(Ustring, utf8_cast_operator)
{
ustring str((const char32_t*)helloStrUcs4);
EXPECT_EQ(0, memcmp(helloStrUtf8, str.utf8_str(), sizeof(helloStrUtf8)));
}
//
// Оператор UTF-16LE
//
TEST(Ustring, utf16_cast_operator)
{
ustring str((const char32_t*)helloStrUcs4);
EXPECT_EQ(0, memcmp(helloStrUtf16le, str.utf16_str(), sizeof(helloStrUtf16le)));
}
//
// Конкатенация строк (operator+=)
//
TEST(Ustring, concat_op_plus_assign)
{
ustring s1((const char32_t*)helloPart1Ucs4);
ustring s2((const char32_t*)helloPart2Ucs4);
ustring s3((const char32_t*)helloPart3Ucs4);
s1 += s2;
s1 += s3;
EXPECT_EQ(0, memcmp(helloResultUcs4, s1.utf32_str(), sizeof(helloResultUcs4)));
}
//
// Конкатенация строк (operator+)
//
TEST(Ustring, concat_op_plus)
{
ustring s1((const char32_t*)helloPart1Ucs4);
//ustring s2((const char32_t*)helloPart2Ucs4);
//ustring s3((const char32_t*)helloPart3Ucs4);
ustring res = s1 + "big" + " world";
EXPECT_EQ(0, memcmp(helloResultUcs4, res.utf32_str(), sizeof(helloResultUcs4)));
}
//
// Поиск символа в строке
//
TEST(Ustring, find_symbol)
{
ustring str((const char32_t*)helloResultUcs4);
EXPECT_EQ(4, str.find('o'));
EXPECT_EQ(11, str.find('o', 8));
}
//
// Поиск подстроки в строке
//
TEST(Ustring, find_string)
{
ustring str = "Hello World! asdf qwerty asdf";
EXPECT_EQ(13, str.find("asdf"));
EXPECT_EQ(25, str.find("asdf", 14));
}
//
// Вывод в поток std::ostream
//
TEST(Ustring, std_ostream_output)
{
std::stringstream ss;
ustring str((const char32_t*)helloResultUcs4);
ss << str;
EXPECT_EQ(0, strcmp(ss.str().c_str(), "Hello big world"));
}
//
// Ввод из std::istream
//
TEST(Ustring, std_istream_intput)
{
std::stringstream ss;
ustring str;
ss << "big";
ss >> str;
EXPECT_EQ(0, memcmp(helloPart2Ucs4, str.utf32_str(), sizeof(helloPart2Ucs4)));
}
//
// Проверка на равенство
//
TEST(Ustring, equality)
{
ustring str((const char32_t*)helloResultUcs4);
ustring str2;
EXPECT_TRUE(str == "Hello big world");
EXPECT_TRUE(str2 == "");
}
//
// Установление нового значения строки
//
TEST(Ustring, assign_new_value)
{
ustring s1 = "first string";
ustring s2 = "second string";
s2.assign(s1.utf32_str(false, current_byte_order()) + 6, 6);
EXPECT_TRUE(s2 == "string");
}
//
// Возвращение подстроки
//
TEST(Ustring, substring)
{
ustring str = "Hello big world!";
ustring str2 = str.substr(6, 3);
ustring str3 = str.substr(6, ustring::npos);
EXPECT_TRUE(str2 == "big");
EXPECT_TRUE(str3 == "big world!");
}
//
// Стирание части строки
//
TEST(Ustring, erasing)
{
ustring str = "Hello big world!";
str.erase(6,4);
EXPECT_TRUE(str == "Hello world!");
str.erase(5, ustring::npos);
EXPECT_TRUE(str == "Hello");
}
//
// Вставка строки в строку
//
TEST(Ustring, insert_string)
{
ustring str = "bla-bla";
str.insert(0, "begin ");
EXPECT_TRUE(str == "begin bla-bla");
str.insert(6, "bla ");
EXPECT_TRUE(str == "begin bla bla-bla");
str.insert(17, " end");
EXPECT_TRUE(str == "begin bla bla-bla end");
}
//
// Замена подвтроки строкой
//
TEST(Ustring, replace_string)
{
ustring str = "111 hello 111 world 111";
ustring str2 = str, str3 = str;
str.replace("111", "2222");
EXPECT_TRUE(str == "2222 hello 2222 world 2222");
str2.replace("111", "22");
EXPECT_TRUE(str2 == "22 hello 22 world 22");
str3.replace("111", "222");
EXPECT_TRUE(str3 == "222 hello 222 world 222");
str2 += " more 22 and 22 more 22";
str2.replace("22", "33");
EXPECT_TRUE(str2 == "33 hello 33 world 33 more 33 and 33 more 33");
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}