xref: /unit/src/nxt_utf8.c (revision 611)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru /*
100Sigor@sysoev.ru  * The nxt_unicode_lowcase.h file is the auto-generated file from
110Sigor@sysoev.ru  * the CaseFolding-6.3.0.txt file provided by Unicode, Inc.:
120Sigor@sysoev.ru  *
130Sigor@sysoev.ru  *   ./lib/src/nxt_unicode_lowcase.pl CaseFolding-6.3.0.txt
140Sigor@sysoev.ru  *
150Sigor@sysoev.ru  * This file should be copied to system specific nxt_unicode_SYSTEM_lowcase.h
160Sigor@sysoev.ru  * file and utf8_file_name_test should be built with this file.
170Sigor@sysoev.ru  * Then a correct system specific file should be generated:
180Sigor@sysoev.ru  *
190Sigor@sysoev.ru  *   ./build/utf8_file_name_test | ./lib/src/nxt_unicode_lowcase.pl
200Sigor@sysoev.ru  *
210Sigor@sysoev.ru  * Only common and simple case foldings are supported.  Full case foldings
220Sigor@sysoev.ru  * is not supported.  Combined characters are also not supported.
230Sigor@sysoev.ru  */
240Sigor@sysoev.ru 
250Sigor@sysoev.ru #if (NXT_MACOSX)
260Sigor@sysoev.ru #include <nxt_unicode_macosx_lowcase.h>
270Sigor@sysoev.ru 
280Sigor@sysoev.ru #else
290Sigor@sysoev.ru #include <nxt_unicode_lowcase.h>
300Sigor@sysoev.ru #endif
310Sigor@sysoev.ru 
320Sigor@sysoev.ru 
330Sigor@sysoev.ru u_char *
340Sigor@sysoev.ru nxt_utf8_encode(u_char *p, uint32_t u)
350Sigor@sysoev.ru {
360Sigor@sysoev.ru     if (u < 0x80) {
37*611Svbart@nginx.com         *p++ = (u_char) (u & 0xFF);
380Sigor@sysoev.ru         return p;
390Sigor@sysoev.ru     }
400Sigor@sysoev.ru 
410Sigor@sysoev.ru     if (u < 0x0800) {
42*611Svbart@nginx.com         *p++ = (u_char) (( u >> 6)          | 0xC0);
43*611Svbart@nginx.com         *p++ = (u_char) (( u        & 0x3F) | 0x80);
440Sigor@sysoev.ru         return p;
450Sigor@sysoev.ru     }
460Sigor@sysoev.ru 
470Sigor@sysoev.ru     if (u < 0x10000) {
48*611Svbart@nginx.com         *p++ = (u_char) ( (u >> 12)         | 0xE0);
49*611Svbart@nginx.com         *p++ = (u_char) (((u >>  6) & 0x3F) | 0x80);
50*611Svbart@nginx.com         *p++ = (u_char) (( u        & 0x3F) | 0x80);
510Sigor@sysoev.ru         return p;
520Sigor@sysoev.ru     }
530Sigor@sysoev.ru 
540Sigor@sysoev.ru     if (u < 0x110000) {
55*611Svbart@nginx.com         *p++ = (u_char) ( (u >> 18)         | 0xF0);
56*611Svbart@nginx.com         *p++ = (u_char) (((u >> 12) & 0x3F) | 0x80);
57*611Svbart@nginx.com         *p++ = (u_char) (((u >>  6) & 0x3F) | 0x80);
58*611Svbart@nginx.com         *p++ = (u_char) (( u        & 0x3F) | 0x80);
590Sigor@sysoev.ru         return p;
600Sigor@sysoev.ru     }
610Sigor@sysoev.ru 
620Sigor@sysoev.ru     return NULL;
630Sigor@sysoev.ru }
640Sigor@sysoev.ru 
650Sigor@sysoev.ru 
660Sigor@sysoev.ru /*
670Sigor@sysoev.ru  * nxt_utf8_decode() decodes UTF-8 sequences and returns a valid
68*611Svbart@nginx.com  * character 0x00 - 0x10FFFF, or 0xFFFFFFFF for invalid or overlong
690Sigor@sysoev.ru  * UTF-8 sequence.
700Sigor@sysoev.ru  */
710Sigor@sysoev.ru 
720Sigor@sysoev.ru uint32_t
730Sigor@sysoev.ru nxt_utf8_decode(const u_char **start, const u_char *end)
740Sigor@sysoev.ru {
750Sigor@sysoev.ru     uint32_t  u;
760Sigor@sysoev.ru 
770Sigor@sysoev.ru     u = (uint32_t) **start;
780Sigor@sysoev.ru 
790Sigor@sysoev.ru     if (u < 0x80) {
800Sigor@sysoev.ru         (*start)++;
810Sigor@sysoev.ru         return u;
820Sigor@sysoev.ru     }
830Sigor@sysoev.ru 
840Sigor@sysoev.ru     return nxt_utf8_decode2(start, end);
850Sigor@sysoev.ru }
860Sigor@sysoev.ru 
870Sigor@sysoev.ru 
880Sigor@sysoev.ru /*
890Sigor@sysoev.ru  * nxt_utf8_decode2() decodes two and more bytes UTF-8 sequences only
90*611Svbart@nginx.com  * and returns a valid character 0x80 - 0x10FFFF, or 0xFFFFFFFF for
910Sigor@sysoev.ru  * invalid or overlong UTF-8 sequence.
920Sigor@sysoev.ru  */
930Sigor@sysoev.ru 
940Sigor@sysoev.ru uint32_t
950Sigor@sysoev.ru nxt_utf8_decode2(const u_char **start, const u_char *end)
960Sigor@sysoev.ru {
970Sigor@sysoev.ru     u_char        c;
980Sigor@sysoev.ru     size_t        n;
990Sigor@sysoev.ru     uint32_t      u, overlong;
1000Sigor@sysoev.ru     const u_char  *p;
1010Sigor@sysoev.ru 
1020Sigor@sysoev.ru     p = *start;
1030Sigor@sysoev.ru     u = (uint32_t) *p;
1040Sigor@sysoev.ru 
105*611Svbart@nginx.com     if (u >= 0xE0) {
1060Sigor@sysoev.ru 
107*611Svbart@nginx.com         if (u >= 0xF0) {
1080Sigor@sysoev.ru 
109*611Svbart@nginx.com             if (nxt_slow_path(u > 0xF4)) {
1100Sigor@sysoev.ru                 /*
111*611Svbart@nginx.com                  * The maximum valid Unicode character is 0x10FFFF
112*611Svbart@nginx.com                  * which is encoded as 0xF4 0x8F 0xBF 0xBF.
1130Sigor@sysoev.ru                  */
114*611Svbart@nginx.com                 return 0xFFFFFFFF;
1150Sigor@sysoev.ru             }
1160Sigor@sysoev.ru 
1170Sigor@sysoev.ru             u &= 0x07;
118*611Svbart@nginx.com             overlong = 0x00FFFF;
1190Sigor@sysoev.ru             n = 3;
1200Sigor@sysoev.ru 
1210Sigor@sysoev.ru         } else {
122*611Svbart@nginx.com             u &= 0x0F;
123*611Svbart@nginx.com             overlong = 0x07FF;
1240Sigor@sysoev.ru             n = 2;
1250Sigor@sysoev.ru         }
1260Sigor@sysoev.ru 
127*611Svbart@nginx.com     } else if (u >= 0xC2) {
1280Sigor@sysoev.ru 
129*611Svbart@nginx.com         /* 0x80 is encoded as 0xC2 0x80. */
1300Sigor@sysoev.ru 
131*611Svbart@nginx.com         u &= 0x1F;
132*611Svbart@nginx.com         overlong = 0x007F;
1330Sigor@sysoev.ru         n = 1;
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru     } else {
136*611Svbart@nginx.com         /* u <= 0xC2 */
137*611Svbart@nginx.com         return 0xFFFFFFFF;
1380Sigor@sysoev.ru     }
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru     p++;
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru     if (nxt_fast_path(p + n <= end)) {
1430Sigor@sysoev.ru 
1440Sigor@sysoev.ru         do {
1450Sigor@sysoev.ru             c = *p++;
1460Sigor@sysoev.ru             /*
147*611Svbart@nginx.com              * The byte must in the 0x80 - 0xBF range.
1480Sigor@sysoev.ru              * Values below 0x80 become >= 0x80.
1490Sigor@sysoev.ru              */
1500Sigor@sysoev.ru             c = c - 0x80;
1510Sigor@sysoev.ru 
152*611Svbart@nginx.com             if (nxt_slow_path(c > 0x3F)) {
153*611Svbart@nginx.com                 return 0xFFFFFFFF;
1540Sigor@sysoev.ru             }
1550Sigor@sysoev.ru 
1560Sigor@sysoev.ru             u = (u << 6) | c;
1570Sigor@sysoev.ru             n--;
1580Sigor@sysoev.ru 
1590Sigor@sysoev.ru         } while (n != 0);
1600Sigor@sysoev.ru 
1610Sigor@sysoev.ru         if (overlong < u && u < 0x110000) {
1620Sigor@sysoev.ru             *start = p;
1630Sigor@sysoev.ru             return u;
1640Sigor@sysoev.ru         }
1650Sigor@sysoev.ru     }
1660Sigor@sysoev.ru 
167*611Svbart@nginx.com     return 0xFFFFFFFF;
1680Sigor@sysoev.ru }
1690Sigor@sysoev.ru 
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru /*
1720Sigor@sysoev.ru  * nxt_utf8_casecmp() tests only up to the minimum of given lengths, but
1730Sigor@sysoev.ru  * requires lengths of both strings because otherwise nxt_utf8_decode2()
1740Sigor@sysoev.ru  * may fail due to incomplete sequence.
1750Sigor@sysoev.ru  */
1760Sigor@sysoev.ru 
1770Sigor@sysoev.ru nxt_int_t
1780Sigor@sysoev.ru nxt_utf8_casecmp(const u_char *start1, const u_char *start2, size_t len1,
1790Sigor@sysoev.ru     size_t len2)
1800Sigor@sysoev.ru {
1810Sigor@sysoev.ru     int32_t       n;
1820Sigor@sysoev.ru     uint32_t      u1, u2;
1830Sigor@sysoev.ru     const u_char  *end1, *end2;
1840Sigor@sysoev.ru 
1850Sigor@sysoev.ru     end1 = start1 + len1;
1860Sigor@sysoev.ru     end2 = start2 + len2;
1870Sigor@sysoev.ru 
1880Sigor@sysoev.ru     while (start1 < end1 && start2 < end2) {
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru         u1 = nxt_utf8_lowcase(&start1, end1);
1910Sigor@sysoev.ru 
1920Sigor@sysoev.ru         u2 = nxt_utf8_lowcase(&start2, end2);
1930Sigor@sysoev.ru 
194*611Svbart@nginx.com         if (nxt_slow_path((u1 | u2) == 0xFFFFFFFF)) {
1950Sigor@sysoev.ru             return NXT_UTF8_SORT_INVALID;
1960Sigor@sysoev.ru         }
1970Sigor@sysoev.ru 
1980Sigor@sysoev.ru         n = u1 - u2;
1990Sigor@sysoev.ru 
2000Sigor@sysoev.ru         if (n != 0) {
2010Sigor@sysoev.ru             return (nxt_int_t) n;
2020Sigor@sysoev.ru         }
2030Sigor@sysoev.ru     }
2040Sigor@sysoev.ru 
2050Sigor@sysoev.ru     return 0;
2060Sigor@sysoev.ru }
2070Sigor@sysoev.ru 
2080Sigor@sysoev.ru 
2090Sigor@sysoev.ru uint32_t
2100Sigor@sysoev.ru nxt_utf8_lowcase(const u_char **start, const u_char *end)
2110Sigor@sysoev.ru {
2120Sigor@sysoev.ru     uint32_t        u;
2130Sigor@sysoev.ru     const uint32_t  *block;
2140Sigor@sysoev.ru 
2150Sigor@sysoev.ru     u = (uint32_t) **start;
2160Sigor@sysoev.ru 
2170Sigor@sysoev.ru     if (nxt_fast_path(u < 0x80)) {
2180Sigor@sysoev.ru         (*start)++;
2190Sigor@sysoev.ru 
2200Sigor@sysoev.ru         return nxt_unicode_block_000[u];
2210Sigor@sysoev.ru     }
2220Sigor@sysoev.ru 
2230Sigor@sysoev.ru     u = nxt_utf8_decode2(start, end);
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru     if (u <= NXT_UNICODE_MAX_LOWCASE) {
2260Sigor@sysoev.ru         block = nxt_unicode_blocks[u / NXT_UNICODE_BLOCK_SIZE];
2270Sigor@sysoev.ru 
2280Sigor@sysoev.ru         if (block != NULL) {
2290Sigor@sysoev.ru             return block[u % NXT_UNICODE_BLOCK_SIZE];
2300Sigor@sysoev.ru         }
2310Sigor@sysoev.ru     }
2320Sigor@sysoev.ru 
2330Sigor@sysoev.ru     return u;
2340Sigor@sysoev.ru }
2350Sigor@sysoev.ru 
2360Sigor@sysoev.ru 
2370Sigor@sysoev.ru ssize_t
2380Sigor@sysoev.ru nxt_utf8_length(const u_char *p, size_t len)
2390Sigor@sysoev.ru {
2400Sigor@sysoev.ru     ssize_t       length;
2410Sigor@sysoev.ru     const u_char  *end;
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru     length = 0;
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru     end = p + len;
2460Sigor@sysoev.ru 
2470Sigor@sysoev.ru     while (p < end) {
248*611Svbart@nginx.com         if (nxt_slow_path(nxt_utf8_decode(&p, end) == 0xFFFFFFFF)) {
2490Sigor@sysoev.ru             return -1;
2500Sigor@sysoev.ru         }
2510Sigor@sysoev.ru 
2520Sigor@sysoev.ru         length++;
2530Sigor@sysoev.ru     }
2540Sigor@sysoev.ru 
2550Sigor@sysoev.ru     return length;
2560Sigor@sysoev.ru }
2570Sigor@sysoev.ru 
2580Sigor@sysoev.ru 
2590Sigor@sysoev.ru nxt_bool_t
2600Sigor@sysoev.ru nxt_utf8_is_valid(const u_char *p, size_t len)
2610Sigor@sysoev.ru {
2620Sigor@sysoev.ru     const u_char  *end;
2630Sigor@sysoev.ru 
2640Sigor@sysoev.ru     end = p + len;
2650Sigor@sysoev.ru 
2660Sigor@sysoev.ru     while (p < end) {
267*611Svbart@nginx.com         if (nxt_slow_path(nxt_utf8_decode(&p, end) == 0xFFFFFFFF)) {
2680Sigor@sysoev.ru             return 0;
2690Sigor@sysoev.ru         }
2700Sigor@sysoev.ru     }
2710Sigor@sysoev.ru 
2720Sigor@sysoev.ru     return 1;
2730Sigor@sysoev.ru }
274