/src/dbus-broker/subprojects/libcutf8-1/src/c-utf8.c
Line | Count | Source |
1 | | /* |
2 | | * UTF-8 Implementation |
3 | | * |
4 | | * For highlevel documentation of the API see the header file and the docbook |
5 | | * comments. This implementation is inspired in part by Rust's std::str. |
6 | | * |
7 | | * So far only validation helpers are implemented, as those seem the most |
8 | | * critical. |
9 | | */ |
10 | | |
11 | | #include <c-stdaux.h> |
12 | | #include <stddef.h> |
13 | | #include <stdint.h> |
14 | | #include "c-utf8.h" |
15 | | |
16 | | /* The following constants are truncated on 32-bit machines */ |
17 | 146k | #define C_UTF8_ASCII_MASK ((size_t)UINT64_C(0x8080808080808080)) |
18 | 146k | #define C_UTF8_ASCII_SUB ((size_t)UINT64_C(0x0101010101010101)) |
19 | | |
20 | 146k | static inline int c_utf8_word_is_ascii(size_t word) { |
21 | | /* True unless any byte is NULL or has the MSB set. */ |
22 | 146k | return ((((word - C_UTF8_ASCII_SUB) | word) & C_UTF8_ASCII_MASK) == 0); |
23 | 146k | } |
24 | | |
25 | | /** |
26 | | * c_utf8_verify_ascii() - verify that a string is ASCII encoded |
27 | | * @strp: pointer to string to verify |
28 | | * @lenp: pointer to length of string |
29 | | * |
30 | | * Up to the first @lenp bytes of the string pointed to by @strp is |
31 | | * verified to be ASCII encoded, and @strp and @lenp are updated to |
32 | | * point to the first non-ASCII character or the first NULL of the |
33 | | * string, and the remaining number of bytes of the string, |
34 | | * respectively. |
35 | | * |
36 | | * If @lenp is NULL the string is scanned until the first invalid |
37 | | * byte, without any upper bound on its length. |
38 | | */ |
39 | 247k | _c_public_ void c_utf8_verify_ascii(const char **strp, size_t *lenp) { |
40 | 247k | const char *str = *strp; |
41 | 247k | size_t len = lenp ? *lenp : (size_t)-1; |
42 | | |
43 | 868k | while (len > 0 && c_load_8(str, 0) < 128) { |
44 | 620k | if ((void*)c_align_to((unsigned long)str, sizeof(size_t)) == str) { |
45 | | /* |
46 | | * If the string is aligned to a word boundary, scan two |
47 | | * words at a time for any NULL or non-ASCII characters. |
48 | | * |
49 | | * We do two words at a time to take advantage of the |
50 | | * compiler being able to use SIMD instructions where |
51 | | * available. |
52 | | */ |
53 | 117k | while (len >= 2 * sizeof(size_t)) { |
54 | 116k | if (!c_utf8_word_is_ascii(c_load(size_t, le, aligned, str, 0)) || |
55 | 30.2k | !c_utf8_word_is_ascii(c_load(size_t, le, aligned, str, sizeof(size_t)))) |
56 | 105k | break; |
57 | | |
58 | 11.6k | str += 2 * sizeof(size_t); |
59 | 11.6k | len -= 2 * sizeof(size_t); |
60 | 11.6k | } |
61 | | |
62 | | |
63 | | /* |
64 | | * Find the actual end of the ASCII-portion of the string. |
65 | | */ |
66 | 543k | while (len > 0 && c_load_8(str, 0) < 128) { |
67 | 437k | if (_c_unlikely_(c_load_8(str, 0) == 0x00)) |
68 | 23 | goto out; |
69 | 437k | ++str; |
70 | 437k | --len; |
71 | 437k | } |
72 | 514k | } else { |
73 | | /* |
74 | | * The string was not aligned, scan one character at a time until |
75 | | * it is. |
76 | | */ |
77 | 514k | if (_c_unlikely_(c_load_8(str, 0) == 0x00)) |
78 | 17 | goto out; |
79 | 514k | ++str; |
80 | 514k | --len; |
81 | 514k | } |
82 | 620k | } |
83 | | |
84 | 247k | out: |
85 | 247k | *strp = str; |
86 | 247k | if (lenp) |
87 | 247k | *lenp = len; |
88 | 247k | } |
89 | | |
90 | | #define C_UTF8_CHAR_IS_TAIL(_x) (((_x) & 0xC0) == 0x80) |
91 | | |
92 | | /** |
93 | | * c_utf8_verify() - verify that a string is UTF-8 encoded |
94 | | * @strp: pointer to string to verify |
95 | | * @lenp: pointer to length of string, or NULL |
96 | | * |
97 | | * Up to the first @lenp bytes of the string pointed to by @strp is |
98 | | * verified to be UTF-8 encoded, and @strp and @lenp are updated to |
99 | | * point to the first non-UTF-8 character or the first NULL of the |
100 | | * string, and the remaining number of bytes of the string, |
101 | | * respectively. |
102 | | * |
103 | | * If @lenp is NULL the string is scanned until the first invalid |
104 | | * byte, without any upper bound on its length. |
105 | | */ |
106 | 2.83k | _c_public_ void c_utf8_verify(const char **strp, size_t *lenp) { |
107 | 2.83k | const char *str = *strp; |
108 | 2.83k | size_t len = lenp ? *lenp : (size_t)-1; |
109 | | |
110 | | /* See Unicode 10.0.0, Chapter 3, Section D92 */ |
111 | | |
112 | 899k | while (len > 0) { |
113 | 897k | switch (c_load_8(str, 0)) { |
114 | 85 | case 0x00: |
115 | 85 | goto out; |
116 | 247k | case 0x01 ... 0x7F: |
117 | | /* |
118 | | * Special-case and optimize the ASCII case. |
119 | | */ |
120 | 247k | c_utf8_verify_ascii((const char **)&str, &len); |
121 | | |
122 | 247k | break; |
123 | 461k | case 0xC2 ... 0xDF: |
124 | 461k | if (_c_unlikely_(len < 2)) |
125 | 104 | goto out; |
126 | 461k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 1)))) |
127 | 70 | goto out; |
128 | | |
129 | 460k | str += 2; |
130 | 460k | len -= 2; |
131 | | |
132 | 460k | break; |
133 | 7.80k | case 0xE0: |
134 | 7.80k | if (_c_unlikely_(len < 3)) |
135 | 6 | goto out; |
136 | 7.79k | if (_c_unlikely_(c_load_8(str, 1) < 0xA0 || c_load_8(str, 1) > 0xBF)) |
137 | 16 | goto out; |
138 | 7.78k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 2)))) |
139 | 5 | goto out; |
140 | | |
141 | 7.77k | str += 3; |
142 | 7.77k | len -= 3; |
143 | | |
144 | 7.77k | break; |
145 | 116k | case 0xE1 ... 0xEC: |
146 | 116k | if (_c_unlikely_(len < 3)) |
147 | 52 | goto out; |
148 | 116k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 1)))) |
149 | 30 | goto out; |
150 | 116k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 2)))) |
151 | 31 | goto out; |
152 | | |
153 | 116k | str += 3; |
154 | 116k | len -= 3; |
155 | | |
156 | 116k | break; |
157 | 10.7k | case 0xED: |
158 | 10.7k | if (_c_unlikely_(len < 3)) |
159 | 7 | goto out; |
160 | 10.6k | if (_c_unlikely_(c_load_8(str, 1) < 0x80 || c_load_8(str, 1) > 0x9F)) |
161 | 20 | goto out; |
162 | 10.6k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 2)))) |
163 | 15 | goto out; |
164 | | |
165 | 10.6k | str += 3; |
166 | 10.6k | len -= 3; |
167 | | |
168 | 10.6k | break; |
169 | 8.61k | case 0xEE ... 0xEF: |
170 | 8.61k | if (_c_unlikely_(len < 3)) |
171 | 9 | goto out; |
172 | 8.60k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 1)))) |
173 | 10 | goto out; |
174 | 8.59k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 2)))) |
175 | 14 | goto out; |
176 | | |
177 | 8.57k | str += 3; |
178 | 8.57k | len -= 3; |
179 | | |
180 | 8.57k | break; |
181 | 5.48k | case 0xF0: |
182 | 5.48k | if (_c_unlikely_(len < 4)) |
183 | 5 | goto out; |
184 | 5.47k | if (_c_unlikely_(c_load_8(str, 1) < 0x90 || c_load_8(str, 1) > 0xBF)) |
185 | 20 | goto out; |
186 | 5.45k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 2)))) |
187 | 8 | goto out; |
188 | 5.45k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 3)))) |
189 | 9 | goto out; |
190 | | |
191 | 5.44k | str += 4; |
192 | 5.44k | len -= 4; |
193 | | |
194 | 5.44k | break; |
195 | 36.6k | case 0xF1 ... 0xF3: |
196 | 36.6k | if (_c_unlikely_(len < 4)) |
197 | 18 | goto out; |
198 | 36.6k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 1)))) |
199 | 10 | goto out; |
200 | 36.6k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 2)))) |
201 | 13 | goto out; |
202 | 36.6k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 3)))) |
203 | 9 | goto out; |
204 | | |
205 | 36.6k | str += 4; |
206 | 36.6k | len -= 4; |
207 | | |
208 | 36.6k | break; |
209 | 2.61k | case 0xF4: |
210 | 2.61k | if (_c_unlikely_(len < 4)) |
211 | 5 | goto out; |
212 | 2.60k | if (_c_unlikely_(c_load_8(str, 1) < 0x80 || c_load_8(str, 1) > 0x8F)) |
213 | 27 | goto out; |
214 | 2.58k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 2)))) |
215 | 10 | goto out; |
216 | 2.57k | if (_c_unlikely_(!C_UTF8_CHAR_IS_TAIL(c_load_8(str, 3)))) |
217 | 10 | goto out; |
218 | | |
219 | 2.56k | str += 4; |
220 | 2.56k | len -= 4; |
221 | | |
222 | 2.56k | break; |
223 | 68 | default: |
224 | 68 | goto out; |
225 | 897k | } |
226 | 897k | } |
227 | | |
228 | 2.83k | out: |
229 | 2.83k | *strp = str; |
230 | 2.83k | if (lenp) |
231 | 2.83k | *lenp = len; |
232 | 2.83k | } |