Skip to content

Commit 355d908

Browse files
Added utf-8 text stamp
1 parent d0c5a01 commit 355d908

File tree

3 files changed

+179
-3
lines changed

3 files changed

+179
-3
lines changed

include/blobstamper/stamp_text.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ class StampStringASCII: public StampBaseStr
4040
std::string ExtractStr(std::shared_ptr<Blob> blob) override;
4141
};
4242

43+
class StampStringUTF8: public StampBaseStr
44+
{
45+
public:
46+
virtual int minSize() override { return 3; }
47+
virtual int maxSize() override { return -1; }
48+
std::string ExtractStr(std::shared_ptr<Blob> blob) override;
49+
};
50+
4351
template<class T> class StampText: public GalleyVectorStrStampBase<T>
4452
{
4553
public:

src/stamp_text.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,139 @@ StampStringASCII::ExtractStr(std::shared_ptr<Blob> blob)
5454
return res;
5555
}
5656

57+
// FIXME: translate into English
58+
59+
/* Эта функция "нормализует" три байта до utf-32 символа. */
60+
61+
/* В utf-32 для кодировки одного символа используется три байта, четвертый всегда не используется */
62+
/* Первый и второй байт при этом используется полностью, а третий принимает значение от 0x00 до 0x10 */
63+
64+
/*
65+
* Нам надо нормолизовать первые три байта так, чтобы нормализованные значения были распределены равномерно
66+
* если нормализованные данные были распределены равномерно, или блико к тому. Проблема в том, как равномерно
67+
* распределить третий байт. Для значений 0x00..0x0F можно добиться равномерного распредеоения обнулив старшие
68+
* 4 бита. Тогда вероятность каждого из значений бует 1/16
69+
* Но у нас еще есть 0x10 который нельзя получить откидыванием бит, но который нам тоже нужен. При этом вероятонстью
70+
* равной или схожей с 0x00..0x0F.
71+
* Поэтому мы если старшие четыре бита принимают значение 0xF, то мы будем считать, что результат у нас 0x10. Вероятнсть этого 1/16
72+
* Во всех остальных случаях будем возвращать значение младших бит. За счет вышеуказанного кейса вероятность каждого значения станет 1/16-1/256
73+
* 1/256 -- это тот случай когда старшие четыре бита приняли значение 0xF
74+
* Будем считать это распредеделение вероятностей достаточно равномерным для наших задач
75+
*/
76+
77+
/*inline*/ uint32_t
78+
ThreeBytesToUtf32UInt(char *buf)
79+
{
80+
uint32_t res = 0;
81+
char * res_buf = (char *) &res;
82+
res_buf[3] = 0; // Старший байт всегда ноль
83+
res_buf[1] = buf[1];
84+
res_buf[0] = buf[0];
85+
86+
if ( (buf[2] & 0xF0) == 0xF0) // вероятность этого 1/16
87+
{
88+
res_buf[2] = 0x10;
89+
} else
90+
{
91+
res_buf[2] = buf[2] & 0x0F; // Значения от 0 до 0x0F, вероятность каждого из них 1/16 - 1/256 (с учетом предыдущей ветки if'а)
92+
}
93+
if ( (res_buf[2] == 0) && ((res_buf[1] & 0xF8) == 0xD8)) // 0x0000FyXX y=8..F, X=0..F
94+
{
95+
// Это суррогатные UTF-16 символы нулевого плейна которые в одиночку встречаться не должны. https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates
96+
// Заменяем их на подчеркивание
97+
res_buf[1] = 0;
98+
res_buf[0] = '_';
99+
}
100+
return res;
101+
}
102+
103+
size_t
104+
append_with_utf8(unsigned char * buf, uint32_t c32)
105+
{
106+
// check if sum in ascii diapazone
107+
if(c32 <= 0x7F)
108+
{
109+
buf[0] = c32 & 0x7F;
110+
return 1;
111+
}
112+
113+
// if contains 2 bytes
114+
if(c32 <= 0x7FF)
115+
{
116+
// get 1st byte
117+
buf[0] = 6 << 5;
118+
buf[0] += c32 >> 6;
119+
// get 2nd byte
120+
buf[1] = 2 << 6;
121+
buf[1] += c32 & 0x3f;
122+
return 2;
123+
}
124+
125+
// if contains 3 bytes
126+
if(c32 <= 0xffff)
127+
{
128+
buf[0] = 0xe << 4;
129+
buf[0] += c32 >> 0xc;
130+
131+
buf[1] = 2 << 6;
132+
buf[1] += (c32 >> 6) & 0x3f;
133+
134+
buf[2] = 2 << 6;
135+
buf[2] += c32 & 0x3f;
136+
return 3;
137+
}
138+
139+
// if contains 4 bytes
140+
if(c32 <= 0x10ffff)
141+
{
142+
buf[0] = 0x1e << 3;
143+
buf[0] += (c32 >> 0x12) & 7;
144+
145+
buf[1] = 2 << 6;
146+
buf[1] += (c32 >> 0xc) & 0x3f;
147+
148+
buf[2] = 2 << 6;
149+
buf[2] += (c32 >> 6) & 0x3f;
150+
151+
buf[3] = 2 << 6;
152+
buf[3] += c32 & 0x3f;
153+
return 4;
154+
155+
} else {
156+
/* FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME */
157+
printf("error in parsing utf32, maybe its not utf32?");
158+
return 0;
159+
}
160+
}
161+
162+
void
163+
utf24_to_utf8(char * in, size_t in_size, char **out, size_t *out_size)
164+
{
165+
in_size = (in_size / 3) * 3;
166+
*out_size = (in_size / 3) * 4 + 1; // +1 for final zero
167+
*out = (char *) malloc(*out_size);
168+
169+
unsigned char * p = (unsigned char*) *out;
170+
for(int i=0; i<in_size; i+=3)
171+
{
172+
uint32_t utf32 = ThreeBytesToUtf32UInt(in + i);
173+
p = p + append_with_utf8(p, utf32);
174+
}
175+
*p = '\0'; // final zero
176+
}
177+
178+
std::string
179+
StampStringUTF8::ExtractStr(std::shared_ptr<Blob> blob)
180+
{
181+
std::vector<char> data = blob->Chop(minSize(), maxSize())->AsByteVector();
182+
183+
char * raw_res;
184+
size_t raw_res_size;
185+
186+
utf24_to_utf8(&data[0], data.size(), &raw_res, &raw_res_size);
187+
188+
std::string res = raw_res;
189+
free(raw_res);
190+
191+
return res;
192+
}

t/150-stamp_text.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ using namespace TAP;
3131
int
3232
main()
3333
{
34-
TEST_START(5);
34+
TEST_START(7);
3535
{ /* 1..1 */
3636
char data[] = "папа\0мама\0бабушка\0дедушка\0братик\0сестричка";
3737
std::shared_ptr<Blob> blob = std::make_shared<Blob>(data, (sizeof data)-1);
@@ -51,28 +51,60 @@ main()
5151
}
5252

5353
{ /* 3..3 */
54+
unsigned char data[] = {
55+
0x41, 0x00, 0x00, 0xB2, 0x03, 0x00, 0x14, 0x04, 0x00, 0x9C, 0x4E, 0x00, 0xC4, 0x30, 0x00, 0x36, 0xF4, 0x01, 0x23, 0x0E, 0x00, 0x0F, 0x00, 0x2C,
56+
0x03, 0x26, 0x00, 0xA9, 0xF4, 0x01, 0x19, 0x4E, 0x00, 0x1E, 0xF6, 0x01, 0x95, 0x0D, 0x00, 0xA5, 0x07, 0x00, 0x7D, 0x0F, 0x00, 0xD0, 0x05, 0x00,
57+
0x13, 0xF9, 0x01, 0xA9, 0x03, 0x00, 0x66, 0x5B, 0x00, 0x0A, 0x0E, 0x00, 0xE3, 0x53, 0x00, 0x30, 0x2B, 0x00, 0xB8, 0x30, 0x01, 0x7C, 0xF4, 0x01,
58+
0xA2, 0xF3, 0x01, 0x35, 0x0C, 0x00, 0x0D, 0x4E, 0x00, 0xB0, 0xF9, 0x01, 0xE6, 0x0C, 0x00, 0x8E, 0xF4, 0x01, 0xA9, 0x06, 0x00, 0xC7, 0xF4, 0x01,
59+
0x5C, 0x0B, 0x00, 0x4B, 0x4E, 0x00, 0xD2, 0x05, 0x00, 0x13, 0xF9, 0x01, 0x28, 0x0F, 0x00, 0xF4, 0xF3, 0x01, 0x95, 0x0B, 0x00, 0x80, 0x0D, 0x00,
60+
0x3E, 0xF3, 0x01, 0x7B,
61+
};
62+
std::shared_ptr<Blob> blob = std::make_shared<Blob>((char *) data, (sizeof data)-1);
63+
StampStringUTF8 stamp_str_ascii;
64+
std::string s = stamp_str_ascii.ExtractStr(blob);
65+
is(s, "AβД亜ツ🐶ร󀀏☃💩丙😞ඕޥཽא🤓Ω学ช口⬰𓂸👼🎢వ不🦰೦💎ک📇ଡ଼之ג🤓༨🏴க඀🌾",
66+
"StampTextUTF8");
67+
}
68+
69+
{ /* 4..4 */
5470
char data[] = "dad\0mam\0granddad\0grandmam\0brother\0sister";
5571
std::shared_ptr<Blob> blob = std::make_shared<Blob>(data, (sizeof data)-1);
5672
StampText<StampStringLatin1> stamp;
5773
std::string s = stamp.ExtractStr(blob);
5874
is(s, "d dad gra n dmam broth er siste", "StampText<StampStringLatin1>");
5975
}
6076

61-
{ /* 4..4 */
77+
{ /* 5..5 */
6278
char data[] = "abcdef" "abcdef" "ABCDEF" "012345";
6379
std::shared_ptr<Blob> blob = std::make_shared<Blob>(data, (sizeof data)-1);
6480
StampText<StampDictLCAlphaSmall> stamp;
6581
std::string s = stamp.ExtractStr(blob);
6682
is(s, "gleam godfather graffiti greened grouping gunshots gleam godfather graffiti greened grouping gunshots dismally dissented divested doorstep dread drunks convertors corpulent counterparts cranking crippled crusades", "StampText<StampDictLCAlphaSmall>");
6783
}
6884

69-
{ /* 5..5 */
85+
{ /* 6..6 */
7086
char data[] = "Некоторый текст написанный русскими буквами в кодировке utf-8";
7187
std::shared_ptr<Blob> blob = std::make_shared<Blob>(data, (sizeof data)-1);
7288
StampText<StampStringASCII> stamp;
7389
std::string s = stamp.ExtractStr(blob);
7490
is(s, "P9 Q Q\x3Q \x1Q \x1P: P8 P<P 8 P 1Q\x3 P:P 2P0 P<P 8 P 2 P: P>P 4P8 Q P>P 2P :P5 ut f-8", "StampText<StampStringASCII>");
7591
}
7692

93+
{ /* 7..7 */
94+
unsigned char data[] = {
95+
0x41, 0x00, 0x00, 0xB2, 0x03, 0x00, 0x14, 0x04, 0x00, 0x9C, 0x4E, 0x00, 0xC4, 0x30, 0x00, 0x36, 0xF4, 0x01, 0x23, 0x0E, 0x00, 0x0F, 0x00, 0x2C,
96+
0x03, 0x26, 0x00, 0xA9, 0xF4, 0x01, 0x19, 0x4E, 0x00, 0x1E, 0xF6, 0x01, 0x95, 0x0D, 0x00, 0xA5, 0x07, 0x00, 0x7D, 0x0F, 0x00, 0xD0, 0x05, 0x00,
97+
0x13, 0xF9, 0x01, 0xA9, 0x03, 0x00, 0x66, 0x5B, 0x00, 0x0A, 0x0E, 0x00, 0xE3, 0x53, 0x00, 0x30, 0x2B, 0x00, 0xB8, 0x30, 0x01, 0x7C, 0xF4, 0x01,
98+
0xA2, 0xF3, 0x01, 0x35, 0x0C, 0x00, 0x0D, 0x4E, 0x00, 0xB0, 0xF9, 0x01, 0xE6, 0x0C, 0x00, 0x8E, 0xF4, 0x01, 0xA9, 0x06, 0x00, 0xC7, 0xF4, 0x01,
99+
0x5C, 0x0B, 0x00, 0x4B, 0x4E, 0x00, 0xD2, 0x05, 0x00, 0x13, 0xF9, 0x01, 0x28, 0x0F, 0x00, 0xF4, 0xF3, 0x01, 0x95, 0x0B, 0x00, 0x80, 0x0D, 0x00,
100+
0x3E, 0xF3, 0x01, 0x7B,
101+
};
102+
std::shared_ptr<Blob> blob = std::make_shared<Blob>((char *)data, (sizeof data)-1);
103+
StampText<StampStringUTF8> stamp;
104+
std::string s = stamp.ExtractStr(blob);
105+
is(s, "񀀃󀀄񀁎񠀰𰇴󰀎𲰀򐀦򐇴󠁎񐇶񐀍󐀇𰀅򐇹񠀃򠁛𰀎S򀀫󀄰𠇴񐇳󐀌N񠇹󠀌򐇴񰀆󀇴򰀋𠁎𰀅򀇹􀀏񐇳 󠀍",
106+
"StampText<StampStringUTF8>");
107+
}
108+
77109
TEST_END;
78110
}

0 commit comments

Comments
 (0)