Сделал декодирование экранированных 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:
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_utf32_str(const char32_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
@ -38,10 +38,10 @@ public:
ustring();
ustring(const 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 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);
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);
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 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 utf8_str_len(const char* str, bool needUnescape);
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_get_symbol_size(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
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 строки
* @param str Исходная строка в UTF-8 (null terminated)
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @detailed Так как символы ASCII являются подмножеством UTF-8, при создании строки
* из массива 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
* @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);
_len = utf8_str_len(strData);
_len = utf8_str_len(strData, needUnescape);
_capacity = _len;
_pData = new char32_t[_len + 1];
utf8_to_ucs4(strData, _pData, _len);
utf8_to_ucs4(strData, _pData, _len, needUnescape);
_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 dst Указатель на буфер для UCS-4 строки
* @param symbols Количество символов в строке
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @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)
void utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols, bool needUnescape)
{
std::size_t bytes = 0;
uint32_t s = 0;
for(std::size_t i = 0; i < symbols; ++i)
{
bytes = utf8_get_symbol_size(src);
bytes = utf8_get_symbol_size(src, needUnescape);
s = 0;
if(bytes == 1) // ASCII
@ -236,15 +237,16 @@ void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols)
/**
* Возвращает длину UTF-8 строки
* @param str UTF-8 строка
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @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;
while(*str)
{
str += utf8_get_symbol_size(str);
str += utf8_get_symbol_size(str, needUnescape);
++len;
}
@ -272,13 +274,14 @@ std::size_t ucs4_get_utf8_str_bytes(const char32_t* str)
/**
* Возвращает размер символа UTF-8 строки с учетом экранированных UCS-2 и UCS-4 символов
* @param str Указатель на требуемый символ в UTF-8 строке
* @param needUnescape Определяет нужно ли декодировать экранированные UTF-8 символы (\uXXXX и \UXXXXXXXX)
* @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;
if(*str == '\\') // UCS unescape
{
if(needUnescape && *str == '\\') // UCS unescape
{
if(*(str + 1) == 'u')
{
return 6;

View File

@ -92,9 +92,9 @@ const unsigned char helloResultUcs4[] = { 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x
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]));
ASSERT_EQ(1, utf8_get_symbol_size((const char*)&helloStrUtf8[0], false));
ASSERT_EQ(2, utf8_get_symbol_size((const char*)&helloStrUtf8[15], false));
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)
{
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)
{
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];
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)));
delete[] data;
}
@ -274,7 +274,7 @@ TEST(Convert, ucs4_to_utf16be)
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)
{
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];
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;
}
@ -335,7 +335,15 @@ TEST(ConvertError, ucs4_wrong_symbol)
TEST(Ustring, utf8_constructor)
{
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_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 символов (по умолчанию выключено), добавить тесты.
3. Тестирование на android.
3. Тестирование на android. Можно сделать в QEMU виртуальную машину с ARM процессором.
4. Сравнение по производительности с std::string, профилирование, оптимизация (возможно переписывание узких мест на ассемблере с использованием SIMD инструкций)