/src/pcre2/src/pcre2_script_run.c
Line | Count | Source |
1 | | /************************************************* |
2 | | * Perl-Compatible Regular Expressions * |
3 | | *************************************************/ |
4 | | |
5 | | /* PCRE is a library of functions to support regular expressions whose syntax |
6 | | and semantics are as close as possible to those of the Perl 5 language. |
7 | | |
8 | | Written by Philip Hazel |
9 | | Original API code Copyright (c) 1997-2012 University of Cambridge |
10 | | New API code Copyright (c) 2016-2021 University of Cambridge |
11 | | |
12 | | ----------------------------------------------------------------------------- |
13 | | Redistribution and use in source and binary forms, with or without |
14 | | modification, are permitted provided that the following conditions are met: |
15 | | |
16 | | * Redistributions of source code must retain the above copyright notice, |
17 | | this list of conditions and the following disclaimer. |
18 | | |
19 | | * Redistributions in binary form must reproduce the above copyright |
20 | | notice, this list of conditions and the following disclaimer in the |
21 | | documentation and/or other materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the University of Cambridge nor the names of its |
24 | | contributors may be used to endorse or promote products derived from |
25 | | this software without specific prior written permission. |
26 | | |
27 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
28 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
29 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
30 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
31 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
32 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
33 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
34 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
35 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
36 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
37 | | POSSIBILITY OF SUCH DAMAGE. |
38 | | ----------------------------------------------------------------------------- |
39 | | */ |
40 | | |
41 | | |
42 | | /* This module contains the function for checking a script run. */ |
43 | | |
44 | | |
45 | | #include "pcre2_internal.h" |
46 | | |
47 | | |
48 | | |
49 | | /************************************************* |
50 | | * Check script run * |
51 | | *************************************************/ |
52 | | |
53 | | /* A script run is conceptually a sequence of characters all in the same |
54 | | Unicode script. However, it isn't quite that simple. There are special rules |
55 | | for scripts that are commonly used together, and also special rules for digits. |
56 | | This function implements the appropriate checks, which is possible only when |
57 | | PCRE2 is compiled with Unicode support. The function returns TRUE if there is |
58 | | no Unicode support; however, it should never be called in that circumstance |
59 | | because an error is given by pcre2_compile() if a script run is called for in a |
60 | | version of PCRE2 compiled without Unicode support. |
61 | | |
62 | | Arguments: |
63 | | pgr point to the first character |
64 | | endptr point after the last character |
65 | | utf TRUE if in UTF mode |
66 | | |
67 | | Returns: TRUE if this is a valid script run |
68 | | */ |
69 | | |
70 | | /* These are states in the checking process. */ |
71 | | |
72 | | enum { |
73 | | SCRIPT_UNSET, // Requirement as yet unknown |
74 | | SCRIPT_MAP, // Bitmap contains acceptable scripts |
75 | | SCRIPT_HANPENDING, // Have had only Han characters |
76 | | SCRIPT_HANHIRAKATA, // Expect Han or Hirikata |
77 | | SCRIPT_HANBOPOMOFO, // Expect Han or Bopomofo |
78 | | SCRIPT_HANHANGUL // Expect Han or Hangul |
79 | | }; |
80 | | |
81 | 3.79M | #define UCD_MAPSIZE (ucp_Unknown / 32 + 1) |
82 | 9.47M | #define FULL_MAPSIZE (ucp_Script_Count / 32 + 1) |
83 | | |
84 | | BOOL |
85 | | PRIV(script_run)(PCRE2_SPTR ptr, PCRE2_SPTR endptr, BOOL utf) |
86 | 662k | { |
87 | 662k | #ifdef SUPPORT_UNICODE |
88 | 662k | uint32_t require_state = SCRIPT_UNSET; |
89 | 662k | uint32_t require_map[FULL_MAPSIZE]; |
90 | 662k | uint32_t map[FULL_MAPSIZE]; |
91 | 662k | uint32_t require_digitset = 0; |
92 | 662k | uint32_t c; |
93 | | |
94 | | #if PCRE2_CODE_UNIT_WIDTH == 32 |
95 | | (void)utf; // Avoid compiler warning |
96 | | #endif |
97 | | |
98 | | /* Any string containing fewer than 2 characters is a valid script run. */ |
99 | | |
100 | 662k | if (ptr >= endptr) |
101 | 161k | return TRUE; |
102 | 500k | GETCHARINCTEST(c, ptr); |
103 | 500k | if (ptr >= endptr) |
104 | 31.7k | return TRUE; |
105 | | |
106 | | /* Initialize the require map. This is a full-size bitmap that has a bit for |
107 | | every script, as opposed to the maps in ucd_script_sets, which only have bits |
108 | | for scripts less than ucp_Unknown - those that appear in script extension |
109 | | lists. */ |
110 | | |
111 | 3.28M | for (int i = 0; i < FULL_MAPSIZE; i++) |
112 | 2.81M | require_map[i] = 0; |
113 | | |
114 | | /* Scan strings of two or more characters, checking the Unicode characteristics |
115 | | of each code point. There is special code for scripts that can be combined with |
116 | | characters from the Han Chinese script. This may be used in conjunction with |
117 | | four other scripts in these combinations: |
118 | | |
119 | | . Han with Hiragana and Katakana is allowed (for Japanese). |
120 | | . Han with Bopomofo is allowed (for Taiwanese Mandarin). |
121 | | . Han with Hangul is allowed (for Korean). |
122 | | |
123 | | If the first significant character's script is one of the four, the required |
124 | | script type is immediately known. However, if the first significant |
125 | | character's script is Han, we have to keep checking for a non-Han character. |
126 | | Hence the SCRIPT_HANPENDING state. */ |
127 | | |
128 | 468k | for (;;) |
129 | 4.32M | { |
130 | 4.32M | const ucd_record *ucd = GET_UCD(c); |
131 | 4.32M | uint32_t script = ucd->script; |
132 | | |
133 | | /* If the script is Unknown, the string is not a valid script run. Such |
134 | | characters can only form script runs of length one (see test above). */ |
135 | | |
136 | 4.32M | if (script == ucp_Unknown) |
137 | 65.8k | return FALSE; |
138 | | |
139 | | /* A character without any script extensions whose script is Inherited or |
140 | | Common is always accepted with any script. If there are extensions, the |
141 | | following processing happens for all scripts. */ |
142 | | |
143 | 4.25M | if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common)) |
144 | 1.26M | { |
145 | 1.26M | BOOL OK; |
146 | | |
147 | | /* Set up a full-sized map for this character that can include bits for all |
148 | | scripts. Copy the scriptx map for this character (which covers those |
149 | | scripts that appear in script extension lists), set the remaining values to |
150 | | zero, and then, except for Common or Inherited, add this script's bit to |
151 | | the map. */ |
152 | | |
153 | 1.26M | memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t)); |
154 | 1.26M | memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t)); |
155 | 1.26M | if (script != ucp_Common && script != ucp_Inherited) |
156 | 1.15M | MAPSET(map, script); |
157 | | |
158 | | /* Handle the different checking states */ |
159 | | |
160 | 1.26M | switch (require_state) |
161 | 1.26M | { |
162 | | /* First significant character - it might follow Common or Inherited |
163 | | characters that do not have any script extensions. */ |
164 | | |
165 | 398k | case SCRIPT_UNSET: |
166 | 398k | switch (script) |
167 | 398k | { |
168 | 64.7k | case ucp_Han: |
169 | 64.7k | require_state = SCRIPT_HANPENDING; |
170 | 64.7k | break; |
171 | | |
172 | 5.89k | case ucp_Hiragana: |
173 | 13.2k | case ucp_Katakana: |
174 | 13.2k | require_state = SCRIPT_HANHIRAKATA; |
175 | 13.2k | break; |
176 | | |
177 | 9.45k | case ucp_Bopomofo: |
178 | 9.45k | require_state = SCRIPT_HANBOPOMOFO; |
179 | 9.45k | break; |
180 | | |
181 | 14.6k | case ucp_Hangul: |
182 | 14.6k | require_state = SCRIPT_HANHANGUL; |
183 | 14.6k | break; |
184 | | |
185 | 295k | default: |
186 | 295k | memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t)); |
187 | 295k | require_state = SCRIPT_MAP; |
188 | 295k | break; |
189 | 398k | } |
190 | | |
191 | 398k | break; |
192 | | |
193 | | /* The first significant character was Han. An inspection of the Unicode |
194 | | 11.0.0 files shows that there are the following types of Script Extension |
195 | | list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul |
196 | | scripts: |
197 | | |
198 | | . Bopomofo + Han |
199 | | . Han + Hiragana + Katakana |
200 | | . Hiragana + Katakana |
201 | | . Bopopmofo + Hangul + Han + Hiragana + Katakana |
202 | | |
203 | | The following code tries to make sense of this. */ |
204 | | |
205 | 398k | #define FOUND_BOPOMOFO 1 |
206 | 398k | #define FOUND_HIRAGANA 2 |
207 | 398k | #define FOUND_KATAKANA 4 |
208 | 398k | #define FOUND_HANGUL 8 |
209 | | |
210 | 398k | case SCRIPT_HANPENDING: |
211 | 155k | if (script != ucp_Han) // Another Han does nothing |
212 | 75.4k | { |
213 | 75.4k | uint32_t chspecial = 0; |
214 | | |
215 | 75.4k | if (MAPBIT(map, ucp_Bopomofo) != 0) |
216 | 18.0k | chspecial |= FOUND_BOPOMOFO; |
217 | 75.4k | if (MAPBIT(map, ucp_Hiragana) != 0) |
218 | 26.6k | chspecial |= FOUND_HIRAGANA; |
219 | 75.4k | if (MAPBIT(map, ucp_Katakana) != 0) |
220 | 32.1k | chspecial |= FOUND_KATAKANA; |
221 | 75.4k | if (MAPBIT(map, ucp_Hangul) != 0) |
222 | 24.7k | chspecial |= FOUND_HANGUL; |
223 | | |
224 | 75.4k | if (chspecial == 0) |
225 | 27.2k | return FALSE; // Not allowed with Han |
226 | | |
227 | 48.1k | if (chspecial == FOUND_BOPOMOFO) |
228 | 3.32k | require_state = SCRIPT_HANBOPOMOFO; |
229 | 44.8k | else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA)) |
230 | 9.42k | require_state = SCRIPT_HANHIRAKATA; |
231 | | |
232 | | /* Otherwise this character must be allowed with all of them, so remain |
233 | | in the pending state. */ |
234 | 48.1k | } |
235 | | |
236 | 128k | break; |
237 | | |
238 | | /* Previously encountered one of the "with Han" scripts. Check that |
239 | | this character is appropriate. */ |
240 | | |
241 | 128k | case SCRIPT_HANHIRAKATA: |
242 | 45.4k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0) |
243 | 21.7k | return FALSE; |
244 | 23.7k | break; |
245 | | |
246 | 26.8k | case SCRIPT_HANBOPOMOFO: |
247 | 26.8k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0) |
248 | 11.6k | return FALSE; |
249 | 15.1k | break; |
250 | | |
251 | 26.1k | case SCRIPT_HANHANGUL: |
252 | 26.1k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0) |
253 | 12.6k | return FALSE; |
254 | 13.4k | break; |
255 | | |
256 | | /* Previously encountered one or more characters that are allowed with a |
257 | | list of scripts. */ |
258 | | |
259 | 611k | case SCRIPT_MAP: |
260 | 611k | OK = FALSE; |
261 | | |
262 | 1.28M | for (int i = 0; i < FULL_MAPSIZE; i++) |
263 | 1.17M | { |
264 | 1.17M | if ((require_map[i] & map[i]) != 0) |
265 | 502k | { |
266 | 502k | OK = TRUE; |
267 | 502k | break; |
268 | 502k | } |
269 | 1.17M | } |
270 | | |
271 | 611k | if (!OK) |
272 | 109k | return FALSE; |
273 | | |
274 | | /* The rest of the string must be in this script, but we have to |
275 | | allow for the Han complications. */ |
276 | | |
277 | 502k | switch (script) |
278 | 502k | { |
279 | 5.57k | case ucp_Han: |
280 | 5.57k | require_state = SCRIPT_HANPENDING; |
281 | 5.57k | break; |
282 | | |
283 | 3.37k | case ucp_Hiragana: |
284 | 9.18k | case ucp_Katakana: |
285 | 9.18k | require_state = SCRIPT_HANHIRAKATA; |
286 | 9.18k | break; |
287 | | |
288 | 4.74k | case ucp_Bopomofo: |
289 | 4.74k | require_state = SCRIPT_HANBOPOMOFO; |
290 | 4.74k | break; |
291 | | |
292 | 4.25k | case ucp_Hangul: |
293 | 4.25k | require_state = SCRIPT_HANHANGUL; |
294 | 4.25k | break; |
295 | | |
296 | | /* Compute the intersection of the required list of scripts and the |
297 | | allowed scripts for this character. */ |
298 | | |
299 | 478k | default: |
300 | 3.35M | for (int i = 0; i < FULL_MAPSIZE; i++) |
301 | 2.87M | require_map[i] &= map[i]; |
302 | 478k | break; |
303 | 502k | } |
304 | | |
305 | 502k | break; |
306 | | |
307 | | /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */ |
308 | 502k | default: |
309 | 0 | PCRE2_DEBUG_UNREACHABLE(); |
310 | 0 | break; |
311 | | /* LCOV_EXCL_STOP */ |
312 | 1.26M | } |
313 | 1.26M | } // End checking character's script and extensions. |
314 | | |
315 | | /* The character is in an acceptable script. We must now ensure that all |
316 | | decimal digits in the string come from the same set. Some scripts (e.g. |
317 | | Common, Arabic) have more than one set of decimal digits. This code does |
318 | | not allow mixing sets, even within the same script. The vector called |
319 | | PRIV(ucd_digit_sets)[] contains, in its first element, the number of |
320 | | following elements, and then, in ascending order, the code points of the |
321 | | '9' characters in every set of 10 digits. Each set is identified by the |
322 | | offset in the vector of its '9' character. An initial check of the first |
323 | | value picks up ASCII digits quickly. Otherwise, a binary chop is used. */ |
324 | | |
325 | 4.07M | if (ucd->chartype == ucp_Nd) |
326 | 287k | { |
327 | 287k | uint32_t digitset; |
328 | | |
329 | 287k | if (c <= PRIV(ucd_digit_sets)[1]) |
330 | 266k | { |
331 | 266k | digitset = 1; |
332 | 266k | } |
333 | 21.5k | else |
334 | 21.5k | { |
335 | 21.5k | int mid; |
336 | 21.5k | int bot = 1; |
337 | 21.5k | int top = PRIV(ucd_digit_sets)[0]; |
338 | 21.5k | for (;;) |
339 | 152k | { |
340 | 152k | if (top <= bot + 1) // <= rather than == is paranoia |
341 | 21.5k | { |
342 | 21.5k | digitset = top; |
343 | 21.5k | break; |
344 | 21.5k | } |
345 | | |
346 | 131k | mid = (top + bot) / 2; |
347 | 131k | if (c <= PRIV(ucd_digit_sets)[mid]) |
348 | 75.8k | top = mid; |
349 | 55.2k | else |
350 | 55.2k | bot = mid; |
351 | 131k | } |
352 | 21.5k | } |
353 | | |
354 | | /* A required value of 0 means "unset". */ |
355 | | |
356 | 287k | if (require_digitset == 0) |
357 | 53.9k | require_digitset = digitset; |
358 | 233k | else if (digitset != require_digitset) |
359 | 6.56k | return FALSE; |
360 | 287k | } // End digit handling |
361 | | |
362 | | /* If we haven't yet got to the end, pick up the next character. */ |
363 | | |
364 | 4.07M | if (ptr >= endptr) |
365 | 214k | return TRUE; |
366 | 3.85M | GETCHARINCTEST(c, ptr); |
367 | 3.85M | } // End checking loop |
368 | | |
369 | | #else /* NOT SUPPORT_UNICODE */ |
370 | | (void)ptr; |
371 | | (void)endptr; |
372 | | (void)utf; |
373 | | return TRUE; |
374 | | #endif /* SUPPORT_UNICODE */ |
375 | 468k | } Line | Count | Source | 86 | 249k | { | 87 | 249k | #ifdef SUPPORT_UNICODE | 88 | 249k | uint32_t require_state = SCRIPT_UNSET; | 89 | 249k | uint32_t require_map[FULL_MAPSIZE]; | 90 | 249k | uint32_t map[FULL_MAPSIZE]; | 91 | 249k | uint32_t require_digitset = 0; | 92 | 249k | uint32_t c; | 93 | | | 94 | | #if PCRE2_CODE_UNIT_WIDTH == 32 | 95 | | (void)utf; // Avoid compiler warning | 96 | | #endif | 97 | | | 98 | | /* Any string containing fewer than 2 characters is a valid script run. */ | 99 | | | 100 | 249k | if (ptr >= endptr) | 101 | 50.3k | return TRUE; | 102 | 199k | GETCHARINCTEST(c, ptr); | 103 | 199k | if (ptr >= endptr) | 104 | 12.7k | return TRUE; | 105 | | | 106 | | /* Initialize the require map. This is a full-size bitmap that has a bit for | 107 | | every script, as opposed to the maps in ucd_script_sets, which only have bits | 108 | | for scripts less than ucp_Unknown - those that appear in script extension | 109 | | lists. */ | 110 | | | 111 | 1.30M | for (int i = 0; i < FULL_MAPSIZE; i++) | 112 | 1.11M | require_map[i] = 0; | 113 | | | 114 | | /* Scan strings of two or more characters, checking the Unicode characteristics | 115 | | of each code point. There is special code for scripts that can be combined with | 116 | | characters from the Han Chinese script. This may be used in conjunction with | 117 | | four other scripts in these combinations: | 118 | | | 119 | | . Han with Hiragana and Katakana is allowed (for Japanese). | 120 | | . Han with Bopomofo is allowed (for Taiwanese Mandarin). | 121 | | . Han with Hangul is allowed (for Korean). | 122 | | | 123 | | If the first significant character's script is one of the four, the required | 124 | | script type is immediately known. However, if the first significant | 125 | | character's script is Han, we have to keep checking for a non-Han character. | 126 | | Hence the SCRIPT_HANPENDING state. */ | 127 | | | 128 | 186k | for (;;) | 129 | 2.48M | { | 130 | 2.48M | const ucd_record *ucd = GET_UCD(c); | 131 | 2.48M | uint32_t script = ucd->script; | 132 | | | 133 | | /* If the script is Unknown, the string is not a valid script run. Such | 134 | | characters can only form script runs of length one (see test above). */ | 135 | | | 136 | 2.48M | if (script == ucp_Unknown) | 137 | 5.00k | return FALSE; | 138 | | | 139 | | /* A character without any script extensions whose script is Inherited or | 140 | | Common is always accepted with any script. If there are extensions, the | 141 | | following processing happens for all scripts. */ | 142 | | | 143 | 2.48M | if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common)) | 144 | 606k | { | 145 | 606k | BOOL OK; | 146 | | | 147 | | /* Set up a full-sized map for this character that can include bits for all | 148 | | scripts. Copy the scriptx map for this character (which covers those | 149 | | scripts that appear in script extension lists), set the remaining values to | 150 | | zero, and then, except for Common or Inherited, add this script's bit to | 151 | | the map. */ | 152 | | | 153 | 606k | memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t)); | 154 | 606k | memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t)); | 155 | 606k | if (script != ucp_Common && script != ucp_Inherited) | 156 | 577k | MAPSET(map, script); | 157 | | | 158 | | /* Handle the different checking states */ | 159 | | | 160 | 606k | switch (require_state) | 161 | 606k | { | 162 | | /* First significant character - it might follow Common or Inherited | 163 | | characters that do not have any script extensions. */ | 164 | | | 165 | 161k | case SCRIPT_UNSET: | 166 | 161k | switch (script) | 167 | 161k | { | 168 | 9.08k | case ucp_Han: | 169 | 9.08k | require_state = SCRIPT_HANPENDING; | 170 | 9.08k | break; | 171 | | | 172 | 2.89k | case ucp_Hiragana: | 173 | 5.83k | case ucp_Katakana: | 174 | 5.83k | require_state = SCRIPT_HANHIRAKATA; | 175 | 5.83k | break; | 176 | | | 177 | 1.85k | case ucp_Bopomofo: | 178 | 1.85k | require_state = SCRIPT_HANBOPOMOFO; | 179 | 1.85k | break; | 180 | | | 181 | 2.06k | case ucp_Hangul: | 182 | 2.06k | require_state = SCRIPT_HANHANGUL; | 183 | 2.06k | break; | 184 | | | 185 | 142k | default: | 186 | 142k | memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t)); | 187 | 142k | require_state = SCRIPT_MAP; | 188 | 142k | break; | 189 | 161k | } | 190 | | | 191 | 161k | break; | 192 | | | 193 | | /* The first significant character was Han. An inspection of the Unicode | 194 | | 11.0.0 files shows that there are the following types of Script Extension | 195 | | list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul | 196 | | scripts: | 197 | | | 198 | | . Bopomofo + Han | 199 | | . Han + Hiragana + Katakana | 200 | | . Hiragana + Katakana | 201 | | . Bopopmofo + Hangul + Han + Hiragana + Katakana | 202 | | | 203 | | The following code tries to make sense of this. */ | 204 | | | 205 | 161k | #define FOUND_BOPOMOFO 1 | 206 | 161k | #define FOUND_HIRAGANA 2 | 207 | 161k | #define FOUND_KATAKANA 4 | 208 | 161k | #define FOUND_HANGUL 8 | 209 | | | 210 | 161k | case SCRIPT_HANPENDING: | 211 | 10.7k | if (script != ucp_Han) // Another Han does nothing | 212 | 9.48k | { | 213 | 9.48k | uint32_t chspecial = 0; | 214 | | | 215 | 9.48k | if (MAPBIT(map, ucp_Bopomofo) != 0) | 216 | 1.75k | chspecial |= FOUND_BOPOMOFO; | 217 | 9.48k | if (MAPBIT(map, ucp_Hiragana) != 0) | 218 | 3.67k | chspecial |= FOUND_HIRAGANA; | 219 | 9.48k | if (MAPBIT(map, ucp_Katakana) != 0) | 220 | 4.11k | chspecial |= FOUND_KATAKANA; | 221 | 9.48k | if (MAPBIT(map, ucp_Hangul) != 0) | 222 | 1.72k | chspecial |= FOUND_HANGUL; | 223 | | | 224 | 9.48k | if (chspecial == 0) | 225 | 3.77k | return FALSE; // Not allowed with Han | 226 | | | 227 | 5.71k | if (chspecial == FOUND_BOPOMOFO) | 228 | 803 | require_state = SCRIPT_HANBOPOMOFO; | 229 | 4.91k | else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA)) | 230 | 2.70k | require_state = SCRIPT_HANHIRAKATA; | 231 | | | 232 | | /* Otherwise this character must be allowed with all of them, so remain | 233 | | in the pending state. */ | 234 | 5.71k | } | 235 | | | 236 | 7.01k | break; | 237 | | | 238 | | /* Previously encountered one of the "with Han" scripts. Check that | 239 | | this character is appropriate. */ | 240 | | | 241 | 11.6k | case SCRIPT_HANHIRAKATA: | 242 | 11.6k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0) | 243 | 9.28k | return FALSE; | 244 | 2.32k | break; | 245 | | | 246 | 3.33k | case SCRIPT_HANBOPOMOFO: | 247 | 3.33k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0) | 248 | 1.97k | return FALSE; | 249 | 1.36k | break; | 250 | | | 251 | 3.94k | case SCRIPT_HANHANGUL: | 252 | 3.94k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0) | 253 | 2.23k | return FALSE; | 254 | 1.70k | break; | 255 | | | 256 | | /* Previously encountered one or more characters that are allowed with a | 257 | | list of scripts. */ | 258 | | | 259 | 416k | case SCRIPT_MAP: | 260 | 416k | OK = FALSE; | 261 | | | 262 | 634k | for (int i = 0; i < FULL_MAPSIZE; i++) | 263 | 599k | { | 264 | 599k | if ((require_map[i] & map[i]) != 0) | 265 | 380k | { | 266 | 380k | OK = TRUE; | 267 | 380k | break; | 268 | 380k | } | 269 | 599k | } | 270 | | | 271 | 416k | if (!OK) | 272 | 35.6k | return FALSE; | 273 | | | 274 | | /* The rest of the string must be in this script, but we have to | 275 | | allow for the Han complications. */ | 276 | | | 277 | 380k | switch (script) | 278 | 380k | { | 279 | 1.22k | case ucp_Han: | 280 | 1.22k | require_state = SCRIPT_HANPENDING; | 281 | 1.22k | break; | 282 | | | 283 | 1.54k | case ucp_Hiragana: | 284 | 5.09k | case ucp_Katakana: | 285 | 5.09k | require_state = SCRIPT_HANHIRAKATA; | 286 | 5.09k | break; | 287 | | | 288 | 727 | case ucp_Bopomofo: | 289 | 727 | require_state = SCRIPT_HANBOPOMOFO; | 290 | 727 | break; | 291 | | | 292 | 2.72k | case ucp_Hangul: | 293 | 2.72k | require_state = SCRIPT_HANHANGUL; | 294 | 2.72k | break; | 295 | | | 296 | | /* Compute the intersection of the required list of scripts and the | 297 | | allowed scripts for this character. */ | 298 | | | 299 | 370k | default: | 300 | 2.59M | for (int i = 0; i < FULL_MAPSIZE; i++) | 301 | 2.22M | require_map[i] &= map[i]; | 302 | 370k | break; | 303 | 380k | } | 304 | | | 305 | 380k | break; | 306 | | | 307 | | /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */ | 308 | 380k | default: | 309 | 0 | PCRE2_DEBUG_UNREACHABLE(); | 310 | 0 | break; | 311 | | /* LCOV_EXCL_STOP */ | 312 | 606k | } | 313 | 606k | } // End checking character's script and extensions. | 314 | | | 315 | | /* The character is in an acceptable script. We must now ensure that all | 316 | | decimal digits in the string come from the same set. Some scripts (e.g. | 317 | | Common, Arabic) have more than one set of decimal digits. This code does | 318 | | not allow mixing sets, even within the same script. The vector called | 319 | | PRIV(ucd_digit_sets)[] contains, in its first element, the number of | 320 | | following elements, and then, in ascending order, the code points of the | 321 | | '9' characters in every set of 10 digits. Each set is identified by the | 322 | | offset in the vector of its '9' character. An initial check of the first | 323 | | value picks up ASCII digits quickly. Otherwise, a binary chop is used. */ | 324 | | | 325 | 2.42M | if (ucd->chartype == ucp_Nd) | 326 | 259k | { | 327 | 259k | uint32_t digitset; | 328 | | | 329 | 259k | if (c <= PRIV(ucd_digit_sets)[1]) | 330 | 253k | { | 331 | 253k | digitset = 1; | 332 | 253k | } | 333 | 5.88k | else | 334 | 5.88k | { | 335 | 5.88k | int mid; | 336 | 5.88k | int bot = 1; | 337 | 5.88k | int top = PRIV(ucd_digit_sets)[0]; | 338 | 5.88k | for (;;) | 339 | 42.1k | { | 340 | 42.1k | if (top <= bot + 1) // <= rather than == is paranoia | 341 | 5.88k | { | 342 | 5.88k | digitset = top; | 343 | 5.88k | break; | 344 | 5.88k | } | 345 | | | 346 | 36.2k | mid = (top + bot) / 2; | 347 | 36.2k | if (c <= PRIV(ucd_digit_sets)[mid]) | 348 | 24.5k | top = mid; | 349 | 11.6k | else | 350 | 11.6k | bot = mid; | 351 | 36.2k | } | 352 | 5.88k | } | 353 | | | 354 | | /* A required value of 0 means "unset". */ | 355 | | | 356 | 259k | if (require_digitset == 0) | 357 | 35.2k | require_digitset = digitset; | 358 | 223k | else if (digitset != require_digitset) | 359 | 1.94k | return FALSE; | 360 | 259k | } // End digit handling | 361 | | | 362 | | /* If we haven't yet got to the end, pick up the next character. */ | 363 | | | 364 | 2.42M | if (ptr >= endptr) | 365 | 126k | return TRUE; | 366 | 2.30M | GETCHARINCTEST(c, ptr); | 367 | 2.30M | } // End checking loop | 368 | | | 369 | | #else /* NOT SUPPORT_UNICODE */ | 370 | | (void)ptr; | 371 | | (void)endptr; | 372 | | (void)utf; | 373 | | return TRUE; | 374 | | #endif /* SUPPORT_UNICODE */ | 375 | 186k | } |
Line | Count | Source | 86 | 64.8k | { | 87 | 64.8k | #ifdef SUPPORT_UNICODE | 88 | 64.8k | uint32_t require_state = SCRIPT_UNSET; | 89 | 64.8k | uint32_t require_map[FULL_MAPSIZE]; | 90 | 64.8k | uint32_t map[FULL_MAPSIZE]; | 91 | 64.8k | uint32_t require_digitset = 0; | 92 | 64.8k | uint32_t c; | 93 | | | 94 | 64.8k | #if PCRE2_CODE_UNIT_WIDTH == 32 | 95 | 64.8k | (void)utf; // Avoid compiler warning | 96 | 64.8k | #endif | 97 | | | 98 | | /* Any string containing fewer than 2 characters is a valid script run. */ | 99 | | | 100 | 64.8k | if (ptr >= endptr) | 101 | 21.9k | return TRUE; | 102 | 42.9k | GETCHARINCTEST(c, ptr); | 103 | 42.9k | if (ptr >= endptr) | 104 | 4.70k | return TRUE; | 105 | | | 106 | | /* Initialize the require map. This is a full-size bitmap that has a bit for | 107 | | every script, as opposed to the maps in ucd_script_sets, which only have bits | 108 | | for scripts less than ucp_Unknown - those that appear in script extension | 109 | | lists. */ | 110 | | | 111 | 267k | for (int i = 0; i < FULL_MAPSIZE; i++) | 112 | 229k | require_map[i] = 0; | 113 | | | 114 | | /* Scan strings of two or more characters, checking the Unicode characteristics | 115 | | of each code point. There is special code for scripts that can be combined with | 116 | | characters from the Han Chinese script. This may be used in conjunction with | 117 | | four other scripts in these combinations: | 118 | | | 119 | | . Han with Hiragana and Katakana is allowed (for Japanese). | 120 | | . Han with Bopomofo is allowed (for Taiwanese Mandarin). | 121 | | . Han with Hangul is allowed (for Korean). | 122 | | | 123 | | If the first significant character's script is one of the four, the required | 124 | | script type is immediately known. However, if the first significant | 125 | | character's script is Han, we have to keep checking for a non-Han character. | 126 | | Hence the SCRIPT_HANPENDING state. */ | 127 | | | 128 | 38.2k | for (;;) | 129 | 171k | { | 130 | 171k | const ucd_record *ucd = GET_UCD(c); | 131 | 171k | uint32_t script = ucd->script; | 132 | | | 133 | | /* If the script is Unknown, the string is not a valid script run. Such | 134 | | characters can only form script runs of length one (see test above). */ | 135 | | | 136 | 171k | if (script == ucp_Unknown) | 137 | 13.4k | return FALSE; | 138 | | | 139 | | /* A character without any script extensions whose script is Inherited or | 140 | | Common is always accepted with any script. If there are extensions, the | 141 | | following processing happens for all scripts. */ | 142 | | | 143 | 157k | if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common)) | 144 | 77.6k | { | 145 | 77.6k | BOOL OK; | 146 | | | 147 | | /* Set up a full-sized map for this character that can include bits for all | 148 | | scripts. Copy the scriptx map for this character (which covers those | 149 | | scripts that appear in script extension lists), set the remaining values to | 150 | | zero, and then, except for Common or Inherited, add this script's bit to | 151 | | the map. */ | 152 | | | 153 | 77.6k | memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t)); | 154 | 77.6k | memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t)); | 155 | 77.6k | if (script != ucp_Common && script != ucp_Inherited) | 156 | 69.6k | MAPSET(map, script); | 157 | | | 158 | | /* Handle the different checking states */ | 159 | | | 160 | 77.6k | switch (require_state) | 161 | 77.6k | { | 162 | | /* First significant character - it might follow Common or Inherited | 163 | | characters that do not have any script extensions. */ | 164 | | | 165 | 32.8k | case SCRIPT_UNSET: | 166 | 32.8k | switch (script) | 167 | 32.8k | { | 168 | 7.28k | case ucp_Han: | 169 | 7.28k | require_state = SCRIPT_HANPENDING; | 170 | 7.28k | break; | 171 | | | 172 | 870 | case ucp_Hiragana: | 173 | 1.93k | case ucp_Katakana: | 174 | 1.93k | require_state = SCRIPT_HANHIRAKATA; | 175 | 1.93k | break; | 176 | | | 177 | 1.45k | case ucp_Bopomofo: | 178 | 1.45k | require_state = SCRIPT_HANBOPOMOFO; | 179 | 1.45k | break; | 180 | | | 181 | 1.32k | case ucp_Hangul: | 182 | 1.32k | require_state = SCRIPT_HANHANGUL; | 183 | 1.32k | break; | 184 | | | 185 | 20.8k | default: | 186 | 20.8k | memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t)); | 187 | 20.8k | require_state = SCRIPT_MAP; | 188 | 20.8k | break; | 189 | 32.8k | } | 190 | | | 191 | 32.8k | break; | 192 | | | 193 | | /* The first significant character was Han. An inspection of the Unicode | 194 | | 11.0.0 files shows that there are the following types of Script Extension | 195 | | list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul | 196 | | scripts: | 197 | | | 198 | | . Bopomofo + Han | 199 | | . Han + Hiragana + Katakana | 200 | | . Hiragana + Katakana | 201 | | . Bopopmofo + Hangul + Han + Hiragana + Katakana | 202 | | | 203 | | The following code tries to make sense of this. */ | 204 | | | 205 | 32.8k | #define FOUND_BOPOMOFO 1 | 206 | 32.8k | #define FOUND_HIRAGANA 2 | 207 | 32.8k | #define FOUND_KATAKANA 4 | 208 | 32.8k | #define FOUND_HANGUL 8 | 209 | | | 210 | 32.8k | case SCRIPT_HANPENDING: | 211 | 7.69k | if (script != ucp_Han) // Another Han does nothing | 212 | 5.40k | { | 213 | 5.40k | uint32_t chspecial = 0; | 214 | | | 215 | 5.40k | if (MAPBIT(map, ucp_Bopomofo) != 0) | 216 | 1.44k | chspecial |= FOUND_BOPOMOFO; | 217 | 5.40k | if (MAPBIT(map, ucp_Hiragana) != 0) | 218 | 1.19k | chspecial |= FOUND_HIRAGANA; | 219 | 5.40k | if (MAPBIT(map, ucp_Katakana) != 0) | 220 | 1.46k | chspecial |= FOUND_KATAKANA; | 221 | 5.40k | if (MAPBIT(map, ucp_Hangul) != 0) | 222 | 1.29k | chspecial |= FOUND_HANGUL; | 223 | | | 224 | 5.40k | if (chspecial == 0) | 225 | 1.72k | return FALSE; // Not allowed with Han | 226 | | | 227 | 3.68k | if (chspecial == FOUND_BOPOMOFO) | 228 | 1.17k | require_state = SCRIPT_HANBOPOMOFO; | 229 | 2.51k | else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA)) | 230 | 906 | require_state = SCRIPT_HANHIRAKATA; | 231 | | | 232 | | /* Otherwise this character must be allowed with all of them, so remain | 233 | | in the pending state. */ | 234 | 3.68k | } | 235 | | | 236 | 5.97k | break; | 237 | | | 238 | | /* Previously encountered one of the "with Han" scripts. Check that | 239 | | this character is appropriate. */ | 240 | | | 241 | 5.97k | case SCRIPT_HANHIRAKATA: | 242 | 4.38k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0) | 243 | 2.61k | return FALSE; | 244 | 1.76k | break; | 245 | | | 246 | 4.26k | case SCRIPT_HANBOPOMOFO: | 247 | 4.26k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0) | 248 | 2.32k | return FALSE; | 249 | 1.94k | break; | 250 | | | 251 | 2.17k | case SCRIPT_HANHANGUL: | 252 | 2.17k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0) | 253 | 1.35k | return FALSE; | 254 | 819 | break; | 255 | | | 256 | | /* Previously encountered one or more characters that are allowed with a | 257 | | list of scripts. */ | 258 | | | 259 | 26.2k | case SCRIPT_MAP: | 260 | 26.2k | OK = FALSE; | 261 | | | 262 | 66.7k | for (int i = 0; i < FULL_MAPSIZE; i++) | 263 | 60.7k | { | 264 | 60.7k | if ((require_map[i] & map[i]) != 0) | 265 | 20.2k | { | 266 | 20.2k | OK = TRUE; | 267 | 20.2k | break; | 268 | 20.2k | } | 269 | 60.7k | } | 270 | | | 271 | 26.2k | if (!OK) | 272 | 6.01k | return FALSE; | 273 | | | 274 | | /* The rest of the string must be in this script, but we have to | 275 | | allow for the Han complications. */ | 276 | | | 277 | 20.2k | switch (script) | 278 | 20.2k | { | 279 | 718 | case ucp_Han: | 280 | 718 | require_state = SCRIPT_HANPENDING; | 281 | 718 | break; | 282 | | | 283 | 878 | case ucp_Hiragana: | 284 | 1.66k | case ucp_Katakana: | 285 | 1.66k | require_state = SCRIPT_HANHIRAKATA; | 286 | 1.66k | break; | 287 | | | 288 | 609 | case ucp_Bopomofo: | 289 | 609 | require_state = SCRIPT_HANBOPOMOFO; | 290 | 609 | break; | 291 | | | 292 | 618 | case ucp_Hangul: | 293 | 618 | require_state = SCRIPT_HANHANGUL; | 294 | 618 | break; | 295 | | | 296 | | /* Compute the intersection of the required list of scripts and the | 297 | | allowed scripts for this character. */ | 298 | | | 299 | 16.6k | default: | 300 | 116k | for (int i = 0; i < FULL_MAPSIZE; i++) | 301 | 99.9k | require_map[i] &= map[i]; | 302 | 16.6k | break; | 303 | 20.2k | } | 304 | | | 305 | 20.2k | break; | 306 | | | 307 | | /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */ | 308 | 20.2k | default: | 309 | 0 | PCRE2_DEBUG_UNREACHABLE(); | 310 | 0 | break; | 311 | | /* LCOV_EXCL_STOP */ | 312 | 77.6k | } | 313 | 77.6k | } // End checking character's script and extensions. | 314 | | | 315 | | /* The character is in an acceptable script. We must now ensure that all | 316 | | decimal digits in the string come from the same set. Some scripts (e.g. | 317 | | Common, Arabic) have more than one set of decimal digits. This code does | 318 | | not allow mixing sets, even within the same script. The vector called | 319 | | PRIV(ucd_digit_sets)[] contains, in its first element, the number of | 320 | | following elements, and then, in ascending order, the code points of the | 321 | | '9' characters in every set of 10 digits. Each set is identified by the | 322 | | offset in the vector of its '9' character. An initial check of the first | 323 | | value picks up ASCII digits quickly. Otherwise, a binary chop is used. */ | 324 | | | 325 | 143k | if (ucd->chartype == ucp_Nd) | 326 | 4.84k | { | 327 | 4.84k | uint32_t digitset; | 328 | | | 329 | 4.84k | if (c <= PRIV(ucd_digit_sets)[1]) | 330 | 2.04k | { | 331 | 2.04k | digitset = 1; | 332 | 2.04k | } | 333 | 2.80k | else | 334 | 2.80k | { | 335 | 2.80k | int mid; | 336 | 2.80k | int bot = 1; | 337 | 2.80k | int top = PRIV(ucd_digit_sets)[0]; | 338 | 2.80k | for (;;) | 339 | 20.4k | { | 340 | 20.4k | if (top <= bot + 1) // <= rather than == is paranoia | 341 | 2.80k | { | 342 | 2.80k | digitset = top; | 343 | 2.80k | break; | 344 | 2.80k | } | 345 | | | 346 | 17.6k | mid = (top + bot) / 2; | 347 | 17.6k | if (c <= PRIV(ucd_digit_sets)[mid]) | 348 | 6.62k | top = mid; | 349 | 11.0k | else | 350 | 11.0k | bot = mid; | 351 | 17.6k | } | 352 | 2.80k | } | 353 | | | 354 | | /* A required value of 0 means "unset". */ | 355 | | | 356 | 4.84k | if (require_digitset == 0) | 357 | 3.29k | require_digitset = digitset; | 358 | 1.54k | else if (digitset != require_digitset) | 359 | 732 | return FALSE; | 360 | 4.84k | } // End digit handling | 361 | | | 362 | | /* If we haven't yet got to the end, pick up the next character. */ | 363 | | | 364 | 142k | if (ptr >= endptr) | 365 | 10.0k | return TRUE; | 366 | 132k | GETCHARINCTEST(c, ptr); | 367 | 132k | } // End checking loop | 368 | | | 369 | | #else /* NOT SUPPORT_UNICODE */ | 370 | | (void)ptr; | 371 | | (void)endptr; | 372 | | (void)utf; | 373 | | return TRUE; | 374 | | #endif /* SUPPORT_UNICODE */ | 375 | 38.2k | } |
Line | Count | Source | 86 | 348k | { | 87 | 348k | #ifdef SUPPORT_UNICODE | 88 | 348k | uint32_t require_state = SCRIPT_UNSET; | 89 | 348k | uint32_t require_map[FULL_MAPSIZE]; | 90 | 348k | uint32_t map[FULL_MAPSIZE]; | 91 | 348k | uint32_t require_digitset = 0; | 92 | 348k | uint32_t c; | 93 | | | 94 | | #if PCRE2_CODE_UNIT_WIDTH == 32 | 95 | | (void)utf; // Avoid compiler warning | 96 | | #endif | 97 | | | 98 | | /* Any string containing fewer than 2 characters is a valid script run. */ | 99 | | | 100 | 348k | if (ptr >= endptr) | 101 | 89.3k | return TRUE; | 102 | 258k | GETCHARINCTEST(c, ptr); | 103 | 258k | if (ptr >= endptr) | 104 | 14.2k | return TRUE; | 105 | | | 106 | | /* Initialize the require map. This is a full-size bitmap that has a bit for | 107 | | every script, as opposed to the maps in ucd_script_sets, which only have bits | 108 | | for scripts less than ucp_Unknown - those that appear in script extension | 109 | | lists. */ | 110 | | | 111 | 1.71M | for (int i = 0; i < FULL_MAPSIZE; i++) | 112 | 1.46M | require_map[i] = 0; | 113 | | | 114 | | /* Scan strings of two or more characters, checking the Unicode characteristics | 115 | | of each code point. There is special code for scripts that can be combined with | 116 | | characters from the Han Chinese script. This may be used in conjunction with | 117 | | four other scripts in these combinations: | 118 | | | 119 | | . Han with Hiragana and Katakana is allowed (for Japanese). | 120 | | . Han with Bopomofo is allowed (for Taiwanese Mandarin). | 121 | | . Han with Hangul is allowed (for Korean). | 122 | | | 123 | | If the first significant character's script is one of the four, the required | 124 | | script type is immediately known. However, if the first significant | 125 | | character's script is Han, we have to keep checking for a non-Han character. | 126 | | Hence the SCRIPT_HANPENDING state. */ | 127 | | | 128 | 244k | for (;;) | 129 | 1.66M | { | 130 | 1.66M | const ucd_record *ucd = GET_UCD(c); | 131 | 1.66M | uint32_t script = ucd->script; | 132 | | | 133 | | /* If the script is Unknown, the string is not a valid script run. Such | 134 | | characters can only form script runs of length one (see test above). */ | 135 | | | 136 | 1.66M | if (script == ucp_Unknown) | 137 | 47.3k | return FALSE; | 138 | | | 139 | | /* A character without any script extensions whose script is Inherited or | 140 | | Common is always accepted with any script. If there are extensions, the | 141 | | following processing happens for all scripts. */ | 142 | | | 143 | 1.61M | if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common)) | 144 | 579k | { | 145 | 579k | BOOL OK; | 146 | | | 147 | | /* Set up a full-sized map for this character that can include bits for all | 148 | | scripts. Copy the scriptx map for this character (which covers those | 149 | | scripts that appear in script extension lists), set the remaining values to | 150 | | zero, and then, except for Common or Inherited, add this script's bit to | 151 | | the map. */ | 152 | | | 153 | 579k | memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t)); | 154 | 579k | memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t)); | 155 | 579k | if (script != ucp_Common && script != ucp_Inherited) | 156 | 509k | MAPSET(map, script); | 157 | | | 158 | | /* Handle the different checking states */ | 159 | | | 160 | 579k | switch (require_state) | 161 | 579k | { | 162 | | /* First significant character - it might follow Common or Inherited | 163 | | characters that do not have any script extensions. */ | 164 | | | 165 | 204k | case SCRIPT_UNSET: | 166 | 204k | switch (script) | 167 | 204k | { | 168 | 48.4k | case ucp_Han: | 169 | 48.4k | require_state = SCRIPT_HANPENDING; | 170 | 48.4k | break; | 171 | | | 172 | 2.13k | case ucp_Hiragana: | 173 | 5.50k | case ucp_Katakana: | 174 | 5.50k | require_state = SCRIPT_HANHIRAKATA; | 175 | 5.50k | break; | 176 | | | 177 | 6.14k | case ucp_Bopomofo: | 178 | 6.14k | require_state = SCRIPT_HANBOPOMOFO; | 179 | 6.14k | break; | 180 | | | 181 | 11.2k | case ucp_Hangul: | 182 | 11.2k | require_state = SCRIPT_HANHANGUL; | 183 | 11.2k | break; | 184 | | | 185 | 132k | default: | 186 | 132k | memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t)); | 187 | 132k | require_state = SCRIPT_MAP; | 188 | 132k | break; | 189 | 204k | } | 190 | | | 191 | 204k | break; | 192 | | | 193 | | /* The first significant character was Han. An inspection of the Unicode | 194 | | 11.0.0 files shows that there are the following types of Script Extension | 195 | | list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul | 196 | | scripts: | 197 | | | 198 | | . Bopomofo + Han | 199 | | . Han + Hiragana + Katakana | 200 | | . Hiragana + Katakana | 201 | | . Bopopmofo + Hangul + Han + Hiragana + Katakana | 202 | | | 203 | | The following code tries to make sense of this. */ | 204 | | | 205 | 204k | #define FOUND_BOPOMOFO 1 | 206 | 204k | #define FOUND_HIRAGANA 2 | 207 | 204k | #define FOUND_KATAKANA 4 | 208 | 204k | #define FOUND_HANGUL 8 | 209 | | | 210 | 204k | case SCRIPT_HANPENDING: | 211 | 137k | if (script != ucp_Han) // Another Han does nothing | 212 | 60.5k | { | 213 | 60.5k | uint32_t chspecial = 0; | 214 | | | 215 | 60.5k | if (MAPBIT(map, ucp_Bopomofo) != 0) | 216 | 14.8k | chspecial |= FOUND_BOPOMOFO; | 217 | 60.5k | if (MAPBIT(map, ucp_Hiragana) != 0) | 218 | 21.8k | chspecial |= FOUND_HIRAGANA; | 219 | 60.5k | if (MAPBIT(map, ucp_Katakana) != 0) | 220 | 26.6k | chspecial |= FOUND_KATAKANA; | 221 | 60.5k | if (MAPBIT(map, ucp_Hangul) != 0) | 222 | 21.7k | chspecial |= FOUND_HANGUL; | 223 | | | 224 | 60.5k | if (chspecial == 0) | 225 | 21.7k | return FALSE; // Not allowed with Han | 226 | | | 227 | 38.7k | if (chspecial == FOUND_BOPOMOFO) | 228 | 1.35k | require_state = SCRIPT_HANBOPOMOFO; | 229 | 37.4k | else if (chspecial == (FOUND_HIRAGANA | FOUND_KATAKANA)) | 230 | 5.80k | require_state = SCRIPT_HANHIRAKATA; | 231 | | | 232 | | /* Otherwise this character must be allowed with all of them, so remain | 233 | | in the pending state. */ | 234 | 38.7k | } | 235 | | | 236 | 115k | break; | 237 | | | 238 | | /* Previously encountered one of the "with Han" scripts. Check that | 239 | | this character is appropriate. */ | 240 | | | 241 | 115k | case SCRIPT_HANHIRAKATA: | 242 | 29.4k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) + MAPBIT(map, ucp_Katakana) == 0) | 243 | 9.81k | return FALSE; | 244 | 19.6k | break; | 245 | | | 246 | 19.6k | case SCRIPT_HANBOPOMOFO: | 247 | 19.2k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0) | 248 | 7.36k | return FALSE; | 249 | 11.8k | break; | 250 | | | 251 | 19.9k | case SCRIPT_HANHANGUL: | 252 | 19.9k | if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0) | 253 | 9.09k | return FALSE; | 254 | 10.8k | break; | 255 | | | 256 | | /* Previously encountered one or more characters that are allowed with a | 257 | | list of scripts. */ | 258 | | | 259 | 169k | case SCRIPT_MAP: | 260 | 169k | OK = FALSE; | 261 | | | 262 | 583k | for (int i = 0; i < FULL_MAPSIZE; i++) | 263 | 515k | { | 264 | 515k | if ((require_map[i] & map[i]) != 0) | 265 | 101k | { | 266 | 101k | OK = TRUE; | 267 | 101k | break; | 268 | 101k | } | 269 | 515k | } | 270 | | | 271 | 169k | if (!OK) | 272 | 67.5k | return FALSE; | 273 | | | 274 | | /* The rest of the string must be in this script, but we have to | 275 | | allow for the Han complications. */ | 276 | | | 277 | 101k | switch (script) | 278 | 101k | { | 279 | 3.63k | case ucp_Han: | 280 | 3.63k | require_state = SCRIPT_HANPENDING; | 281 | 3.63k | break; | 282 | | | 283 | 955 | case ucp_Hiragana: | 284 | 2.42k | case ucp_Katakana: | 285 | 2.42k | require_state = SCRIPT_HANHIRAKATA; | 286 | 2.42k | break; | 287 | | | 288 | 3.41k | case ucp_Bopomofo: | 289 | 3.41k | require_state = SCRIPT_HANBOPOMOFO; | 290 | 3.41k | break; | 291 | | | 292 | 914 | case ucp_Hangul: | 293 | 914 | require_state = SCRIPT_HANHANGUL; | 294 | 914 | break; | 295 | | | 296 | | /* Compute the intersection of the required list of scripts and the | 297 | | allowed scripts for this character. */ | 298 | | | 299 | 91.5k | default: | 300 | 641k | for (int i = 0; i < FULL_MAPSIZE; i++) | 301 | 549k | require_map[i] &= map[i]; | 302 | 91.5k | break; | 303 | 101k | } | 304 | | | 305 | 101k | break; | 306 | | | 307 | | /* LCOV_EXCL_START - require_state is assigned only SCRIPT_* values. */ | 308 | 101k | default: | 309 | 0 | PCRE2_DEBUG_UNREACHABLE(); | 310 | 0 | break; | 311 | | /* LCOV_EXCL_STOP */ | 312 | 579k | } | 313 | 579k | } // End checking character's script and extensions. | 314 | | | 315 | | /* The character is in an acceptable script. We must now ensure that all | 316 | | decimal digits in the string come from the same set. Some scripts (e.g. | 317 | | Common, Arabic) have more than one set of decimal digits. This code does | 318 | | not allow mixing sets, even within the same script. The vector called | 319 | | PRIV(ucd_digit_sets)[] contains, in its first element, the number of | 320 | | following elements, and then, in ascending order, the code points of the | 321 | | '9' characters in every set of 10 digits. Each set is identified by the | 322 | | offset in the vector of its '9' character. An initial check of the first | 323 | | value picks up ASCII digits quickly. Otherwise, a binary chop is used. */ | 324 | | | 325 | 1.50M | if (ucd->chartype == ucp_Nd) | 326 | 23.5k | { | 327 | 23.5k | uint32_t digitset; | 328 | | | 329 | 23.5k | if (c <= PRIV(ucd_digit_sets)[1]) | 330 | 10.7k | { | 331 | 10.7k | digitset = 1; | 332 | 10.7k | } | 333 | 12.8k | else | 334 | 12.8k | { | 335 | 12.8k | int mid; | 336 | 12.8k | int bot = 1; | 337 | 12.8k | int top = PRIV(ucd_digit_sets)[0]; | 338 | 12.8k | for (;;) | 339 | 89.9k | { | 340 | 89.9k | if (top <= bot + 1) // <= rather than == is paranoia | 341 | 12.8k | { | 342 | 12.8k | digitset = top; | 343 | 12.8k | break; | 344 | 12.8k | } | 345 | | | 346 | 77.1k | mid = (top + bot) / 2; | 347 | 77.1k | if (c <= PRIV(ucd_digit_sets)[mid]) | 348 | 44.6k | top = mid; | 349 | 32.5k | else | 350 | 32.5k | bot = mid; | 351 | 77.1k | } | 352 | 12.8k | } | 353 | | | 354 | | /* A required value of 0 means "unset". */ | 355 | | | 356 | 23.5k | if (require_digitset == 0) | 357 | 15.4k | require_digitset = digitset; | 358 | 8.16k | else if (digitset != require_digitset) | 359 | 3.88k | return FALSE; | 360 | 23.5k | } // End digit handling | 361 | | | 362 | | /* If we haven't yet got to the end, pick up the next character. */ | 363 | | | 364 | 1.49M | if (ptr >= endptr) | 365 | 77.5k | return TRUE; | 366 | 1.42M | GETCHARINCTEST(c, ptr); | 367 | 1.42M | } // End checking loop | 368 | | | 369 | | #else /* NOT SUPPORT_UNICODE */ | 370 | | (void)ptr; | 371 | | (void)endptr; | 372 | | (void)utf; | 373 | | return TRUE; | 374 | | #endif /* SUPPORT_UNICODE */ | 375 | 244k | } |
|
376 | | |
377 | | /* End of pcre2_script_run.c */ |