Branch data Line data Source code
1 : : /*
2 : : Basic UTF-8 manipulation routines
3 : : by Jeff Bezanson
4 : : placed in the public domain Fall 2005
5 : :
6 : : This code is designed to provide the utilities you need to manipulate
7 : : UTF-8 as an internal string encoding. These functions do not perform the
8 : : error checking normally needed when handling UTF-8 data, so if you happen
9 : : to be from the Unicode Consortium you will want to flay me alive.
10 : : I do this because error checking can be performed at the boundaries (I/O),
11 : : with these routines reserved for higher performance on data known to be
12 : : valid.
13 : : A UTF-8 validation routine is included.
14 : : */
15 : : #include <stdlib.h>
16 : : #include <stdio.h>
17 : : #include <string.h>
18 : : #include <stdarg.h>
19 : : #include <stdint.h>
20 : : #include <wchar.h>
21 : : #include <wctype.h>
22 : :
23 : : #include "utf8proc.h"
24 : : #undef JL_DLLEXPORT /* avoid conflicting definition */
25 : :
26 : : #include "dtypes.h"
27 : :
28 : : #ifdef _OS_WINDOWS_
29 : : #include <malloc.h>
30 : : #define snprintf _snprintf
31 : : #else
32 : : #ifndef __FreeBSD__
33 : : #include <alloca.h>
34 : : #endif /* __FreeBSD__ */
35 : : #endif
36 : : #include <assert.h>
37 : :
38 : : #include "utf8.h"
39 : :
40 : : #ifdef __cplusplus
41 : : extern "C" {
42 : : #endif
43 : :
44 : : static const uint32_t offsetsFromUTF8[6] = {
45 : : 0x00000000UL, 0x00003080UL, 0x000E2080UL,
46 : : 0x03C82080UL, 0xFA082080UL, 0x82082080UL
47 : : };
48 : :
49 : : static const char trailingBytesForUTF8[256] = {
50 : : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
51 : : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
52 : : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
53 : : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
54 : : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
55 : : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
56 : : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
57 : : 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
58 : : };
59 : :
60 : : /* returns length of next utf-8 sequence */
61 : 11020760 : size_t u8_seqlen(const char *s)
62 : : {
63 : 11020760 : return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1;
64 : : }
65 : :
66 : : /* returns the # of bytes needed to encode a certain character
67 : : 0 means the character cannot (or should not) be encoded. */
68 : 0 : size_t u8_charlen(uint32_t ch)
69 : : {
70 [ # # ]: 0 : if (ch < 0x80)
71 : 0 : return 1;
72 [ # # ]: 0 : else if (ch < 0x800)
73 : 0 : return 2;
74 [ # # ]: 0 : else if (ch < 0x10000)
75 : 0 : return 3;
76 [ # # ]: 0 : else if (ch < 0x110000)
77 : 0 : return 4;
78 : 0 : return 0;
79 : : }
80 : :
81 : : /* conversions without error checking
82 : : only works for valid UTF-8, i.e. no 5- or 6-byte sequences
83 : : srcsz = source size in bytes
84 : : sz = dest size in # of wide characters
85 : :
86 : : returns # characters converted
87 : : if sz == srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
88 : : */
89 : 0 : size_t u8_toucs(uint32_t *dest, size_t sz, const char *src, size_t srcsz)
90 : : {
91 : : uint32_t ch;
92 : 0 : const char *src_end = src + srcsz;
93 : : size_t nb;
94 : 0 : size_t i=0;
95 : :
96 [ # # # # ]: 0 : if (sz == 0 || srcsz == 0)
97 : 0 : return 0;
98 : :
99 [ # # ]: 0 : while (i < sz) {
100 [ # # ]: 0 : if (!isutf(*src)) { // invalid sequence
101 : 0 : dest[i++] = 0xFFFD;
102 : 0 : src++;
103 [ # # ]: 0 : if (src >= src_end) break;
104 : 0 : continue;
105 : : }
106 : 0 : nb = trailingBytesForUTF8[(unsigned char)*src];
107 [ # # ]: 0 : if (src + nb >= src_end)
108 : 0 : break;
109 : 0 : ch = 0;
110 [ # # # # : 0 : switch (nb) {
# # # ]
111 : : /* these fall through deliberately */
112 : 0 : case 5: ch += (unsigned char)*src++; ch <<= 6; JL_FALLTHROUGH;
113 : 0 : case 4: ch += (unsigned char)*src++; ch <<= 6; JL_FALLTHROUGH;
114 : 0 : case 3: ch += (unsigned char)*src++; ch <<= 6; JL_FALLTHROUGH;
115 : 0 : case 2: ch += (unsigned char)*src++; ch <<= 6; JL_FALLTHROUGH;
116 : 0 : case 1: ch += (unsigned char)*src++; ch <<= 6; JL_FALLTHROUGH;
117 : 0 : case 0: ch += (unsigned char)*src++;
118 : : }
119 : 0 : ch -= offsetsFromUTF8[nb];
120 : 0 : dest[i++] = ch;
121 : : }
122 : 0 : return i;
123 : : }
124 : :
125 : : /* srcsz = number of source characters
126 : : sz = size of dest buffer in bytes
127 : :
128 : : returns # bytes stored in dest
129 : : the destination string will never be bigger than the source string.
130 : : */
131 : 1642742 : size_t u8_toutf8(char *dest, size_t sz, const uint32_t *src, size_t srcsz)
132 : : {
133 : : uint32_t ch;
134 : 1642742 : size_t i = 0;
135 : 1642742 : char *dest0 = dest;
136 : 1642742 : char *dest_end = dest + sz;
137 : :
138 [ + + ]: 3285480 : while (i < srcsz) {
139 : 1642742 : ch = src[i];
140 [ + + ]: 1642742 : if (ch < 0x80) {
141 [ - + ]: 1580592 : if (dest >= dest_end)
142 : 0 : break;
143 : 1580592 : *dest++ = (char)ch;
144 : : }
145 [ + + ]: 62150 : else if (ch < 0x800) {
146 [ - + ]: 15856 : if (dest >= dest_end-1)
147 : 0 : break;
148 : 15856 : *dest++ = (ch>>6) | 0xC0;
149 : 15856 : *dest++ = (ch & 0x3F) | 0x80;
150 : : }
151 [ + + ]: 46294 : else if (ch < 0x10000) {
152 [ - + ]: 29806 : if (dest >= dest_end-2)
153 : 0 : break;
154 : 29806 : *dest++ = (ch>>12) | 0xE0;
155 : 29806 : *dest++ = ((ch>>6) & 0x3F) | 0x80;
156 : 29806 : *dest++ = (ch & 0x3F) | 0x80;
157 : : }
158 [ + - ]: 16488 : else if (ch < 0x110000) {
159 [ - + ]: 16488 : if (dest >= dest_end-3)
160 : 0 : break;
161 : 16488 : *dest++ = (ch>>18) | 0xF0;
162 : 16488 : *dest++ = ((ch>>12) & 0x3F) | 0x80;
163 : 16488 : *dest++ = ((ch>>6) & 0x3F) | 0x80;
164 : 16488 : *dest++ = (ch & 0x3F) | 0x80;
165 : : }
166 : : else {
167 [ # # ]: 0 : if (dest >= dest_end-2)
168 : 0 : break;
169 : : // invalid: use replacement char \ufffd
170 : 0 : *dest++ = (char)0xef;
171 : 0 : *dest++ = (char)0xbf;
172 : 0 : *dest++ = (char)0xbd;
173 : : }
174 : 1642742 : i++;
175 : : }
176 : 1642742 : return (dest-dest0);
177 : : }
178 : :
179 : 474 : size_t u8_wc_toutf8(char *dest, uint32_t ch)
180 : : {
181 [ - + ]: 474 : if (ch < 0x80) {
182 : 0 : dest[0] = (char)ch;
183 : 0 : return 1;
184 : : }
185 [ + + ]: 474 : if (ch < 0x800) {
186 : 32 : dest[0] = (ch>>6) | 0xC0;
187 : 32 : dest[1] = (ch & 0x3F) | 0x80;
188 : 32 : return 2;
189 : : }
190 [ + + ]: 442 : if (ch < 0x10000) {
191 : 406 : dest[0] = (ch>>12) | 0xE0;
192 : 406 : dest[1] = ((ch>>6) & 0x3F) | 0x80;
193 : 406 : dest[2] = (ch & 0x3F) | 0x80;
194 : 406 : return 3;
195 : : }
196 [ + - ]: 36 : if (ch < 0x110000) {
197 : 36 : dest[0] = (ch>>18) | 0xF0;
198 : 36 : dest[1] = ((ch>>12) & 0x3F) | 0x80;
199 : 36 : dest[2] = ((ch>>6) & 0x3F) | 0x80;
200 : 36 : dest[3] = (ch & 0x3F) | 0x80;
201 : 36 : return 4;
202 : : }
203 : 0 : dest[0] = (char)0xef;
204 : 0 : dest[1] = (char)0xbf;
205 : 0 : dest[2] = (char)0xbd;
206 : 0 : return 3;
207 : : }
208 : :
209 : : /* charnum => byte offset */
210 : 0 : size_t u8_offset(const char *s, size_t charnum)
211 : : {
212 : 0 : size_t i=0;
213 : :
214 [ # # ]: 0 : while (charnum > 0) {
215 [ # # ]: 0 : if (s[i++] & 0x80) {
216 [ # # # # : 0 : (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
# # ]
217 : : }
218 : 0 : charnum--;
219 : : }
220 : 0 : return i;
221 : : }
222 : :
223 : : /* byte offset => charnum */
224 : 48 : size_t u8_charnum(const char *s, size_t offset)
225 : : {
226 : 48 : size_t charnum = 0;
227 [ + - ]: 48 : if (offset) {
228 : : do {
229 : : // Simply not count continuation bytes
230 : : // Since we are not doing validation anyway, we can just
231 : : // assume this is a valid UTF-8 string
232 : 892 : charnum += ((*(unsigned char *)s++ & 0xc0) != 0x80);
233 [ + + ]: 892 : } while (--offset);
234 : : }
235 : 48 : return charnum;
236 : : }
237 : :
238 : 1770382 : size_t u8_strwidth(const char *s)
239 : : {
240 : : uint32_t ch;
241 : 1770382 : size_t nb, tot=0;
242 : : signed char sc;
243 : :
244 [ + + ]: 4199520 : while ((sc = (signed char)*s) != 0) {
245 [ + + ]: 2429140 : if (sc >= 0) {
246 : 2425620 : s++;
247 [ + - ]: 2425620 : if (sc) tot++;
248 : : }
249 : : else {
250 [ - + ]: 3526 : if (!isutf(sc)) { tot++; s++; continue; }
251 : 3526 : nb = trailingBytesForUTF8[(unsigned char)sc];
252 : 3526 : ch = 0;
253 [ - - - + : 3526 : switch (nb) {
+ - - ]
254 : : /* these fall through deliberately */
255 : 0 : case 5: ch += (unsigned char)*s++; ch <<= 6; JL_FALLTHROUGH;
256 : 0 : case 4: ch += (unsigned char)*s++; ch <<= 6; JL_FALLTHROUGH;
257 : 0 : case 3: ch += (unsigned char)*s++; ch <<= 6; JL_FALLTHROUGH;
258 : 3204 : case 2: ch += (unsigned char)*s++; ch <<= 6; JL_FALLTHROUGH;
259 : 3526 : case 1: ch += (unsigned char)*s++; ch <<= 6; JL_FALLTHROUGH;
260 : 3526 : case 0: ch += (unsigned char)*s++;
261 : : }
262 : 3526 : ch -= offsetsFromUTF8[nb];
263 : 3526 : tot += utf8proc_charwidth(ch);
264 : : }
265 : : }
266 : 1770382 : return tot;
267 : : }
268 : :
269 : : /* reads the next utf-8 sequence out of a string, updating an index */
270 : 10689000 : uint32_t u8_nextchar(const char *s, size_t *i)
271 : : {
272 : 10689000 : uint32_t ch = 0;
273 : : size_t sz, j;
274 : :
275 : 10689000 : sz = u8_seqlen(&s[*i]);
276 [ + + ]: 21707200 : for (j = sz; j > 0; j--) {
277 : 11018140 : ch <<= 6;
278 : 11018140 : ch += (unsigned char)s[(*i)++];
279 : : }
280 : 10689000 : ch -= offsetsFromUTF8[sz-1];
281 : :
282 : 10689000 : return ch;
283 : : }
284 : :
285 : : /* next character without NUL character terminator */
286 : 0 : uint32_t u8_nextmemchar(const char *s, size_t *i)
287 : : {
288 : 0 : return u8_nextchar(s,i);
289 : : }
290 : :
291 : 0 : void u8_inc(const char *s, size_t *i)
292 : : {
293 [ # # # # : 0 : (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) || isutf(s[++(*i)]) || ++(*i));
# # # # ]
294 : 0 : }
295 : :
296 : 0 : void u8_dec(const char *s, size_t *i)
297 : : {
298 [ # # # # : 0 : (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || isutf(s[--(*i)]) || --(*i));
# # # # ]
299 : 0 : }
300 : :
301 : 131400 : int octal_digit(char c)
302 : : {
303 [ + + + + ]: 131400 : return (c >= '0' && c <= '7');
304 : : }
305 : :
306 : 240978 : int hex_digit(char c)
307 : : {
308 [ + + + + ]: 240978 : return ((c >= '0' && c <= '9') ||
309 [ + + + + : 544134 : (c >= 'A' && c <= 'F') ||
+ + ]
310 [ + + ]: 62178 : (c >= 'a' && c <= 'f'));
311 : : }
312 : :
313 : 50518 : char read_escape_control_char(char c)
314 : : {
315 [ + + ]: 50518 : if (c == 'n')
316 : 2220 : return '\n';
317 [ + + ]: 48298 : else if (c == 't')
318 : 148 : return '\t';
319 [ + + ]: 48150 : else if (c == 'r')
320 : 230 : return '\r';
321 [ + + ]: 47920 : else if (c == 'e')
322 : 636 : return '\x1B';
323 [ + + ]: 47284 : else if (c == 'b')
324 : 76 : return '\b';
325 [ + + ]: 47208 : else if (c == 'f')
326 : 12 : return '\f';
327 [ + + ]: 47196 : else if (c == 'v')
328 : 8 : return '\v';
329 [ + + ]: 47188 : else if (c == 'a')
330 : 12 : return '\a';
331 : 47176 : return c;
332 : : }
333 : :
334 : : /* assumes that src points to the character after a backslash
335 : : returns number of input characters processed, 0 if error */
336 : 0 : size_t u8_read_escape_sequence(const char *str, size_t ssz, uint32_t *dest)
337 : : {
338 [ # # ]: 0 : assert(ssz > 0);
339 : : uint32_t ch;
340 : : char digs[10];
341 : 0 : int dno=0, ndig;
342 : 0 : size_t i=1;
343 : 0 : char c0 = str[0];
344 : :
345 [ # # ]: 0 : if (octal_digit(c0)) {
346 : 0 : i = 0;
347 : : do {
348 : 0 : digs[dno++] = str[i++];
349 [ # # # # : 0 : } while (i<ssz && octal_digit(str[i]) && dno<3);
# # ]
350 : 0 : digs[dno] = '\0';
351 : 0 : ch = strtol(digs, NULL, 8);
352 : : }
353 [ # # # # ]: 0 : else if ((c0=='x' && (ndig=2)) ||
354 [ # # ]: 0 : (c0=='u' && (ndig=4)) ||
355 : 0 : (c0=='U' && (ndig=8))) {
356 [ # # # # : 0 : while (i<ssz && hex_digit(str[i]) && dno<ndig) {
# # ]
357 : 0 : digs[dno++] = str[i++];
358 : : }
359 [ # # ]: 0 : if (dno == 0) return 0;
360 : 0 : digs[dno] = '\0';
361 : 0 : ch = strtol(digs, NULL, 16);
362 : : }
363 : : else {
364 : 0 : ch = (uint32_t)read_escape_control_char(c0);
365 : : }
366 : 0 : *dest = ch;
367 : :
368 : 0 : return i;
369 : : }
370 : :
371 : 0 : static inline int buf_put2c(char *buf, const char *src)
372 : : {
373 : 0 : buf[0] = src[0];
374 : 0 : buf[1] = src[1];
375 : 0 : buf[2] = '\0';
376 : 0 : return 2;
377 : : }
378 : :
379 : 0 : int u8_escape_wchar(char *buf, size_t sz, uint32_t ch)
380 : : {
381 [ # # ]: 0 : assert(sz > 2);
382 [ # # ]: 0 : if (ch == L'\n')
383 : 0 : return buf_put2c(buf, "\\n");
384 [ # # ]: 0 : else if (ch == L'\t')
385 : 0 : return buf_put2c(buf, "\\t");
386 [ # # ]: 0 : else if (ch == L'\r')
387 : 0 : return buf_put2c(buf, "\\r");
388 [ # # ]: 0 : else if (ch == L'\x1B')
389 : 0 : return buf_put2c(buf, "\\e");
390 [ # # ]: 0 : else if (ch == L'\b')
391 : 0 : return buf_put2c(buf, "\\b");
392 [ # # ]: 0 : else if (ch == L'\f')
393 : 0 : return buf_put2c(buf, "\\f");
394 [ # # ]: 0 : else if (ch == L'\v')
395 : 0 : return buf_put2c(buf, "\\v");
396 [ # # ]: 0 : else if (ch == L'\a')
397 : 0 : return buf_put2c(buf, "\\a");
398 [ # # ]: 0 : else if (ch == L'\\')
399 : 0 : return buf_put2c(buf, "\\\\");
400 [ # # # # ]: 0 : else if (ch < 32 || ch == 0x7f)
401 : 0 : return snprintf(buf, sz, "\\x%.2hhx", (unsigned char)ch);
402 [ # # ]: 0 : else if (ch > 0xFFFF)
403 : 0 : return snprintf(buf, sz, "\\U%.8x", (uint32_t)ch);
404 [ # # ]: 0 : else if (ch >= 0x80)
405 : 0 : return snprintf(buf, sz, "\\u%.4hx", (unsigned short)ch);
406 : :
407 : 0 : buf[0] = (char)ch;
408 : 0 : buf[1] = '\0';
409 : 0 : return 1;
410 : : }
411 : :
412 : 0 : size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end,
413 : : int escape_quotes, int ascii)
414 : : {
415 : 0 : size_t i = *pi, i0;
416 : : uint32_t ch;
417 : 0 : char *start = buf;
418 : 0 : char *blim = start + sz-11;
419 [ # # ]: 0 : assert(sz > 11);
420 : :
421 [ # # # # ]: 0 : while (i<end && buf<blim) {
422 : : // sz-11: leaves room for longest escape sequence
423 [ # # # # ]: 0 : if (escape_quotes && src[i] == '"') {
424 : 0 : buf += buf_put2c(buf, "\\\"");
425 : 0 : i++;
426 : : }
427 [ # # ]: 0 : else if (src[i] == '\\') {
428 : 0 : buf += buf_put2c(buf, "\\\\");
429 : 0 : i++;
430 : : }
431 : : else {
432 : 0 : i0 = i;
433 : 0 : ch = u8_nextmemchar(src, &i);
434 [ # # # # ]: 0 : if (ascii || !iswprint((wint_t)ch)) {
435 : 0 : buf += u8_escape_wchar(buf, sz - (buf-start), ch);
436 : : }
437 : : else {
438 : 0 : i = i0;
439 : : do {
440 : 0 : *buf++ = src[i++];
441 [ # # ]: 0 : } while (!isutf(src[i]));
442 : : }
443 : : }
444 : : }
445 : 0 : *buf++ = '\0';
446 : 0 : *pi = i;
447 : 0 : return (buf-start);
448 : : }
449 : :
450 : 0 : char *u8_memchr(const char *s, uint32_t ch, size_t sz, size_t *charn)
451 : : {
452 : 0 : size_t i = 0, lasti=0;
453 : : uint32_t c;
454 : : int csz;
455 : :
456 : 0 : *charn = 0;
457 [ # # ]: 0 : while (i < sz) {
458 : 0 : c = csz = 0;
459 : : do {
460 : 0 : c <<= 6;
461 : 0 : c += (unsigned char)s[i++];
462 : 0 : csz++;
463 [ # # # # ]: 0 : } while (i < sz && !isutf(s[i]));
464 : 0 : c -= offsetsFromUTF8[csz-1];
465 : :
466 [ # # ]: 0 : if (c == ch) {
467 : 0 : return (char*)&s[lasti];
468 : : }
469 : 0 : lasti = i;
470 : 0 : (*charn)++;
471 : : }
472 : 0 : return NULL;
473 : : }
474 : :
475 : 0 : char *u8_memrchr(const char *s, uint32_t ch, size_t sz)
476 : : {
477 : 0 : size_t i = sz-1, tempi=0;
478 : : uint32_t c;
479 : :
480 [ # # ]: 0 : if (sz == 0) return NULL;
481 : :
482 [ # # # # ]: 0 : while (i && !isutf(s[i])) i--;
483 : :
484 : : while (1) {
485 : 0 : tempi = i;
486 : 0 : c = u8_nextmemchar(s, &tempi);
487 [ # # ]: 0 : if (c == ch) {
488 : 0 : return (char*)&s[i];
489 : : }
490 [ # # ]: 0 : if (i == 0)
491 : 0 : break;
492 : 0 : tempi = i;
493 : 0 : u8_dec(s, &i);
494 [ # # ]: 0 : if (i > tempi)
495 : 0 : break;
496 : : }
497 : 0 : return NULL;
498 : : }
499 : :
500 : 0 : size_t u8_vprintf(const char *fmt, va_list ap)
501 : : {
502 : 0 : size_t cnt, sz=0, nc, needfree=0;
503 : : char *buf;
504 : : uint32_t *wcs;
505 : :
506 : 0 : sz = 512;
507 : 0 : buf = (char*)alloca(sz);
508 : 0 : cnt = vsnprintf(buf, sz, fmt, ap);
509 [ # # ]: 0 : if ((intptr_t)cnt < 0)
510 : 0 : return 0;
511 [ # # ]: 0 : if (cnt >= sz) {
512 : 0 : buf = (char*)malloc_s(cnt+1);
513 : 0 : needfree = 1;
514 : 0 : vsnprintf(buf, cnt+1, fmt, ap);
515 : : }
516 : 0 : wcs = (uint32_t*)alloca((cnt+1) * sizeof(uint32_t));
517 : 0 : nc = u8_toucs(wcs, cnt+1, buf, cnt);
518 : 0 : wcs[nc] = 0;
519 : 0 : printf("%ls", (wchar_t*)wcs);
520 [ # # ]: 0 : if (needfree)
521 : 0 : free(buf);
522 : 0 : return nc;
523 : : }
524 : :
525 : 0 : size_t u8_printf(const char *fmt, ...)
526 : : {
527 : : size_t cnt;
528 : : va_list args;
529 : :
530 : 0 : va_start(args, fmt);
531 : :
532 : 0 : cnt = u8_vprintf(fmt, args);
533 : :
534 : 0 : va_end(args);
535 : 0 : return cnt;
536 : : }
537 : :
538 : : /* Rewritten completely, original code not based on anything else
539 : :
540 : : length is in bytes, since without knowing whether the string is valid
541 : : it's hard to know how many characters there are! */
542 : 143714 : int u8_isvalid(const char *str, size_t len)
543 : : {
544 : : const unsigned char *pnt; // Current pointer in string
545 : : const unsigned char *pend; // End of string
546 : : unsigned char byt; // Current byte
547 : :
548 : : // Empty strings can be considered valid ASCII
549 [ - + ]: 143714 : if (!len) return 1;
550 : 143714 : pnt = (unsigned char *)str;
551 : 143714 : pend = (unsigned char *)str + len;
552 : : // First scan for non-ASCII characters as fast as possible
553 : : do {
554 [ + - ]: 143714 : if (*pnt++ & 0x80) goto chkutf8;
555 [ # # ]: 0 : } while (pnt < pend);
556 : 0 : return 1;
557 : :
558 : : // Check validity of UTF-8 sequences
559 : 143714 : chkutf8:
560 [ - + ]: 143714 : if (pnt == pend) return 0; // Last byte can't be > 127
561 : 143714 : byt = pnt[-1];
562 : : // Must be between 0xc2 and 0xf4 inclusive to be valid
563 [ - + ]: 143714 : if (((uint32_t)byt - 0xc2) > (0xf4-0xc2)) return 0;
564 [ + + ]: 143714 : if (byt < 0xe0) { // 2-byte sequence
565 : : // Must have valid continuation character
566 [ - + ]: 30954 : if ((*pnt++ & 0xc0) != 0x80) return 0;
567 [ + + ]: 112760 : } else if (byt < 0xf0) { // 3-byte sequence
568 [ + - ]: 88028 : if ((pnt + 1 >= pend)
569 [ + - ]: 88028 : || (*pnt & 0xc0) != 0x80
570 [ - + ]: 88028 : || (pnt[1] & 0xc0) != 0x80)
571 : 0 : return 0;
572 : : // Check for surrogate chars
573 [ - + - - ]: 88028 : if (byt == 0xed && *pnt > 0x9f) return 0;
574 : : // Check for overlong encoding
575 [ + + - + ]: 88028 : if (byt == 0xe0 && *pnt < 0xa0) return 0;
576 : 88028 : pnt += 2;
577 : : } else { // 4-byte sequence
578 : : // Must have 3 valid continuation characters
579 [ + - ]: 24732 : if ((pnt + 2 >= pend)
580 [ + - ]: 24732 : || (*pnt & 0xc0) != 0x80
581 [ + - ]: 24732 : || (pnt[1] & 0xc0) != 0x80
582 [ - + ]: 24732 : || (pnt[2] & 0xc0) != 0x80)
583 : 0 : return 0;
584 : : // Make sure in correct range (0x10000 - 0x10ffff)
585 [ + - ]: 24732 : if (byt == 0xf0) {
586 [ - + ]: 24732 : if (*pnt < 0x90) return 0;
587 [ # # ]: 0 : } else if (byt == 0xf4) {
588 [ # # ]: 0 : if (*pnt > 0x8f) return 0;
589 : : }
590 : 24732 : pnt += 3;
591 : : }
592 : : // Find next non-ASCII characters as fast as possible
593 [ - + ]: 143714 : while (pnt < pend) {
594 [ # # ]: 0 : if (*pnt++ & 0x80) goto chkutf8;
595 : : }
596 : 143714 : return 2; // Valid UTF-8
597 : : }
598 : : #ifdef __cplusplus
599 : : }
600 : : #endif
|