Сделал декодирование экранированных UTF-8 символов опциональным

This commit is contained in:
Selim Mustafaev 2013-10-06 23:53:06 +04:00
parent 3a3b6b18fd
commit 0c3287aa1b
6 changed files with 44 additions and 31 deletions

View File

@ -24,7 +24,7 @@ private:
private: private:
inline void clear_internal_buffers() const; inline void clear_internal_buffers() const;
inline void init_from_utf8_str(const char* str); inline void init_from_utf8_str(const char* str, bool needUnescape);
inline void init_from_utf16_str(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN); 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); inline void init_from_utf32_str(const char32_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
@ -38,10 +38,10 @@ public:
ustring(); ustring();
ustring(const ustring& str); ustring(const ustring& str);
ustring(ustring&& str); ustring(ustring&& str);
ustring(const char* str); ustring(const char* str, bool needUnescape = false);
ustring(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN); 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 char32_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
ustring(const std::string& str); ustring(const std::string& str, bool needUnescape = false);
ustring(const std::wstring& str); ustring(const std::wstring& str);
explicit ustring(std::size_t n); explicit ustring(std::size_t n);

View File

@ -60,13 +60,13 @@ const char32_t* utf32_skip_bom(const char32_t* str);
std::size_t significant_bits(uint32_t v); 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 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 utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols, bool needUnescape);
void ucs4_to_utf8(const char32_t* src, char* 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 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); 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_str_len(const char* str, bool needUnescape);
std::size_t utf8_get_symbol_size(const char* str); std::size_t utf8_get_symbol_size(const char* str, bool needUnescape);
std::size_t utf16_str_len(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN); 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 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 utf32_str_len(const char32_t* str);

View File

@ -44,12 +44,13 @@ ustring::ustring(ustring&& str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(null
/** /**
* Конструктор из UTF-8 строки * Конструктор из UTF-8 строки
* @param str Исходная строка в UTF-8 (null terminated) * @param str Исходная строка в UTF-8 (null terminated)
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @detailed Так как символы ASCII являются подмножеством UTF-8, при создании строки * @detailed Так как символы ASCII являются подмножеством UTF-8, при создании строки
* из массива ASCII символов нужно применять именно этот конструктор * из массива ASCII символов нужно применять именно этот конструктор
*/ */
ustring::ustring(const char* str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) ustring::ustring(const char* str, bool needUnescape /* = false */): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr)
{ {
init_from_utf8_str(str); init_from_utf8_str(str, needUnescape);
} }
/** /**
@ -75,10 +76,11 @@ ustring::ustring(const char32_t* str, byte_order byteOrder /* = BYTE_ORDER_LITTL
/** /**
* Конструктор из std::string * Конструктор из std::string
* @param str Исходная строка в UTF-8 * @param str Исходная строка в UTF-8
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
*/ */
ustring::ustring(const std::string& str): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr) ustring::ustring(const std::string& str, bool needUnescape /* = false */): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nullptr)
{ {
init_from_utf8_str(str.c_str()); init_from_utf8_str(str.c_str(), needUnescape);
} }
/** /**
@ -102,13 +104,13 @@ ustring::ustring( std::size_t n ): _pUtf8(nullptr), _pUtf16(nullptr), _pUtf32(nu
} }
void ustring::init_from_utf8_str(const char* str) void ustring::init_from_utf8_str(const char* str, bool needUnescape)
{ {
const char* strData = utf8_skip_bom(str); const char* strData = utf8_skip_bom(str);
_len = utf8_str_len(strData); _len = utf8_str_len(strData, needUnescape);
_capacity = _len; _capacity = _len;
_pData = new char32_t[_len + 1]; _pData = new char32_t[_len + 1];
utf8_to_ucs4(strData, _pData, _len); utf8_to_ucs4(strData, _pData, _len, needUnescape);
_pData[_len] = char32_t(0); _pData[_len] = char32_t(0);
} }

View File

@ -113,18 +113,19 @@ char32_t invert_byte_order_32(char32_t val)
* @param src Указатель на буфер с UTF-8 строкой (без BOM) * @param src Указатель на буфер с UTF-8 строкой (без BOM)
* @param dst Указатель на буфер для UCS-4 строки * @param dst Указатель на буфер для UCS-4 строки
* @param symbols Количество символов в строке * @param symbols Количество символов в строке
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @throw bad_conversion Генерируется в случае ошибки декодирования (строка на входе закодирована не в UTF-8) * @throw bad_conversion Генерируется в случае ошибки декодирования (строка на входе закодирована не в UTF-8)
* @detailed Функция поддерживает декодирование экранированных UCS-2 (\uXXXX) * @detailed Функция поддерживает декодирование экранированных UCS-2 (\uXXXX)
* и UCS-4 (\UXXXXXXXX) символов * и UCS-4 (\UXXXXXXXX) символов
*/ */
void utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols) void utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols, bool needUnescape)
{ {
std::size_t bytes = 0; std::size_t bytes = 0;
uint32_t s = 0; uint32_t s = 0;
for(std::size_t i = 0; i < symbols; ++i) for(std::size_t i = 0; i < symbols; ++i)
{ {
bytes = utf8_get_symbol_size(src); bytes = utf8_get_symbol_size(src, needUnescape);
s = 0; s = 0;
if(bytes == 1) // ASCII if(bytes == 1) // ASCII
@ -236,15 +237,16 @@ void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols)
/** /**
* Возвращает длину UTF-8 строки * Возвращает длину UTF-8 строки
* @param str UTF-8 строка * @param str UTF-8 строка
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @return Длина втроки в символах * @return Длина втроки в символах
*/ */
std::size_t utf8_str_len(const char* str) std::size_t utf8_str_len(const char* str, bool needUnescape)
{ {
std::size_t len = 0; std::size_t len = 0;
while(*str) while(*str)
{ {
str += utf8_get_symbol_size(str); str += utf8_get_symbol_size(str, needUnescape);
++len; ++len;
} }
@ -272,12 +274,13 @@ std::size_t ucs4_get_utf8_str_bytes(const char32_t* str)
/** /**
* Возвращает размер символа UTF-8 строки с учетом экранированных UCS-2 и UCS-4 символов * Возвращает размер символа UTF-8 строки с учетом экранированных UCS-2 и UCS-4 символов
* @param str Указатель на требуемый символ в UTF-8 строке * @param str Указатель на требуемый символ в UTF-8 строке
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @return Количество байт, занимаемых символом * @return Количество байт, занимаемых символом
*/ */
std::size_t utf8_get_symbol_size(const char* str) std::size_t utf8_get_symbol_size(const char* str, bool needUnescape)
{ {
uint32_t s = 0; uint32_t s = 0;
if(*str == '\\') // UCS unescape if(needUnescape && *str == '\\') // UCS unescape
{ {
if(*(str + 1) == 'u') if(*(str + 1) == 'u')
{ {

View File

@ -92,9 +92,9 @@ const unsigned char helloResultUcs4[] = { 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x
TEST(Convert, utf8_symbol_size) TEST(Convert, utf8_symbol_size)
{ {
ASSERT_EQ(1, utf8_get_symbol_size((const char*)&helloStrUtf8[0])); ASSERT_EQ(1, utf8_get_symbol_size((const char*)&helloStrUtf8[0], false));
ASSERT_EQ(2, utf8_get_symbol_size((const char*)&helloStrUtf8[15])); ASSERT_EQ(2, utf8_get_symbol_size((const char*)&helloStrUtf8[15], false));
ASSERT_EQ(4, utf8_get_symbol_size((const char*)&helloStrUtf8[41])); ASSERT_EQ(4, utf8_get_symbol_size((const char*)&helloStrUtf8[41], false));
} }
// //
@ -114,7 +114,7 @@ TEST(Convert, utf8_symbol_size_by_ucs4_symbol)
TEST(Convert, utf8_length) TEST(Convert, utf8_length)
{ {
ASSERT_EQ(helloStrLen, utf8_str_len((const char*)helloStrUtf8)); ASSERT_EQ(helloStrLen, utf8_str_len((const char*)helloStrUtf8, false));
} }
// //
@ -193,9 +193,9 @@ TEST(Convert, utf32_length)
// //
TEST(Convert, utf8_to_ucs4) TEST(Convert, utf8_to_ucs4)
{ {
std::size_t len = utf8_str_len((const char*)helloStrUtf8); std::size_t len = utf8_str_len((const char*)helloStrUtf8, false);
char32_t* data = new char32_t[len]; char32_t* data = new char32_t[len];
utf8_to_ucs4((const char*)helloStrUtf8, data, len); utf8_to_ucs4((const char*)helloStrUtf8, data, len, false);
EXPECT_EQ(0, memcmp(helloStrUcs4, data, len*sizeof(char32_t))); EXPECT_EQ(0, memcmp(helloStrUcs4, data, len*sizeof(char32_t)));
delete[] data; delete[] data;
} }
@ -274,7 +274,7 @@ TEST(Convert, ucs4_to_utf16be)
TEST(ConvertError, utf8_length_error) TEST(ConvertError, utf8_length_error)
{ {
EXPECT_THROW(utf8_str_len((const char*)badStr2Utf8), bad_conversion); EXPECT_THROW(utf8_str_len((const char*)badStr2Utf8, false), bad_conversion);
} }
// //
@ -283,9 +283,9 @@ TEST(ConvertError, utf8_length_error)
TEST(ConvertError, utf8_decode_error) TEST(ConvertError, utf8_decode_error)
{ {
std::size_t len = utf8_str_len((const char*)badStrUtf8); std::size_t len = utf8_str_len((const char*)badStrUtf8, false);
char32_t* data = new char32_t[len]; char32_t* data = new char32_t[len];
EXPECT_THROW(utf8_to_ucs4((const char*)badStrUtf8, data, len), bad_conversion); EXPECT_THROW(utf8_to_ucs4((const char*)badStrUtf8, data, len, false), bad_conversion);
delete[] data; delete[] data;
} }
@ -335,7 +335,15 @@ TEST(ConvertError, ucs4_wrong_symbol)
TEST(Ustring, utf8_constructor) TEST(Ustring, utf8_constructor)
{ {
ustring str((const char*)helloStrUtf8); ustring str((const char*)helloStrUtf8);
ustring str2("Hello\\u0020world!", true);
ustring str3("Hello\\u0020world!", false);
ustring str4 = "Hello\\u0020world!";
EXPECT_EQ(0, memcmp(helloStrUcs4, str.utf32_str(), sizeof(helloStrUcs4))); EXPECT_EQ(0, memcmp(helloStrUcs4, str.utf32_str(), sizeof(helloStrUcs4)));
EXPECT_TRUE(str2 == "Hello world!");
EXPECT_TRUE(str3 == "Hello\\u0020world!");
EXPECT_TRUE(str4 == "Hello\\u0020world!");
} }
// //

4
todo
View File

@ -1,9 +1,9 @@
Задачи на будущее Задачи на будущее
1. Сделать декодирование экранированных UCS-2 (\uXXXX) и UCS-4 (\UXXXXXXXX) символов опциональным (по умолчанию выключено), добавить соответствующий тестю 1. Сделать декодирование экранированных UCS-2 (\uXXXX) и UCS-4 (\UXXXXXXXX) символов опциональным (по умолчанию выключено), добавить соответствующий тест.
2. В функции ucs4_to_utf8 добавить опциональное экранирование не-ANSI символов (по умолчанию выключено), добавить тесты. 2. В функции ucs4_to_utf8 добавить опциональное экранирование не-ANSI символов (по умолчанию выключено), добавить тесты.
3. Тестирование на android. 3. Тестирование на android. Можно сделать в QEMU виртуальную машину с ARM процессором.
4. Сравнение по производительности с std::string, профилирование, оптимизация (возможно переписывание узких мест на ассемблере с использованием SIMD инструкций) 4. Сравнение по производительности с std::string, профилирование, оптимизация (возможно переписывание узких мест на ассемблере с использованием SIMD инструкций)