Initial uploading sources
This commit is contained in:
parent
a5e64123c8
commit
9edaae6000
20
CMakeLists.txt
Executable file
20
CMakeLists.txt
Executable 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
84
include/ustring.h
Executable 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
79
include/utf.h
Executable 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
19
src/CMakeLists.txt
Executable 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
679
src/ustring.cpp
Executable 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
609
src/utf.cpp
Executable 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
50
test/CMakeLists.txt
Executable 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
554
test/main.cpp
Executable 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();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user