Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : * unicode_norm.c
3 : : * Normalize a Unicode string
4 : : *
5 : : * This implements Unicode normalization, per the documentation at
6 : : * https://www.unicode.org/reports/tr15/.
7 : : *
8 : : * Portions Copyright (c) 2017-2026, PostgreSQL Global Development Group
9 : : *
10 : : * IDENTIFICATION
11 : : * src/common/unicode_norm.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #ifndef FRONTEND
16 : : #include "postgres.h"
17 : : #else
18 : : #include "postgres_fe.h"
19 : : #endif
20 : :
21 : : #include "common/unicode_norm.h"
22 : : #ifndef FRONTEND
23 : : #include "common/unicode_norm_hashfunc.h"
24 : : #include "common/unicode_normprops_table.h"
25 : : #include "port/pg_bswap.h"
26 : : #include "utils/memutils.h"
27 : : #else
28 : : #include "common/unicode_norm_table.h"
29 : : #endif
30 : :
31 : : #ifndef FRONTEND
32 : : #define ALLOC(size) palloc(size)
33 : : #define FREE(size) pfree(size)
34 : : #else
35 : : #define ALLOC(size) malloc(size)
36 : : #define FREE(size) free(size)
37 : : #endif
38 : :
39 : : /* Constants for calculations with Hangul characters */
40 : : #define SBASE 0xAC00 /* U+AC00 */
41 : : #define LBASE 0x1100 /* U+1100 */
42 : : #define VBASE 0x1161 /* U+1161 */
43 : : #define TBASE 0x11A7 /* U+11A7 */
44 : : #define LCOUNT 19
45 : : #define VCOUNT 21
46 : : #define TCOUNT 28
47 : : #define NCOUNT VCOUNT * TCOUNT
48 : : #define SCOUNT LCOUNT * NCOUNT
49 : :
50 : : #ifdef FRONTEND
51 : : /* comparison routine for bsearch() of decomposition lookup table. */
52 : : static int
53 : 14699 : conv_compare(const void *p1, const void *p2)
54 : : {
55 : : uint32 v1,
56 : : v2;
57 : :
58 : 14699 : v1 = *(const uint32 *) p1;
59 : 14699 : v2 = ((const pg_unicode_decomposition *) p2)->codepoint;
60 [ + + + + ]: 14699 : return (v1 > v2) ? 1 : ((v1 == v2) ? 0 : -1);
61 : : }
62 : :
63 : : #endif
64 : :
65 : : /*
66 : : * get_code_entry
67 : : *
68 : : * Get the entry corresponding to code in the decomposition lookup table.
69 : : * The backend version of this code uses a perfect hash function for the
70 : : * lookup, while the frontend version uses a binary search.
71 : : */
72 : : static const pg_unicode_decomposition *
73 : 14849 : get_code_entry(char32_t code)
74 : : {
75 : : #ifndef FRONTEND
76 : : int h;
77 : : uint32 hashkey;
78 : 13718 : pg_unicode_decompinfo decompinfo = UnicodeDecompInfo;
79 : :
80 : : /*
81 : : * Compute the hash function. The hash key is the codepoint with the bytes
82 : : * in network order.
83 : : */
84 : 13718 : hashkey = pg_hton32(code);
85 : 13718 : h = decompinfo.hash(&hashkey);
86 : :
87 : : /* An out-of-range result implies no match */
88 [ + - + + ]: 13718 : if (h < 0 || h >= decompinfo.num_decomps)
89 : 3496 : return NULL;
90 : :
91 : : /*
92 : : * Since it's a perfect hash, we need only match to the specific codepoint
93 : : * it identifies.
94 : : */
95 [ + + ]: 10222 : if (code != decompinfo.decomps[h].codepoint)
96 : 9516 : return NULL;
97 : :
98 : : /* Success! */
99 : 706 : return &decompinfo.decomps[h];
100 : : #else
101 : 1131 : return bsearch(&(code),
102 : : UnicodeDecompMain,
103 : : lengthof(UnicodeDecompMain),
104 : : sizeof(pg_unicode_decomposition),
105 : : conv_compare);
106 : : #endif
107 : : }
108 : :
109 : : /*
110 : : * Get the combining class of the given codepoint.
111 : : */
112 : : static uint8
113 : 8391 : get_canonical_class(char32_t code)
114 : : {
115 : 8391 : const pg_unicode_decomposition *entry = get_code_entry(code);
116 : :
117 : : /*
118 : : * If no entries are found, the character used is either a Hangul
119 : : * character or a character with a class of 0 and no decompositions.
120 : : */
121 [ + + ]: 8391 : if (!entry)
122 : 8051 : return 0;
123 : : else
124 : 340 : return entry->comb_class;
125 : : }
126 : :
127 : : /*
128 : : * Given a decomposition entry looked up earlier, get the decomposed
129 : : * characters.
130 : : *
131 : : * Note: the returned pointer can point to statically allocated buffer, and
132 : : * is only valid until next call to this function!
133 : : */
134 : : static const char32_t *
135 : 138 : get_code_decomposition(const pg_unicode_decomposition *entry, int *dec_size)
136 : : {
137 : : static char32_t x;
138 : :
139 [ + + ]: 138 : if (DECOMPOSITION_IS_INLINE(entry))
140 : : {
141 : : Assert(DECOMPOSITION_SIZE(entry) == 1);
142 : 42 : x = (char32_t) entry->dec_index;
143 : 42 : *dec_size = 1;
144 : 42 : return &x;
145 : : }
146 : : else
147 : : {
148 : 96 : *dec_size = DECOMPOSITION_SIZE(entry);
149 : 96 : return &UnicodeDecomp_codepoints[entry->dec_index];
150 : : }
151 : : }
152 : :
153 : : /*
154 : : * Calculate how many characters a given character will decompose to.
155 : : *
156 : : * This needs to recurse, if the character decomposes into characters that
157 : : * are, in turn, decomposable.
158 : : */
159 : : static int
160 : 3264 : get_decomposed_size(char32_t code, bool compat)
161 : : {
162 : : const pg_unicode_decomposition *entry;
163 : 3264 : int size = 0;
164 : : int i;
165 : : const uint32 *decomp;
166 : : int dec_size;
167 : :
168 : : /*
169 : : * Fast path for Hangul characters not stored in tables to save memory as
170 : : * decomposition is algorithmic. See
171 : : * https://www.unicode.org/reports/tr15/tr15-18.html, annex 10 for details
172 : : * on the matter.
173 : : */
174 [ + + + - ]: 3264 : if (code >= SBASE && code < SBASE + SCOUNT)
175 : : {
176 : : uint32 tindex,
177 : : sindex;
178 : :
179 : 35 : sindex = code - SBASE;
180 : 35 : tindex = sindex % TCOUNT;
181 : :
182 [ + + ]: 35 : if (tindex != 0)
183 : 10 : return 3;
184 : 25 : return 2;
185 : : }
186 : :
187 : 3229 : entry = get_code_entry(code);
188 : :
189 : : /*
190 : : * Just count current code if no other decompositions. A NULL entry is
191 : : * equivalent to a character with class 0 and no decompositions.
192 : : */
193 [ + + + + ]: 3229 : if (entry == NULL || DECOMPOSITION_SIZE(entry) == 0 ||
194 [ + + + + ]: 96 : (!compat && DECOMPOSITION_IS_COMPAT(entry)))
195 : 3160 : return 1;
196 : :
197 : : /*
198 : : * If this entry has other decomposition codes look at them as well. First
199 : : * get its decomposition in the list of tables available.
200 : : */
201 : 69 : decomp = get_code_decomposition(entry, &dec_size);
202 [ + + ]: 186 : for (i = 0; i < dec_size; i++)
203 : : {
204 : 117 : uint32 lcode = decomp[i];
205 : :
206 : 117 : size += get_decomposed_size(lcode, compat);
207 : : }
208 : :
209 : 69 : return size;
210 : : }
211 : :
212 : : /*
213 : : * Recompose a set of characters. For hangul characters, the calculation
214 : : * is algorithmic. For others, an inverse lookup at the decomposition
215 : : * table is necessary. Returns true if a recomposition can be done, and
216 : : * false otherwise.
217 : : */
218 : : static bool
219 : 2609 : recompose_code(uint32 start, uint32 code, uint32 *result)
220 : : {
221 : : /*
222 : : * Handle Hangul characters algorithmically, per the Unicode spec.
223 : : *
224 : : * Check if two current characters are L and V.
225 : : */
226 [ + + + + : 2609 : if (start >= LBASE && start < LBASE + LCOUNT &&
+ - ]
227 [ + - ]: 45 : code >= VBASE && code < VBASE + VCOUNT)
228 : : {
229 : : /* make syllable of form LV */
230 : 45 : uint32 lindex = start - LBASE;
231 : 45 : uint32 vindex = code - VBASE;
232 : :
233 : 45 : *result = SBASE + (lindex * VCOUNT + vindex) * TCOUNT;
234 : 45 : return true;
235 : : }
236 : : /* Check if two current characters are LV and T */
237 [ + + + - ]: 2564 : else if (start >= SBASE && start < (SBASE + SCOUNT) &&
238 [ + - + + ]: 35 : ((start - SBASE) % TCOUNT) == 0 &&
239 [ + - ]: 25 : code > TBASE && code < (TBASE + TCOUNT))
240 : : {
241 : : /* make syllable of form LVT */
242 : 25 : uint32 tindex = code - TBASE;
243 : :
244 : 25 : *result = start + tindex;
245 : 25 : return true;
246 : : }
247 : : else
248 : : {
249 : : const pg_unicode_decomposition *entry;
250 : :
251 : : /*
252 : : * Do an inverse lookup of the decomposition tables to see if anything
253 : : * matches. The comparison just needs to be a perfect match on the
254 : : * sub-table of size two, because the start character has already been
255 : : * recomposed partially. This lookup uses a perfect hash function for
256 : : * the backend code.
257 : : */
258 : : #ifndef FRONTEND
259 : :
260 : : int h,
261 : : inv_lookup_index;
262 : : uint64 hashkey;
263 : 2340 : pg_unicode_recompinfo recompinfo = UnicodeRecompInfo;
264 : :
265 : : /*
266 : : * Compute the hash function. The hash key is formed by concatenating
267 : : * bytes of the two codepoints in network order. See also
268 : : * src/common/unicode/generate-unicode_norm_table.pl.
269 : : */
270 : 2340 : hashkey = pg_hton64(((uint64) start << 32) | (uint64) code);
271 : 2340 : h = recompinfo.hash(&hashkey);
272 : :
273 : : /* An out-of-range result implies no match */
274 [ + + + + ]: 2340 : if (h < 0 || h >= recompinfo.num_recomps)
275 : 1933 : return false;
276 : :
277 : 439 : inv_lookup_index = recompinfo.inverse_lookup[h];
278 : 439 : entry = &UnicodeDecompMain[inv_lookup_index];
279 : :
280 [ + + ]: 439 : if (start == UnicodeDecomp_codepoints[entry->dec_index] &&
281 [ + + ]: 36 : code == UnicodeDecomp_codepoints[entry->dec_index + 1])
282 : : {
283 : 32 : *result = entry->codepoint;
284 : 32 : return true;
285 : : }
286 : :
287 : : #else
288 : :
289 [ + + ]: 1368921 : for (size_t i = 0; i < lengthof(UnicodeDecompMain); i++)
290 : : {
291 : 1368722 : entry = &UnicodeDecompMain[i];
292 : :
293 [ + + ]: 1368722 : if (DECOMPOSITION_SIZE(entry) != 2)
294 : 1031616 : continue;
295 : :
296 [ + + ]: 337106 : if (DECOMPOSITION_NO_COMPOSE(entry))
297 : 145867 : continue;
298 : :
299 [ + + ]: 191239 : if (start == UnicodeDecomp_codepoints[entry->dec_index] &&
300 [ - + ]: 1718 : code == UnicodeDecomp_codepoints[entry->dec_index + 1])
301 : : {
302 : 0 : *result = entry->codepoint;
303 : 0 : return true;
304 : : }
305 : : }
306 : : #endif /* !FRONTEND */
307 : : }
308 : :
309 : 606 : return false;
310 : : }
311 : :
312 : : /*
313 : : * Decompose the given code into the array given by caller. The
314 : : * decomposition begins at the position given by caller, saving one
315 : : * lookup on the decomposition table. The current position needs to be
316 : : * updated here to let the caller know from where to continue filling
317 : : * in the array result.
318 : : */
319 : : static void
320 : 3264 : decompose_code(char32_t code, bool compat, char32_t **result, int *current)
321 : : {
322 : : const pg_unicode_decomposition *entry;
323 : : int i;
324 : : const uint32 *decomp;
325 : : int dec_size;
326 : :
327 : : /*
328 : : * Fast path for Hangul characters not stored in tables to save memory as
329 : : * decomposition is algorithmic. See
330 : : * https://www.unicode.org/reports/tr15/tr15-18.html, annex 10 for details
331 : : * on the matter.
332 : : */
333 [ + + + - ]: 3264 : if (code >= SBASE && code < SBASE + SCOUNT)
334 : : {
335 : : uint32 l,
336 : : v,
337 : : tindex,
338 : : sindex;
339 : 35 : char32_t *res = *result;
340 : :
341 : 35 : sindex = code - SBASE;
342 : 35 : l = LBASE + sindex / (VCOUNT * TCOUNT);
343 : 35 : v = VBASE + (sindex % (VCOUNT * TCOUNT)) / TCOUNT;
344 : 35 : tindex = sindex % TCOUNT;
345 : :
346 : 35 : res[*current] = l;
347 : 35 : (*current)++;
348 : 35 : res[*current] = v;
349 : 35 : (*current)++;
350 : :
351 [ + + ]: 35 : if (tindex != 0)
352 : : {
353 : 10 : res[*current] = TBASE + tindex;
354 : 10 : (*current)++;
355 : : }
356 : :
357 : 3195 : return;
358 : : }
359 : :
360 : 3229 : entry = get_code_entry(code);
361 : :
362 : : /*
363 : : * Just fill in with the current decomposition if there are no
364 : : * decomposition codes to recurse to. A NULL entry is equivalent to a
365 : : * character with class 0 and no decompositions, so just leave also in
366 : : * this case.
367 : : */
368 [ + + + + ]: 3229 : if (entry == NULL || DECOMPOSITION_SIZE(entry) == 0 ||
369 [ + + + + ]: 96 : (!compat && DECOMPOSITION_IS_COMPAT(entry)))
370 : : {
371 : 3160 : char32_t *res = *result;
372 : :
373 : 3160 : res[*current] = code;
374 : 3160 : (*current)++;
375 : 3160 : return;
376 : : }
377 : :
378 : : /*
379 : : * If this entry has other decomposition codes look at them as well.
380 : : */
381 : 69 : decomp = get_code_decomposition(entry, &dec_size);
382 [ + + ]: 186 : for (i = 0; i < dec_size; i++)
383 : : {
384 : 117 : char32_t lcode = (char32_t) decomp[i];
385 : :
386 : : /* Leave if no more decompositions */
387 : 117 : decompose_code(lcode, compat, result, current);
388 : : }
389 : : }
390 : :
391 : : /*
392 : : * unicode_normalize - Normalize a Unicode string to the specified form.
393 : : *
394 : : * The input is a 0-terminated array of codepoints.
395 : : *
396 : : * In frontend, returns a 0-terminated array of codepoints, allocated with
397 : : * malloc. Or NULL if we run out of memory. In backend, the returned
398 : : * string is palloc'd instead, and OOM is reported with ereport().
399 : : */
400 : : char32_t *
401 : 429 : unicode_normalize(UnicodeNormalizationForm form, const char32_t *input)
402 : : {
403 [ + + + + ]: 429 : bool compat = (form == UNICODE_NFKC || form == UNICODE_NFKD);
404 [ + + + + ]: 429 : bool recompose = (form == UNICODE_NFC || form == UNICODE_NFKC);
405 : : char32_t *decomp_chars;
406 : : char32_t *recomp_chars;
407 : : int decomp_size,
408 : : current_size;
409 : : int count;
410 : : const char32_t *p;
411 : :
412 : : /* variables for recomposition */
413 : : int last_class;
414 : : int starter_pos;
415 : : int target_pos;
416 : : uint32 starter_ch;
417 : :
418 : : /* First, do character decomposition */
419 : :
420 : : /*
421 : : * Calculate how many characters long the decomposed version will be.
422 : : *
423 : : * Some characters decompose to quite a few code points, so that the
424 : : * decomposed version's size could overrun MaxAllocSize, and even 32-bit
425 : : * size_t, even though the input string presumably fits in that. In
426 : : * frontend we want to just return NULL in that case, so monitor the sum
427 : : * and exit early once we'd need more than MaxAllocSize bytes.
428 : : */
429 : 429 : decomp_size = 0;
430 [ + + ]: 3576 : for (p = input; *p; p++)
431 : : {
432 : 3147 : decomp_size += get_decomposed_size(*p, compat);
433 [ - + ]: 3147 : if (unlikely(decomp_size > MaxAllocSize / sizeof(char32_t)))
434 : : {
435 : : #ifndef FRONTEND
436 : : /* Exit loop and let palloc() throw error below */
437 : 0 : break;
438 : : #else
439 : : /* Just return NULL with no explicit error */
440 : 0 : return NULL;
441 : : #endif
442 : : }
443 : : }
444 : :
445 : 429 : decomp_chars = (char32_t *) ALLOC((decomp_size + 1) * sizeof(char32_t));
446 [ - + ]: 429 : if (decomp_chars == NULL)
447 : 0 : return NULL;
448 : :
449 : : /*
450 : : * Now fill in each entry recursively. This needs a second pass on the
451 : : * decomposition table.
452 : : */
453 : 429 : current_size = 0;
454 [ + + ]: 3576 : for (p = input; *p; p++)
455 : 3147 : decompose_code(*p, compat, &decomp_chars, ¤t_size);
456 : 429 : decomp_chars[decomp_size] = '\0';
457 : : Assert(decomp_size == current_size);
458 : :
459 : : /* Leave if there is nothing to decompose */
460 [ + + ]: 429 : if (decomp_size == 0)
461 : 13 : return decomp_chars;
462 : :
463 : : /*
464 : : * Now apply canonical ordering.
465 : : */
466 [ + + ]: 3240 : for (count = 1; count < decomp_size; count++)
467 : : {
468 : 2824 : char32_t prev = decomp_chars[count - 1];
469 : 2824 : char32_t next = decomp_chars[count];
470 : : char32_t tmp;
471 : 2824 : const uint8 prevClass = get_canonical_class(prev);
472 : 2824 : const uint8 nextClass = get_canonical_class(next);
473 : :
474 : : /*
475 : : * Per Unicode (https://www.unicode.org/reports/tr15/tr15-18.html)
476 : : * annex 4, a sequence of two adjacent characters in a string is an
477 : : * exchangeable pair if the combining class (from the Unicode
478 : : * Character Database) for the first character is greater than the
479 : : * combining class for the second, and the second is not a starter. A
480 : : * character is a starter if its combining class is 0.
481 : : */
482 [ + + + - ]: 2824 : if (prevClass == 0 || nextClass == 0)
483 : 2824 : continue;
484 : :
485 [ # # ]: 0 : if (prevClass <= nextClass)
486 : 0 : continue;
487 : :
488 : : /* exchange can happen */
489 : 0 : tmp = decomp_chars[count - 1];
490 : 0 : decomp_chars[count - 1] = decomp_chars[count];
491 : 0 : decomp_chars[count] = tmp;
492 : :
493 : : /* backtrack to check again */
494 [ # # ]: 0 : if (count > 1)
495 : 0 : count -= 2;
496 : : }
497 : :
498 [ + + ]: 416 : if (!recompose)
499 : 73 : return decomp_chars;
500 : :
501 : : /*
502 : : * The last phase of NFC and NFKC is the recomposition of the reordered
503 : : * Unicode string using combining classes. The recomposed string cannot be
504 : : * longer than the decomposed one, so make the allocation of the output
505 : : * string based on that assumption.
506 : : */
507 : 343 : recomp_chars = (char32_t *) ALLOC((decomp_size + 1) * sizeof(char32_t));
508 [ - + ]: 343 : if (!recomp_chars)
509 : : {
510 : 0 : FREE(decomp_chars);
511 : 0 : return NULL;
512 : : }
513 : :
514 : 343 : last_class = -1; /* this eliminates a special check */
515 : 343 : starter_pos = 0;
516 : 343 : target_pos = 1;
517 : 343 : starter_ch = recomp_chars[0] = decomp_chars[0];
518 : :
519 [ + + ]: 2952 : for (count = 1; count < decomp_size; count++)
520 : : {
521 : 2609 : char32_t ch = decomp_chars[count];
522 : 2609 : int ch_class = get_canonical_class(ch);
523 : : char32_t composite;
524 : :
525 [ + - + + ]: 5218 : if (last_class < ch_class &&
526 : 2609 : recompose_code(starter_ch, ch, &composite))
527 : : {
528 : 102 : recomp_chars[starter_pos] = composite;
529 : 102 : starter_ch = composite;
530 : : }
531 [ + - ]: 2507 : else if (ch_class == 0)
532 : : {
533 : 2507 : starter_pos = target_pos;
534 : 2507 : starter_ch = ch;
535 : 2507 : last_class = -1;
536 : 2507 : recomp_chars[target_pos++] = ch;
537 : : }
538 : : else
539 : : {
540 : 0 : last_class = ch_class;
541 : 0 : recomp_chars[target_pos++] = ch;
542 : : }
543 : : }
544 : 343 : recomp_chars[target_pos] = (char32_t) '\0';
545 : :
546 : 343 : FREE(decomp_chars);
547 : :
548 : 343 : return recomp_chars;
549 : : }
550 : :
551 : : /*
552 : : * Normalization "quick check" algorithm; see
553 : : * <http://www.unicode.org/reports/tr15/#Detecting_Normalization_Forms>
554 : : */
555 : :
556 : : /* We only need this in the backend. */
557 : : #ifndef FRONTEND
558 : :
559 : : static const pg_unicode_normprops *
560 : 134 : qc_hash_lookup(char32_t ch, const pg_unicode_norminfo *norminfo)
561 : : {
562 : : int h;
563 : : uint32 hashkey;
564 : :
565 : : /*
566 : : * Compute the hash function. The hash key is the codepoint with the bytes
567 : : * in network order.
568 : : */
569 : 134 : hashkey = pg_hton32(ch);
570 : 134 : h = norminfo->hash(&hashkey);
571 : :
572 : : /* An out-of-range result implies no match */
573 [ + - + + ]: 134 : if (h < 0 || h >= norminfo->num_normprops)
574 : 92 : return NULL;
575 : :
576 : : /*
577 : : * Since it's a perfect hash, we need only match to the specific codepoint
578 : : * it identifies.
579 : : */
580 [ + + ]: 42 : if (ch != norminfo->normprops[h].codepoint)
581 : 18 : return NULL;
582 : :
583 : : /* Success! */
584 : 24 : return &norminfo->normprops[h];
585 : : }
586 : :
587 : : /*
588 : : * Look up the normalization quick check character property
589 : : */
590 : : static UnicodeNormalizationQC
591 : 134 : qc_is_allowed(UnicodeNormalizationForm form, char32_t ch)
592 : : {
593 : 134 : const pg_unicode_normprops *found = NULL;
594 : :
595 [ + + - ]: 134 : switch (form)
596 : : {
597 : 86 : case UNICODE_NFC:
598 : 86 : found = qc_hash_lookup(ch, &UnicodeNormInfo_NFC_QC);
599 : 86 : break;
600 : 48 : case UNICODE_NFKC:
601 : 48 : found = qc_hash_lookup(ch, &UnicodeNormInfo_NFKC_QC);
602 : 48 : break;
603 : 0 : default:
604 : : Assert(false);
605 : 0 : break;
606 : : }
607 : :
608 [ + + ]: 134 : if (found)
609 : 24 : return found->quickcheck;
610 : : else
611 : 110 : return UNICODE_NORM_QC_YES;
612 : : }
613 : :
614 : : UnicodeNormalizationQC
615 : 90 : unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const char32_t *input)
616 : : {
617 : 90 : uint8 lastCanonicalClass = 0;
618 : 90 : UnicodeNormalizationQC result = UNICODE_NORM_QC_YES;
619 : :
620 : : /*
621 : : * For the "D" forms, we don't run the quickcheck. We don't include the
622 : : * lookup tables for those because they are huge, checking for these
623 : : * particular forms is less common, and running the slow path is faster
624 : : * for the "D" forms than the "C" forms because you don't need to
625 : : * recompose, which is slow.
626 : : */
627 [ + + + + ]: 90 : if (form == UNICODE_NFD || form == UNICODE_NFKD)
628 : 40 : return UNICODE_NORM_QC_MAYBE;
629 : :
630 [ + + ]: 176 : for (const char32_t *p = input; *p; p++)
631 : : {
632 : 134 : char32_t ch = *p;
633 : : uint8 canonicalClass;
634 : : UnicodeNormalizationQC check;
635 : :
636 : 134 : canonicalClass = get_canonical_class(ch);
637 [ + + - + ]: 134 : if (lastCanonicalClass > canonicalClass && canonicalClass != 0)
638 : 0 : return UNICODE_NORM_QC_NO;
639 : :
640 : 134 : check = qc_is_allowed(form, ch);
641 [ + + ]: 134 : if (check == UNICODE_NORM_QC_NO)
642 : 8 : return UNICODE_NORM_QC_NO;
643 [ + + ]: 126 : else if (check == UNICODE_NORM_QC_MAYBE)
644 : 16 : result = UNICODE_NORM_QC_MAYBE;
645 : :
646 : 126 : lastCanonicalClass = canonicalClass;
647 : : }
648 : 42 : return result;
649 : : }
650 : :
651 : : #endif /* !FRONTEND */
|