PostgreSQL Source Code git master
Loading...
Searching...
No Matches
unicode_norm.c
Go to the documentation of this file.
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
25#include "port/pg_bswap.h"
26#include "utils/memutils.h"
27#else
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. */
52static int
53conv_compare(const void *p1, const void *p2)
54{
55 uint32 v1,
56 v2;
57
58 v1 = *(const uint32 *) p1;
59 v2 = ((const pg_unicode_decomposition *) p2)->codepoint;
60 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 */
72static const pg_unicode_decomposition *
73get_code_entry(char32_t code)
74{
75#ifndef FRONTEND
76 int h;
79
80 /*
81 * Compute the hash function. The hash key is the codepoint with the bytes
82 * in network order.
83 */
84 hashkey = pg_hton32(code);
85 h = decompinfo.hash(&hashkey);
86
87 /* An out-of-range result implies no match */
88 if (h < 0 || h >= decompinfo.num_decomps)
89 return NULL;
90
91 /*
92 * Since it's a perfect hash, we need only match to the specific codepoint
93 * it identifies.
94 */
95 if (code != decompinfo.decomps[h].codepoint)
96 return NULL;
97
98 /* Success! */
99 return &decompinfo.decomps[h];
100#else
101 return bsearch(&(code),
106#endif
107}
108
109/*
110 * Get the combining class of the given codepoint.
111 */
112static uint8
114{
115 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 if (!entry)
122 return 0;
123 else
124 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 */
134static const char32_t *
136{
137 static char32_t x;
138
139 if (DECOMPOSITION_IS_INLINE(entry))
140 {
141 Assert(DECOMPOSITION_SIZE(entry) == 1);
142 x = (char32_t) entry->dec_index;
143 *dec_size = 1;
144 return &x;
145 }
146 else
147 {
149 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 */
159static int
160get_decomposed_size(char32_t code, bool compat)
161{
162 const pg_unicode_decomposition *entry;
163 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 if (code >= SBASE && code < SBASE + SCOUNT)
175 {
177 sindex;
178
179 sindex = code - SBASE;
180 tindex = sindex % TCOUNT;
181
182 if (tindex != 0)
183 return 3;
184 return 2;
185 }
186
187 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 if (entry == NULL || DECOMPOSITION_SIZE(entry) == 0 ||
194 (!compat && DECOMPOSITION_IS_COMPAT(entry)))
195 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 */
202 for (i = 0; i < dec_size; i++)
203 {
204 uint32 lcode = decomp[i];
205
207 }
208
209 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 */
218static bool
220{
221 /*
222 * Handle Hangul characters algorithmically, per the Unicode spec.
223 *
224 * Check if two current characters are L and V.
225 */
226 if (start >= LBASE && start < LBASE + LCOUNT &&
227 code >= VBASE && code < VBASE + VCOUNT)
228 {
229 /* make syllable of form LV */
231 uint32 vindex = code - VBASE;
232
233 *result = SBASE + (lindex * VCOUNT + vindex) * TCOUNT;
234 return true;
235 }
236 /* Check if two current characters are LV and T */
237 else if (start >= SBASE && start < (SBASE + SCOUNT) &&
238 ((start - SBASE) % TCOUNT) == 0 &&
239 code > TBASE && code < (TBASE + TCOUNT))
240 {
241 /* make syllable of form LVT */
242 uint32 tindex = code - TBASE;
243
244 *result = start + tindex;
245 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,
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 hashkey = pg_hton64(((uint64) start << 32) | (uint64) code);
271 h = recompinfo.hash(&hashkey);
272
273 /* An out-of-range result implies no match */
274 if (h < 0 || h >= recompinfo.num_recomps)
275 return false;
276
277 inv_lookup_index = recompinfo.inverse_lookup[h];
279
281 code == UnicodeDecomp_codepoints[entry->dec_index + 1])
282 {
283 *result = entry->codepoint;
284 return true;
285 }
286
287#else
288
289 for (size_t i = 0; i < lengthof(UnicodeDecompMain); i++)
290 {
291 entry = &UnicodeDecompMain[i];
292
293 if (DECOMPOSITION_SIZE(entry) != 2)
294 continue;
295
296 if (DECOMPOSITION_NO_COMPOSE(entry))
297 continue;
298
300 code == UnicodeDecomp_codepoints[entry->dec_index + 1])
301 {
302 *result = entry->codepoint;
303 return true;
304 }
305 }
306#endif /* !FRONTEND */
307 }
308
309 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 */
319static void
320decompose_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 if (code >= SBASE && code < SBASE + SCOUNT)
334 {
335 uint32 l,
336 v,
337 tindex,
338 sindex;
339 char32_t *res = *result;
340
341 sindex = code - SBASE;
342 l = LBASE + sindex / (VCOUNT * TCOUNT);
343 v = VBASE + (sindex % (VCOUNT * TCOUNT)) / TCOUNT;
344 tindex = sindex % TCOUNT;
345
346 res[*current] = l;
347 (*current)++;
348 res[*current] = v;
349 (*current)++;
350
351 if (tindex != 0)
352 {
353 res[*current] = TBASE + tindex;
354 (*current)++;
355 }
356
357 return;
358 }
359
360 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 if (entry == NULL || DECOMPOSITION_SIZE(entry) == 0 ||
369 (!compat && DECOMPOSITION_IS_COMPAT(entry)))
370 {
371 char32_t *res = *result;
372
373 res[*current] = code;
374 (*current)++;
375 return;
376 }
377
378 /*
379 * If this entry has other decomposition codes look at them as well.
380 */
382 for (i = 0; i < dec_size; i++)
383 {
384 char32_t lcode = (char32_t) decomp[i];
385
386 /* Leave if no more decompositions */
387 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 */
400char32_t *
402{
403 bool compat = (form == UNICODE_NFKC || form == UNICODE_NFKD);
404 bool recompose = (form == UNICODE_NFC || form == UNICODE_NFKC);
405 char32_t *decomp_chars;
406 char32_t *recomp_chars;
407 int decomp_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;
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 decomp_size = 0;
430 for (p = input; *p; p++)
431 {
433 if (unlikely(decomp_size > MaxAllocSize / sizeof(char32_t)))
434 {
435#ifndef FRONTEND
436 /* Exit loop and let palloc() throw error below */
437 break;
438#else
439 /* Just return NULL with no explicit error */
440 return NULL;
441#endif
442 }
443 }
444
445 decomp_chars = (char32_t *) ALLOC((decomp_size + 1) * sizeof(char32_t));
446 if (decomp_chars == NULL)
447 return NULL;
448
449 /*
450 * Now fill in each entry recursively. This needs a second pass on the
451 * decomposition table.
452 */
453 current_size = 0;
454 for (p = input; *p; p++)
458
459 /* Leave if there is nothing to decompose */
460 if (decomp_size == 0)
461 return decomp_chars;
462
463 /*
464 * Now apply canonical ordering.
465 */
466 for (count = 1; count < decomp_size; count++)
467 {
468 char32_t prev = decomp_chars[count - 1];
469 char32_t next = decomp_chars[count];
470 char32_t tmp;
471 const uint8 prevClass = get_canonical_class(prev);
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 if (prevClass == 0 || nextClass == 0)
483 continue;
484
485 if (prevClass <= nextClass)
486 continue;
487
488 /* exchange can happen */
489 tmp = decomp_chars[count - 1];
490 decomp_chars[count - 1] = decomp_chars[count];
491 decomp_chars[count] = tmp;
492
493 /* backtrack to check again */
494 if (count > 1)
495 count -= 2;
496 }
497
498 if (!recompose)
499 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 recomp_chars = (char32_t *) ALLOC((decomp_size + 1) * sizeof(char32_t));
508 if (!recomp_chars)
509 {
511 return NULL;
512 }
513
514 last_class = -1; /* this eliminates a special check */
515 starter_pos = 0;
516 target_pos = 1;
518
519 for (count = 1; count < decomp_size; count++)
520 {
521 char32_t ch = decomp_chars[count];
523 char32_t composite;
524
525 if (last_class < ch_class &&
526 recompose_code(starter_ch, ch, &composite))
527 {
528 recomp_chars[starter_pos] = composite;
529 starter_ch = composite;
530 }
531 else if (ch_class == 0)
532 {
534 starter_ch = ch;
535 last_class = -1;
537 }
538 else
539 {
542 }
543 }
545
547
548 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
559static const pg_unicode_normprops *
561{
562 int h;
564
565 /*
566 * Compute the hash function. The hash key is the codepoint with the bytes
567 * in network order.
568 */
570 h = norminfo->hash(&hashkey);
571
572 /* An out-of-range result implies no match */
573 if (h < 0 || h >= norminfo->num_normprops)
574 return NULL;
575
576 /*
577 * Since it's a perfect hash, we need only match to the specific codepoint
578 * it identifies.
579 */
580 if (ch != norminfo->normprops[h].codepoint)
581 return NULL;
582
583 /* Success! */
584 return &norminfo->normprops[h];
585}
586
587/*
588 * Look up the normalization quick check character property
589 */
592{
593 const pg_unicode_normprops *found = NULL;
594
595 switch (form)
596 {
597 case UNICODE_NFC:
599 break;
600 case UNICODE_NFKC:
602 break;
603 default:
604 Assert(false);
605 break;
606 }
607
608 if (found)
609 return found->quickcheck;
610 else
611 return UNICODE_NORM_QC_YES;
612}
613
616{
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 if (form == UNICODE_NFD || form == UNICODE_NFKD)
629
630 for (const char32_t *p = input; *p; p++)
631 {
632 char32_t ch = *p;
635
638 return UNICODE_NORM_QC_NO;
639
640 check = qc_is_allowed(form, ch);
641 if (check == UNICODE_NORM_QC_NO)
642 return UNICODE_NORM_QC_NO;
643 else if (check == UNICODE_NORM_QC_MAYBE)
645
647 }
648 return result;
649}
650
651#endif /* !FRONTEND */
static int32 next
Definition blutils.c:225
uint8_t uint8
Definition c.h:681
#define Assert(condition)
Definition c.h:1002
uint64_t uint64
Definition c.h:684
#define unlikely(x)
Definition c.h:497
uint32_t uint32
Definition c.h:683
#define lengthof(array)
Definition c.h:932
uint32_t char32_t
Definition c.h:1561
uint32 result
enum COMPAT_MODE compat
Definition ecpg.c:26
#define MaxAllocSize
Definition fe_memutils.h:22
return str start
FILE * input
int x
Definition isn.c:75
int i
Definition isn.c:77
#define pg_hton32(x)
Definition pg_bswap.h:121
#define pg_hton64(x)
Definition pg_bswap.h:122
static int64 current_size
static int fb(int x)
static void decompose_code(char32_t code, bool compat, char32_t **result, int *current)
#define TCOUNT
static uint8 get_canonical_class(char32_t code)
#define TBASE
#define VBASE
#define VCOUNT
UnicodeNormalizationQC unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const char32_t *input)
static const pg_unicode_normprops * qc_hash_lookup(char32_t ch, const pg_unicode_norminfo *norminfo)
#define LBASE
static const char32_t * get_code_decomposition(const pg_unicode_decomposition *entry, int *dec_size)
#define LCOUNT
char32_t * unicode_normalize(UnicodeNormalizationForm form, const char32_t *input)
#define ALLOC(size)
#define FREE(size)
static int get_decomposed_size(char32_t code, bool compat)
static UnicodeNormalizationQC qc_is_allowed(UnicodeNormalizationForm form, char32_t ch)
static const pg_unicode_decomposition * get_code_entry(char32_t code)
#define SBASE
#define SCOUNT
static bool recompose_code(uint32 start, uint32 code, uint32 *result)
UnicodeNormalizationForm
@ UNICODE_NFKD
@ UNICODE_NFD
@ UNICODE_NFC
@ UNICODE_NFKC
UnicodeNormalizationQC
@ UNICODE_NORM_QC_YES
@ UNICODE_NORM_QC_NO
@ UNICODE_NORM_QC_MAYBE
static const pg_unicode_decompinfo UnicodeDecompInfo
static const pg_unicode_recompinfo UnicodeRecompInfo
#define DECOMPOSITION_NO_COMPOSE(x)
static const uint32 UnicodeDecomp_codepoints[5138]
#define DECOMPOSITION_IS_INLINE(x)
#define DECOMPOSITION_IS_COMPAT(x)
static const pg_unicode_decomposition UnicodeDecompMain[6878]
#define DECOMPOSITION_SIZE(x)
static const pg_unicode_norminfo UnicodeNormInfo_NFKC_QC
static const pg_unicode_norminfo UnicodeNormInfo_NFC_QC