utf8.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #pragma once
  2. #include "recode_result.h"
  3. #include <util/generic/strbuf.h>
  4. #include <util/generic/string.h>
  5. #include <util/generic/yexception.h>
  6. #include <util/system/defaults.h>
  7. #include <util/system/yassert.h>
  8. extern const wchar32 BROKEN_RUNE;
  9. inline unsigned char UTF8LeadByteMask(size_t utf8_rune_len) {
  10. // Y_ASSERT (utf8_rune_len <= 4);
  11. return "\0\0\037\017\007"[utf8_rune_len];
  12. }
  13. inline size_t UTF8RuneLen(const unsigned char lead_byte) {
  14. //b0XXXXXXX
  15. if ((lead_byte & 0x80) == 0x00) {
  16. return 1;
  17. }
  18. //b110XXXXX
  19. if ((lead_byte & 0xe0) == 0xc0) {
  20. return 2;
  21. }
  22. //b1110XXXX
  23. if ((lead_byte & 0xf0) == 0xe0) {
  24. return 3;
  25. }
  26. //b11110XXX
  27. if ((lead_byte & 0xf8) == 0xf0) {
  28. return 4;
  29. }
  30. //b10XXXXXX
  31. return 0;
  32. }
  33. inline size_t UTF8RuneLenByUCS(wchar32 rune) {
  34. if (rune < 0x80)
  35. return 1U;
  36. else if (rune < 0x800)
  37. return 2U;
  38. else if (rune < 0x10000)
  39. return 3U;
  40. else if (rune < 0x200000)
  41. return 4U;
  42. else if (rune < 0x4000000)
  43. return 5U;
  44. else
  45. return 6U;
  46. }
  47. inline void PutUTF8LeadBits(wchar32& rune, unsigned char c, size_t len) {
  48. rune = c;
  49. rune &= UTF8LeadByteMask(len);
  50. }
  51. inline void PutUTF8SixBits(wchar32& rune, unsigned char c) {
  52. rune <<= 6;
  53. rune |= c & 0x3F;
  54. }
  55. inline bool IsUTF8ContinuationByte(unsigned char c) {
  56. return (c & static_cast<unsigned char>(0xC0)) == static_cast<unsigned char>(0x80);
  57. }
  58. //! returns length of the current UTF8 character
  59. //! @param n length of the current character, it is assigned in case of valid UTF8 byte sequence
  60. //! @param p pointer to the current character
  61. //! @param e end of the character sequence
  62. inline RECODE_RESULT GetUTF8CharLen(size_t& n, const unsigned char* p, const unsigned char* e) {
  63. Y_ASSERT(p < e); // since p < e then we will check RECODE_EOINPUT only for n > 1 (see calls of this functions)
  64. switch (UTF8RuneLen(*p)) {
  65. case 0:
  66. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in first byte
  67. case 1:
  68. n = 1;
  69. return RECODE_OK;
  70. case 2:
  71. if (p + 2 > e) {
  72. return RECODE_EOINPUT;
  73. } else if (!IsUTF8ContinuationByte(p[1])) {
  74. return RECODE_BROKENSYMBOL;
  75. } else {
  76. n = 2;
  77. return RECODE_OK;
  78. }
  79. case 3:
  80. if (p + 3 > e) {
  81. return RECODE_EOINPUT;
  82. } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2])) {
  83. return RECODE_BROKENSYMBOL;
  84. } else {
  85. n = 3;
  86. return RECODE_OK;
  87. }
  88. default: // actually 4
  89. if (p + 4 > e) {
  90. return RECODE_EOINPUT;
  91. } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2]) || !IsUTF8ContinuationByte(p[3])) {
  92. return RECODE_BROKENSYMBOL;
  93. } else {
  94. n = 4;
  95. return RECODE_OK;
  96. }
  97. }
  98. }
  99. //! returns number of characters in UTF8 encoded text, stops immediately if UTF8 byte sequence is wrong
  100. //! @param text UTF8 encoded text
  101. //! @param len the length of the text in bytes
  102. //! @param number number of encoded symbols in the text
  103. inline bool GetNumberOfUTF8Chars(const char* text, size_t len, size_t& number) {
  104. const unsigned char* cur = reinterpret_cast<const unsigned char*>(text);
  105. const unsigned char* const last = cur + len;
  106. number = 0;
  107. size_t runeLen;
  108. bool res = true;
  109. while (cur != last) {
  110. if (GetUTF8CharLen(runeLen, cur, last) != RECODE_OK) { // actually it could be RECODE_BROKENSYMBOL only
  111. res = false;
  112. break;
  113. }
  114. cur += runeLen;
  115. Y_ASSERT(cur <= last);
  116. ++number;
  117. }
  118. return res;
  119. }
  120. inline size_t GetNumberOfUTF8Chars(TStringBuf text) {
  121. size_t number;
  122. if (!GetNumberOfUTF8Chars(text.data(), text.size(), number)) {
  123. ythrow yexception() << "GetNumberOfUTF8Chars failed on invalid utf-8 " << TString(text.substr(0, 50)).Quote();
  124. }
  125. return number;
  126. }
  127. //! reads one unicode symbol from a character sequence encoded UTF8 and checks for overlong encoding
  128. //! @param rune value of the current character
  129. //! @param rune_len length of the UTF8 bytes sequence that has been read
  130. //! @param s pointer to the current character
  131. //! @param end the end of the character sequence
  132. inline RECODE_RESULT SafeReadUTF8Char(wchar32& rune, size_t& rune_len, const unsigned char* s, const unsigned char* end) {
  133. rune = BROKEN_RUNE;
  134. rune_len = 0;
  135. wchar32 _rune;
  136. size_t _len = UTF8RuneLen(*s);
  137. if (s + _len > end)
  138. return RECODE_EOINPUT; //[EOINPUT]
  139. if (_len == 0)
  140. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in first byte
  141. _rune = *s++; //[00000000 0XXXXXXX]
  142. if (_len > 1) {
  143. _rune &= UTF8LeadByteMask(_len);
  144. unsigned char ch = *s++;
  145. if (!IsUTF8ContinuationByte(ch))
  146. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in second byte
  147. PutUTF8SixBits(_rune, ch); //[00000XXX XXYYYYYY]
  148. if (_len > 2) {
  149. ch = *s++;
  150. if (!IsUTF8ContinuationByte(ch))
  151. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in third byte
  152. PutUTF8SixBits(_rune, ch); //[XXXXYYYY YYZZZZZZ]
  153. if (_len > 3) {
  154. ch = *s;
  155. if (!IsUTF8ContinuationByte(ch))
  156. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in fourth byte
  157. PutUTF8SixBits(_rune, ch); //[XXXYY YYYYZZZZ ZZQQQQQQ]
  158. if (_rune > 0x10FFFF) // it is not a valid Unicode code point
  159. return RECODE_BROKENSYMBOL;
  160. if (_rune < 0x10000) // check for overlong encoding
  161. return RECODE_BROKENSYMBOL;
  162. } else {
  163. if (_rune < 0x800) // check for overlong encoding
  164. return RECODE_BROKENSYMBOL;
  165. }
  166. } else {
  167. if (_rune < 0x80) // check for overlong encoding
  168. return RECODE_BROKENSYMBOL;
  169. }
  170. }
  171. rune_len = _len;
  172. rune = _rune;
  173. return RECODE_OK;
  174. }
  175. //! reads one unicode symbol from a character sequence encoded UTF8 and moves pointer to the next character
  176. //! @param c value of the current character
  177. //! @param p pointer to the current character, it will be changed in case of valid UTF8 byte sequence
  178. //! @param e the end of the character sequence
  179. Y_FORCE_INLINE RECODE_RESULT ReadUTF8CharAndAdvance(wchar32& rune, const unsigned char*& p, const unsigned char* e) noexcept {
  180. Y_ASSERT(p < e); // since p < e then we will check RECODE_EOINPUT only for n > 1 (see calls of this functions)
  181. switch (UTF8RuneLen(*p)) {
  182. case 0:
  183. rune = BROKEN_RUNE;
  184. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in first byte
  185. case 1:
  186. rune = *p; //[00000000 0XXXXXXX]
  187. ++p;
  188. return RECODE_OK;
  189. case 2:
  190. if (p + 2 > e) {
  191. return RECODE_EOINPUT;
  192. } else if (!IsUTF8ContinuationByte(p[1])) {
  193. rune = BROKEN_RUNE;
  194. return RECODE_BROKENSYMBOL;
  195. } else {
  196. PutUTF8LeadBits(rune, *p++, 2); //[00000000 000XXXXX]
  197. PutUTF8SixBits(rune, *p++); //[00000XXX XXYYYYYY]
  198. if (Y_UNLIKELY(rune < 0x80)) { // overlong encoding
  199. p -= 2;
  200. rune = BROKEN_RUNE;
  201. return RECODE_BROKENSYMBOL;
  202. }
  203. return RECODE_OK;
  204. }
  205. case 3:
  206. if (p + 3 > e) {
  207. return RECODE_EOINPUT;
  208. } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2])) {
  209. rune = BROKEN_RUNE;
  210. return RECODE_BROKENSYMBOL;
  211. } else {
  212. PutUTF8LeadBits(rune, *p++, 3); //[00000000 0000XXXX]
  213. PutUTF8SixBits(rune, *p++); //[000000XX XXYYYYYY]
  214. PutUTF8SixBits(rune, *p++); //[XXXXYYYY YYZZZZZZ]
  215. if (Y_UNLIKELY(rune < 0x800)) { // overlong encoding
  216. p -= 3;
  217. rune = BROKEN_RUNE;
  218. return RECODE_BROKENSYMBOL;
  219. }
  220. return RECODE_OK;
  221. }
  222. case 4:
  223. if (p + 4 > e) {
  224. return RECODE_EOINPUT;
  225. } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2]) || !IsUTF8ContinuationByte(p[3])) {
  226. rune = BROKEN_RUNE;
  227. return RECODE_BROKENSYMBOL;
  228. } else {
  229. PutUTF8LeadBits(rune, *p++, 4); //[00000000 00000000 00000XXX]
  230. PutUTF8SixBits(rune, *p++); //[00000000 0000000X XXYYYYYY]
  231. PutUTF8SixBits(rune, *p++); //[00000000 0XXXYYYY YYZZZZZZ]
  232. PutUTF8SixBits(rune, *p++); //[000XXXYY YYYYZZZZ ZZQQQQQQ]
  233. if (Y_UNLIKELY(rune < 0x10000 || rune > 0x10FFFF)) { // overlong encoding or non-valid code point
  234. p -= 4;
  235. rune = BROKEN_RUNE;
  236. return RECODE_BROKENSYMBOL;
  237. }
  238. return RECODE_OK;
  239. }
  240. default: // >4
  241. rune = BROKEN_RUNE;
  242. return RECODE_BROKENSYMBOL;
  243. }
  244. }
  245. //! writes one unicode symbol into a character sequence encoded UTF8
  246. //! checks for end of the buffer and returns the result of encoding
  247. //! @param rune value of the current character
  248. //! @param rune_len length of the UTF8 byte sequence that has been written
  249. //! @param s pointer to the output buffer
  250. //! @param tail available size of the buffer
  251. inline RECODE_RESULT SafeWriteUTF8Char(wchar32 rune, size_t& rune_len, unsigned char* s, size_t tail) {
  252. rune_len = 0;
  253. if (rune < 0x80) {
  254. if (tail <= 0)
  255. return RECODE_EOOUTPUT;
  256. *s = static_cast<unsigned char>(rune);
  257. rune_len = 1;
  258. return RECODE_OK;
  259. }
  260. if (rune < 0x800) {
  261. if (tail <= 1)
  262. return RECODE_EOOUTPUT;
  263. *s++ = static_cast<unsigned char>(0xC0 | (rune >> 6));
  264. *s = static_cast<unsigned char>(0x80 | (rune & 0x3F));
  265. rune_len = 2;
  266. return RECODE_OK;
  267. }
  268. if (rune < 0x10000) {
  269. if (tail <= 2)
  270. return RECODE_EOOUTPUT;
  271. *s++ = static_cast<unsigned char>(0xE0 | (rune >> 12));
  272. *s++ = static_cast<unsigned char>(0x80 | ((rune >> 6) & 0x3F));
  273. *s = static_cast<unsigned char>(0x80 | (rune & 0x3F));
  274. rune_len = 3;
  275. return RECODE_OK;
  276. }
  277. /*if (rune < 0x200000)*/ {
  278. if (tail <= 3)
  279. return RECODE_EOOUTPUT;
  280. *s++ = static_cast<unsigned char>(0xF0 | ((rune >> 18) & 0x07));
  281. *s++ = static_cast<unsigned char>(0x80 | ((rune >> 12) & 0x3F));
  282. *s++ = static_cast<unsigned char>(0x80 | ((rune >> 6) & 0x3F));
  283. *s = static_cast<unsigned char>(0x80 | (rune & 0x3F));
  284. rune_len = 4;
  285. return RECODE_OK;
  286. }
  287. }
  288. inline RECODE_RESULT SafeWriteUTF8Char(wchar32 rune, size_t& rune_len, unsigned char* s, const unsigned char* end) {
  289. return SafeWriteUTF8Char(rune, rune_len, s, end - s);
  290. }
  291. //! writes one unicode symbol into a character sequence encoded UTF8
  292. //! @attention this function works as @c SafeWriteUTF8Char it does not check
  293. //! the size of the output buffer, it supposes that buffer is long enough
  294. //! @param rune value of the current character
  295. //! @param rune_len length of the UTF8 byte sequence that has been written
  296. //! @param s pointer to the output buffer
  297. inline void WriteUTF8Char(wchar32 rune, size_t& rune_len, unsigned char* s) {
  298. if (rune < 0x80) {
  299. *s = static_cast<unsigned char>(rune);
  300. rune_len = 1;
  301. return;
  302. }
  303. if (rune < 0x800) {
  304. *s++ = static_cast<unsigned char>(0xC0 | (rune >> 6));
  305. *s = static_cast<unsigned char>(0x80 | (rune & 0x3F));
  306. rune_len = 2;
  307. return;
  308. }
  309. if (rune < 0x10000) {
  310. *s++ = static_cast<unsigned char>(0xE0 | (rune >> 12));
  311. *s++ = static_cast<unsigned char>(0x80 | ((rune >> 6) & 0x3F));
  312. *s = static_cast<unsigned char>(0x80 | (rune & 0x3F));
  313. rune_len = 3;
  314. return;
  315. }
  316. /*if (rune < 0x200000)*/ {
  317. *s++ = static_cast<unsigned char>(0xF0 | ((rune >> 18) & 0x07));
  318. *s++ = static_cast<unsigned char>(0x80 | ((rune >> 12) & 0x3F));
  319. *s++ = static_cast<unsigned char>(0x80 | ((rune >> 6) & 0x3F));
  320. *s = static_cast<unsigned char>(0x80 | (rune & 0x3F));
  321. rune_len = 4;
  322. }
  323. }
  324. TStringBuf SubstrUTF8(const TStringBuf str, size_t pos, size_t len);
  325. enum EUTF8Detect {
  326. NotUTF8,
  327. UTF8,
  328. ASCII
  329. };
  330. EUTF8Detect UTF8Detect(const char* s, size_t len);
  331. inline EUTF8Detect UTF8Detect(const TStringBuf input) {
  332. return UTF8Detect(input.data(), input.size());
  333. }
  334. inline bool IsUtf(const char* input, size_t len) {
  335. return UTF8Detect(input, len) != NotUTF8;
  336. }
  337. inline bool IsUtf(const TStringBuf input) {
  338. return IsUtf(input.data(), input.size());
  339. }
  340. //! returns true, if result is not the same as input, and put it in newString
  341. //! returns false, if result is unmodified
  342. bool ToLowerUTF8Impl(const char* beg, size_t n, TString& newString);
  343. TString ToLowerUTF8(const TString& s);
  344. TString ToLowerUTF8(TStringBuf s);
  345. TString ToLowerUTF8(const char* s);
  346. inline TString ToLowerUTF8(const std::string& s) {
  347. return ToLowerUTF8(TStringBuf(s));
  348. }
  349. //! returns true, if result is not the same as input, and put it in newString
  350. //! returns false, if result is unmodified
  351. bool ToUpperUTF8Impl(const char* beg, size_t n, TString& newString);
  352. TString ToUpperUTF8(const TString& s);
  353. TString ToUpperUTF8(TStringBuf s);
  354. TString ToUpperUTF8(const char* s);