LCOV - code coverage report
Current view: top level - src/common - unicode_norm.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 73.3 % 255 187
Test Date: 2026-01-26 10:56:24 Functions: 90.9 % 11 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 63.2 % 155 98

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

Generated by: LCOV version 2.3.2-1