LCOV - code coverage report
Current view: top level - glib - gunidecomp.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 100.0 % 242 242
Test Date: 2025-02-18 05:25:45 Functions: 100.0 % 13 13
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* decomp.c - Character decomposition.
       2                 :             :  *
       3                 :             :  *  Copyright (C) 1999, 2000 Tom Tromey
       4                 :             :  *  Copyright 2000 Red Hat, Inc.
       5                 :             :  *
       6                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       7                 :             :  *
       8                 :             :  * This library is free software; you can redistribute it and/or
       9                 :             :  * modify it under the terms of the GNU Lesser General Public
      10                 :             :  * License as published by the Free Software Foundation; either
      11                 :             :  * version 2.1 of the License, or (at your option) any later version.
      12                 :             :  *
      13                 :             :  * This library is distributed in the hope that it will be useful,
      14                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16                 :             :  * Lesser General Public License for more details.
      17                 :             :  *
      18                 :             :  * You should have received a copy of the GNU Lesser General Public License
      19                 :             :  * along with this library; if not, see <http://www.gnu.org/licenses/>.
      20                 :             :  */
      21                 :             : 
      22                 :             : #include "config.h"
      23                 :             : 
      24                 :             : #include <stdlib.h>
      25                 :             : 
      26                 :             : #include "gunicode.h"
      27                 :             : #include "gunidecomp.h"
      28                 :             : #include "gmem.h"
      29                 :             : #include "gtestutils.h"
      30                 :             : #include "gunicomp.h"
      31                 :             : #include "gunicodeprivate.h"
      32                 :             : 
      33                 :             : 
      34                 :             : #define CC_PART1(Page, Char) \
      35                 :             :   ((combining_class_table_part1[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
      36                 :             :    ? (combining_class_table_part1[Page] - G_UNICODE_MAX_TABLE_INDEX) \
      37                 :             :    : (cclass_data[combining_class_table_part1[Page]][Char]))
      38                 :             : 
      39                 :             : #define CC_PART2(Page, Char) \
      40                 :             :   ((combining_class_table_part2[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
      41                 :             :    ? (combining_class_table_part2[Page] - G_UNICODE_MAX_TABLE_INDEX) \
      42                 :             :    : (cclass_data[combining_class_table_part2[Page]][Char]))
      43                 :             : 
      44                 :             : #define COMBINING_CLASS(Char) \
      45                 :             :   (((Char) <= G_UNICODE_LAST_CHAR_PART1) \
      46                 :             :    ? CC_PART1 ((Char) >> 8, (Char) & 0xff) \
      47                 :             :    : (((Char) >= 0xe0000 && (Char) <= G_UNICODE_LAST_CHAR) \
      48                 :             :       ? CC_PART2 (((Char) - 0xe0000) >> 8, (Char) & 0xff) \
      49                 :             :       : 0))
      50                 :             : 
      51                 :             : /**
      52                 :             :  * g_unichar_combining_class:
      53                 :             :  * @uc: a Unicode character
      54                 :             :  * 
      55                 :             :  * Determines the canonical combining class of a Unicode character.
      56                 :             :  * 
      57                 :             :  * Returns: the combining class of the character
      58                 :             :  *
      59                 :             :  * Since: 2.14
      60                 :             :  **/
      61                 :             : gint
      62                 :          66 : g_unichar_combining_class (gunichar uc)
      63                 :             : {
      64                 :          66 :   return COMBINING_CLASS (uc);
      65                 :             : }
      66                 :             : 
      67                 :             : /* constants for hangul syllable [de]composition */
      68                 :             : #define SBase 0xAC00 
      69                 :             : #define LBase 0x1100 
      70                 :             : #define VBase 0x1161 
      71                 :             : #define TBase 0x11A7
      72                 :             : #define LCount 19 
      73                 :             : #define VCount 21
      74                 :             : #define TCount 28
      75                 :             : #define NCount (VCount * TCount)
      76                 :             : #define SCount (LCount * NCount)
      77                 :             : 
      78                 :             : /**
      79                 :             :  * g_unicode_canonical_ordering:
      80                 :             :  * @string: (array length=len) (element-type gunichar): a UCS-4 encoded string.
      81                 :             :  * @len: the maximum length of @string to use.
      82                 :             :  *
      83                 :             :  * Computes the canonical ordering of a string in-place.  
      84                 :             :  * This rearranges decomposed characters in the string 
      85                 :             :  * according to their combining classes.  See the Unicode 
      86                 :             :  * manual for more information. 
      87                 :             :  **/
      88                 :             : void
      89                 :     1070241 : g_unicode_canonical_ordering (gunichar *string,
      90                 :             :                               gsize     len)
      91                 :             : {
      92                 :             :   gsize i;
      93                 :     1070241 :   int swap = 1;
      94                 :             : 
      95                 :     2153728 :   while (swap)
      96                 :             :     {
      97                 :             :       int last;
      98                 :     1083487 :       swap = 0;
      99                 :     1083487 :       last = COMBINING_CLASS (string[0]);
     100                 :     2218471 :       for (i = 0; i < len - 1; ++i)
     101                 :             :         {
     102                 :     1134984 :           int next = COMBINING_CLASS (string[i + 1]);
     103                 :     1134984 :           if (next != 0 && last > next)
     104                 :             :             {
     105                 :             :               gsize j;
     106                 :             :               /* Percolate item leftward through string.  */
     107                 :       60304 :               for (j = i + 1; j > 0; --j)
     108                 :             :                 {
     109                 :             :                   gunichar t;
     110                 :       60292 :                   if (COMBINING_CLASS (string[j - 1]) <= next)
     111                 :       24714 :                     break;
     112                 :       35578 :                   t = string[j];
     113                 :       35578 :                   string[j] = string[j - 1];
     114                 :       35578 :                   string[j - 1] = t;
     115                 :       35578 :                   swap = 1;
     116                 :             :                 }
     117                 :             :               /* We're re-entering the loop looking at the old
     118                 :             :                  character again.  */
     119                 :       24726 :               next = last;
     120                 :             :             }
     121                 :     1134984 :           last = next;
     122                 :             :         }
     123                 :             :     }
     124                 :     1070241 : }
     125                 :             : 
     126                 :             : /* http://www.unicode.org/unicode/reports/tr15/#Hangul
     127                 :             :  * r should be null or have sufficient space. Calling with r == NULL will
     128                 :             :  * only calculate the result_len; however, a buffer with space for three
     129                 :             :  * characters will always be big enough. */
     130                 :             : static void
     131                 :      293089 : decompose_hangul (gunichar s,
     132                 :             :                   gunichar *r,
     133                 :             :                   gsize *result_len)
     134                 :             : {
     135                 :      293089 :   gint SIndex = s - SBase;
     136                 :      293089 :   gint TIndex = SIndex % TCount;
     137                 :             : 
     138                 :      293089 :   if (r)
     139                 :             :     {
     140                 :      135382 :       r[0] = LBase + SIndex / NCount;
     141                 :      135382 :       r[1] = VBase + (SIndex % NCount) / TCount;
     142                 :             :     }
     143                 :             : 
     144                 :      293089 :   if (TIndex)
     145                 :             :     {
     146                 :      280169 :       if (r)
     147                 :      129316 :         r[2] = TBase + TIndex;
     148                 :      280169 :       *result_len = 3;
     149                 :             :     }
     150                 :             :   else
     151                 :       12920 :     *result_len = 2;
     152                 :      293089 : }
     153                 :             : 
     154                 :             : /* returns a pointer to a null-terminated UTF-8 string */
     155                 :             : static const gchar *
     156                 :     3625609 : find_decomposition (gunichar ch,
     157                 :             :                     gboolean compat)
     158                 :             : {
     159                 :     3625609 :   int start = 0;
     160                 :     3625609 :   int end = G_N_ELEMENTS (decomp_table);
     161                 :             :   
     162                 :     3625609 :   if (ch >= decomp_table[start].ch &&
     163                 :     3372260 :       ch <= decomp_table[end - 1].ch)
     164                 :             :     {
     165                 :             :       while (TRUE)
     166                 :    18800004 :         {
     167                 :    20334244 :           int half = (start + end) / 2;
     168                 :    20334244 :           if (ch == decomp_table[half].ch)
     169                 :             :             {
     170                 :             :               int offset;
     171                 :             : 
     172                 :      168502 :               if (compat)
     173                 :             :                 {
     174                 :       84293 :                   offset = decomp_table[half].compat_offset;
     175                 :       84293 :                   if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
     176                 :       34013 :                     offset = decomp_table[half].canon_offset;
     177                 :             :                 }
     178                 :             :               else
     179                 :             :                 {
     180                 :       84209 :                   offset = decomp_table[half].canon_offset;
     181                 :       84209 :                   if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
     182                 :       50146 :                     return NULL;
     183                 :             :                 }
     184                 :             :               
     185                 :      118356 :               return &(decomp_expansion_string[offset]);
     186                 :             :             }
     187                 :    20165742 :           else if (half == start)
     188                 :     1365738 :             break;
     189                 :    18800004 :           else if (ch > decomp_table[half].ch)
     190                 :     8406086 :             start = half;
     191                 :             :           else
     192                 :    10393918 :             end = half;
     193                 :             :         }
     194                 :             :     }
     195                 :             : 
     196                 :     3457107 :   return NULL;
     197                 :             : }
     198                 :             : 
     199                 :             : /**
     200                 :             :  * g_unicode_canonical_decomposition:
     201                 :             :  * @ch: a Unicode character.
     202                 :             :  * @result_len: location to store the length of the return value.
     203                 :             :  *
     204                 :             :  * Computes the canonical decomposition of a Unicode character.  
     205                 :             :  * 
     206                 :             :  * Returns: a newly allocated string of Unicode characters.
     207                 :             :  *   @result_len is set to the resulting length of the string.
     208                 :             :  *
     209                 :             :  * Deprecated: 2.30: Use the more flexible g_unichar_fully_decompose()
     210                 :             :  *   instead.
     211                 :             :  **/
     212                 :             : gunichar *
     213                 :          16 : g_unicode_canonical_decomposition (gunichar ch,
     214                 :             :                                    gsize   *result_len)
     215                 :             : {
     216                 :             :   const gchar *decomp;
     217                 :             :   const gchar *p;
     218                 :             :   gunichar *r;
     219                 :             : 
     220                 :             :   /* Hangul syllable */
     221                 :          16 :   if (ch >= SBase && ch < SBase + SCount)
     222                 :             :     {
     223                 :           4 :       decompose_hangul (ch, NULL, result_len);
     224                 :           4 :       r = g_malloc (*result_len * sizeof (gunichar));
     225                 :           4 :       decompose_hangul (ch, r, result_len);
     226                 :             :     }
     227                 :          12 :   else if ((decomp = find_decomposition (ch, FALSE)) != NULL)
     228                 :             :     {
     229                 :             :       /* Found it.  */
     230                 :             :       int i;
     231                 :             :       
     232                 :          10 :       *result_len = g_utf8_strlen (decomp, -1);
     233                 :          10 :       r = g_malloc (*result_len * sizeof (gunichar));
     234                 :             :       
     235                 :          30 :       for (p = decomp, i = 0; *p != '\0'; p = g_utf8_next_char (p), i++)
     236                 :          20 :         r[i] = g_utf8_get_char (p);
     237                 :             :     }
     238                 :             :   else
     239                 :             :     {
     240                 :             :       /* Not in our table.  */
     241                 :           2 :       r = g_malloc (sizeof (gunichar));
     242                 :           2 :       *r = ch;
     243                 :           2 :       *result_len = 1;
     244                 :             :     }
     245                 :             : 
     246                 :          16 :   return r;
     247                 :             : }
     248                 :             : 
     249                 :             : /* L,V => LV and LV,T => LVT  */
     250                 :             : static gboolean
     251                 :      352541 : combine_hangul (gunichar a,
     252                 :             :                 gunichar b,
     253                 :             :                 gunichar *result)
     254                 :             : {
     255                 :      352541 :   gint LIndex = a - LBase;
     256                 :      352541 :   gint SIndex = a - SBase;
     257                 :             : 
     258                 :      352541 :   gint VIndex = b - VBase;
     259                 :      352541 :   gint TIndex = b - TBase;
     260                 :             : 
     261                 :      352541 :   if (0 <= LIndex && LIndex < LCount
     262                 :      113368 :       && 0 <= VIndex && VIndex < VCount)
     263                 :             :     {
     264                 :      112938 :       *result = SBase + (LIndex * VCount + VIndex) * TCount;
     265                 :      112938 :       return TRUE;
     266                 :             :     }
     267                 :      239603 :   else if (0 <= SIndex && SIndex < SCount && (SIndex % TCount) == 0
     268                 :      108828 :            && 0 < TIndex && TIndex < TCount)
     269                 :             :     {
     270                 :      107768 :       *result = a + TIndex;
     271                 :      107768 :       return TRUE;
     272                 :             :     }
     273                 :             : 
     274                 :      131835 :   return FALSE;
     275                 :             : }
     276                 :             : 
     277                 :             : #define CI(Page, Char) \
     278                 :             :   ((compose_table[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
     279                 :             :    ? (compose_table[Page] - G_UNICODE_MAX_TABLE_INDEX) \
     280                 :             :    : (compose_data[compose_table[Page]][Char]))
     281                 :             : 
     282                 :             : #define COMPOSE_INDEX(Char) \
     283                 :             :      (((Char >> 8) > (COMPOSE_TABLE_LAST)) ? 0 : CI((Char) >> 8, (Char) & 0xff))
     284                 :             : 
     285                 :             : static gboolean
     286                 :      352541 : combine (gunichar  a,
     287                 :             :          gunichar  b,
     288                 :             :          gunichar *result)
     289                 :             : {
     290                 :             :   gushort index_a, index_b;
     291                 :             : 
     292                 :      352541 :   if (combine_hangul (a, b, result))
     293                 :      220706 :     return TRUE;
     294                 :             : 
     295                 :      131835 :   index_a = COMPOSE_INDEX(a);
     296                 :             : 
     297                 :      131835 :   if (index_a >= COMPOSE_FIRST_SINGLE_START && index_a < COMPOSE_SECOND_START)
     298                 :             :     {
     299                 :        8631 :       if (b == compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][0])
     300                 :             :         {
     301                 :        6845 :           *result = compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][1];
     302                 :        6845 :           return TRUE;
     303                 :             :         }
     304                 :             :       else
     305                 :        1786 :         return FALSE;
     306                 :             :     }
     307                 :             :   
     308                 :      123204 :   index_b = COMPOSE_INDEX(b);
     309                 :             : 
     310                 :      123204 :   if (index_b >= COMPOSE_SECOND_SINGLE_START && index_b < COMPOSE_EITHER_START)
     311                 :             :     {
     312                 :         354 :       if (a == compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][0])
     313                 :             :         {
     314                 :         319 :           *result = compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][1];
     315                 :         319 :           return TRUE;
     316                 :             :         }
     317                 :             :       else
     318                 :          35 :         return FALSE;
     319                 :             :     }
     320                 :             : 
     321                 :      122850 :   if (index_a >= COMPOSE_FIRST_START && index_a < COMPOSE_FIRST_SINGLE_START &&
     322                 :       32362 :       index_b >= COMPOSE_SECOND_START && index_b < COMPOSE_SECOND_SINGLE_START)
     323                 :             :     {
     324                 :       32361 :       gunichar res = compose_array[index_a - COMPOSE_FIRST_START][index_b - COMPOSE_SECOND_START];
     325                 :             : 
     326                 :       32361 :       if (res)
     327                 :             :         {
     328                 :       29540 :           *result = res;
     329                 :       29540 :           return TRUE;
     330                 :             :         }
     331                 :             :     }
     332                 :             : 
     333                 :       93310 :   if (index_a >= COMPOSE_EITHER_START &&
     334                 :             :       index_b >= COMPOSE_EITHER_START)
     335                 :             :     {
     336                 :        1353 :       gunichar res = compose_either_array[index_a - COMPOSE_EITHER_START][index_b - COMPOSE_EITHER_START];
     337                 :             : 
     338                 :        1353 :       if (res)
     339                 :             :         {
     340                 :        1162 :           *result = res;
     341                 :        1162 :           return TRUE;
     342                 :             :         }
     343                 :             :     }
     344                 :             : 
     345                 :       92148 :   return FALSE;
     346                 :             : }
     347                 :             : 
     348                 :             : gunichar *
     349                 :      404329 : _g_utf8_normalize_wc (const gchar    *str,
     350                 :             :                       gssize          max_len,
     351                 :             :                       GNormalizeMode  mode)
     352                 :             : {
     353                 :             :   gsize n_wc;
     354                 :             :   gunichar *wc_buffer;
     355                 :             :   const char *p;
     356                 :             :   gsize last_start;
     357                 :      404329 :   gboolean do_compat = (mode == G_NORMALIZE_NFKC ||
     358                 :             :                         mode == G_NORMALIZE_NFKD);
     359                 :      404329 :   gboolean do_compose = (mode == G_NORMALIZE_NFC ||
     360                 :             :                          mode == G_NORMALIZE_NFKC);
     361                 :             : 
     362                 :             :   /* Do a first pass to work out the length of the normalised string so we can
     363                 :             :    * allocate a buffer. */
     364                 :      404329 :   n_wc = 0;
     365                 :      404329 :   p = str;
     366                 :     1249520 :   while ((max_len < 0 || p < str + max_len) && *p)
     367                 :             :     {
     368                 :             :       const gchar *decomp;
     369                 :             :       const char *next, *between;
     370                 :             :       gunichar wc;
     371                 :             : 
     372                 :      845211 :       next = g_utf8_next_char (p);
     373                 :             :       /* Avoid reading truncated multibyte characters
     374                 :             :          which run past the end of the buffer */
     375                 :      845211 :       if (max_len < 0)
     376                 :             :         {
     377                 :             :           /* Does the character contain a NUL terminator? */
     378                 :     2133187 :           for (between = &p[1]; between < next; between++)
     379                 :             :             {
     380                 :     1313808 :               if (G_UNLIKELY (!*between))
     381                 :           6 :                 return NULL;
     382                 :             :             }
     383                 :             :         }
     384                 :             :       else
     385                 :             :         {
     386                 :       25826 :           if (G_UNLIKELY (next > str + max_len))
     387                 :           7 :             return NULL;
     388                 :             :         }
     389                 :      845198 :       wc = g_utf8_get_char (p);
     390                 :             : 
     391                 :      845198 :       if (G_UNLIKELY (wc == (gunichar) -1))
     392                 :             :         {
     393                 :           7 :           return NULL;
     394                 :             :         }
     395                 :      845191 :       else if (wc >= SBase && wc < SBase + SCount)
     396                 :      135355 :         {
     397                 :             :           gsize result_len;
     398                 :      135355 :           decompose_hangul (wc, NULL, &result_len);
     399                 :      135355 :           n_wc += result_len;
     400                 :             :         }
     401                 :             :       else 
     402                 :             :         {
     403                 :      709836 :           decomp = find_decomposition (wc, do_compat);
     404                 :             : 
     405                 :      709836 :           if (decomp)
     406                 :       55139 :             n_wc += g_utf8_strlen (decomp, -1);
     407                 :             :           else
     408                 :      654697 :             n_wc++;
     409                 :             :         }
     410                 :             : 
     411                 :      845191 :       p = next;
     412                 :             :     }
     413                 :             : 
     414                 :             :   /* Allocate the buffer for the result. */
     415                 :      404309 :   wc_buffer = g_new (gunichar, n_wc + 1);
     416                 :             : 
     417                 :             :   /* Do another pass to fill the buffer with the normalised string. */
     418                 :      404309 :   last_start = 0;
     419                 :      404309 :   n_wc = 0;
     420                 :      404309 :   p = str;
     421                 :     1249452 :   while ((max_len < 0 || p < str + max_len) && *p)
     422                 :             :     {
     423                 :      845143 :       gunichar wc = g_utf8_get_char (p);
     424                 :             :       const gchar *decomp;
     425                 :             :       int cc;
     426                 :      845143 :       gsize old_n_wc = n_wc;
     427                 :             :           
     428                 :      845143 :       if (wc >= SBase && wc < SBase + SCount)
     429                 :      135354 :         {
     430                 :             :           gsize result_len;
     431                 :      135354 :           decompose_hangul (wc, wc_buffer + n_wc, &result_len);
     432                 :      135354 :           n_wc += result_len;
     433                 :             :         }
     434                 :             :       else
     435                 :             :         {
     436                 :      709789 :           decomp = find_decomposition (wc, do_compat);
     437                 :             :           
     438                 :      709789 :           if (decomp)
     439                 :             :             {
     440                 :             :               const char *pd;
     441                 :      161275 :               for (pd = decomp; *pd != '\0'; pd = g_utf8_next_char (pd))
     442                 :      106136 :                 wc_buffer[n_wc++] = g_utf8_get_char (pd);
     443                 :             :             }
     444                 :             :           else
     445                 :      654650 :             wc_buffer[n_wc++] = wc;
     446                 :             :         }
     447                 :             : 
     448                 :             :       /* Each code path above *must* have appended at least gunichar to wc_buffer. */
     449                 :      845143 :       g_assert (n_wc > old_n_wc);
     450                 :             : 
     451                 :      845143 :       cc = COMBINING_CLASS (wc_buffer[old_n_wc]);
     452                 :             : 
     453                 :      845143 :       if (cc == 0)
     454                 :             :         {
     455                 :      665936 :           g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
     456                 :      665936 :           last_start = old_n_wc;
     457                 :             :         }
     458                 :             :       
     459                 :      845143 :       p = g_utf8_next_char (p);
     460                 :             :     }
     461                 :             : 
     462                 :      404309 :   if (n_wc > 0)
     463                 :             :     {
     464                 :      404305 :       g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
     465                 :      404305 :       last_start = n_wc;
     466                 :             :       (void) last_start;
     467                 :             :     }
     468                 :             :           
     469                 :      404309 :   wc_buffer[n_wc] = 0;
     470                 :             : 
     471                 :             :   /* All decomposed and reordered */ 
     472                 :             : 
     473                 :      404309 :   if (do_compose && n_wc > 0)
     474                 :             :     {
     475                 :             :       gsize i, j;
     476                 :      204380 :       int last_cc = 0;
     477                 :      204380 :       last_start = 0;
     478                 :             :       
     479                 :      795517 :       for (i = 0; i < n_wc; i++)
     480                 :             :         {
     481                 :      591137 :           int cc = COMBINING_CLASS (wc_buffer[i]);
     482                 :             : 
     483                 :      591137 :           if (i > 0 &&
     484                 :      423738 :               (last_cc == 0 || last_cc < cc) &&
     485                 :      352496 :               combine (wc_buffer[last_start], wc_buffer[i],
     486                 :      352496 :                        &wc_buffer[last_start]))
     487                 :             :             {
     488                 :      404156 :               for (j = i + 1; j < n_wc; j++)
     489                 :      145598 :                 wc_buffer[j-1] = wc_buffer[j];
     490                 :      258558 :               n_wc--;
     491                 :      258558 :               i--;
     492                 :             :               
     493                 :      258558 :               if (i == last_start)
     494                 :      252895 :                 last_cc = 0;
     495                 :             :               else
     496                 :        5663 :                 last_cc = COMBINING_CLASS (wc_buffer[i-1]);
     497                 :             :               
     498                 :      258558 :               continue;
     499                 :             :             }
     500                 :             : 
     501                 :      332579 :           if (cc == 0)
     502                 :      259855 :             last_start = i;
     503                 :             : 
     504                 :      332579 :           last_cc = cc;
     505                 :             :         }
     506                 :             :     }
     507                 :             : 
     508                 :      404309 :   wc_buffer[n_wc] = 0;
     509                 :             : 
     510                 :      404309 :   return wc_buffer;
     511                 :             : }
     512                 :             : 
     513                 :             : /**
     514                 :             :  * g_utf8_normalize:
     515                 :             :  * @str: a UTF-8 encoded string.
     516                 :             :  * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
     517                 :             :  * @mode: the type of normalization to perform.
     518                 :             :  *
     519                 :             :  * Converts a string into canonical form, standardizing
     520                 :             :  * such issues as whether a character with an accent
     521                 :             :  * is represented as a base character and combining
     522                 :             :  * accent or as a single precomposed character. The
     523                 :             :  * string has to be valid UTF-8, otherwise %NULL is
     524                 :             :  * returned. You should generally call g_utf8_normalize()
     525                 :             :  * before comparing two Unicode strings.
     526                 :             :  *
     527                 :             :  * The normalization mode %G_NORMALIZE_DEFAULT only
     528                 :             :  * standardizes differences that do not affect the
     529                 :             :  * text content, such as the above-mentioned accent
     530                 :             :  * representation. %G_NORMALIZE_ALL also standardizes
     531                 :             :  * the "compatibility" characters in Unicode, such
     532                 :             :  * as SUPERSCRIPT THREE to the standard forms
     533                 :             :  * (in this case DIGIT THREE). Formatting information
     534                 :             :  * may be lost but for most text operations such
     535                 :             :  * characters should be considered the same.
     536                 :             :  *
     537                 :             :  * %G_NORMALIZE_DEFAULT_COMPOSE and %G_NORMALIZE_ALL_COMPOSE
     538                 :             :  * are like %G_NORMALIZE_DEFAULT and %G_NORMALIZE_ALL,
     539                 :             :  * but returned a result with composed forms rather
     540                 :             :  * than a maximally decomposed form. This is often
     541                 :             :  * useful if you intend to convert the string to
     542                 :             :  * a legacy encoding or pass it to a system with
     543                 :             :  * less capable Unicode handling.
     544                 :             :  *
     545                 :             :  * Returns: (nullable): a newly allocated string, that
     546                 :             :  *   is the normalized form of @str, or %NULL if @str
     547                 :             :  *   is not valid UTF-8.
     548                 :             :  **/
     549                 :             : gchar *
     550                 :      403852 : g_utf8_normalize (const gchar    *str,
     551                 :             :                   gssize          len,
     552                 :             :                   GNormalizeMode  mode)
     553                 :             : {
     554                 :      403852 :   gunichar *result_wc = _g_utf8_normalize_wc (str, len, mode);
     555                 :      403852 :   gchar *result = NULL;
     556                 :             : 
     557                 :      403852 :   if (G_LIKELY (result_wc != NULL))
     558                 :             :     {
     559                 :      403832 :       result = g_ucs4_to_utf8 (result_wc, -1, NULL, NULL, NULL);
     560                 :      403832 :       g_free (result_wc);
     561                 :             :     }
     562                 :             : 
     563                 :      403852 :   return result;
     564                 :             : }
     565                 :             : 
     566                 :             : static gboolean
     567                 :     1127383 : decompose_hangul_step (gunichar  ch,
     568                 :             :                        gunichar *a,
     569                 :             :                        gunichar *b)
     570                 :             : {
     571                 :             :   gint SIndex, TIndex;
     572                 :             : 
     573                 :     1127383 :   if (ch < SBase || ch >= SBase + SCount)
     574                 :     1116207 :     return FALSE;  /* not a hangul syllable */
     575                 :             : 
     576                 :       11176 :   SIndex = ch - SBase;
     577                 :       11176 :   TIndex = SIndex % TCount;
     578                 :             : 
     579                 :       11176 :   if (TIndex)
     580                 :             :     {
     581                 :             :       /* split LVT -> LV,T */
     582                 :       10775 :       *a = ch - TIndex;
     583                 :       10775 :       *b = TBase + TIndex;
     584                 :             :     }
     585                 :             :   else
     586                 :             :     {
     587                 :             :       /* split LV -> L,V */
     588                 :         401 :       *a = LBase + SIndex / NCount;
     589                 :         401 :       *b = VBase + (SIndex % NCount) / TCount;
     590                 :             :     }
     591                 :             : 
     592                 :       11176 :   return TRUE;
     593                 :             : }
     594                 :             : 
     595                 :             : /**
     596                 :             :  * g_unichar_decompose:
     597                 :             :  * @ch: a Unicode character
     598                 :             :  * @a: (out) (not optional): return location for the first component of @ch
     599                 :             :  * @b: (out) (not optional): return location for the second component of @ch
     600                 :             :  *
     601                 :             :  * Performs a single decomposition step of the
     602                 :             :  * Unicode canonical decomposition algorithm.
     603                 :             :  *
     604                 :             :  * This function does not include compatibility
     605                 :             :  * decompositions. It does, however, include algorithmic
     606                 :             :  * Hangul Jamo decomposition, as well as 'singleton'
     607                 :             :  * decompositions which replace a character by a single
     608                 :             :  * other character. In the case of singletons `*b` will
     609                 :             :  * be set to zero.
     610                 :             :  *
     611                 :             :  * If @ch is not decomposable, `*a` is set to @ch and `*b`
     612                 :             :  * is set to zero.
     613                 :             :  *
     614                 :             :  * Note that the way Unicode decomposition pairs are
     615                 :             :  * defined, it is guaranteed that @b would not decompose
     616                 :             :  * further, but @a may itself decompose.  To get the full
     617                 :             :  * canonical decomposition for @ch, one would need to
     618                 :             :  * recursively call this function on @a.  Or use
     619                 :             :  * g_unichar_fully_decompose().
     620                 :             :  *
     621                 :             :  * See
     622                 :             :  * [UAX#15](http://unicode.org/reports/tr15/)
     623                 :             :  * for details.
     624                 :             :  *
     625                 :             :  * Returns: %TRUE if the character could be decomposed
     626                 :             :  *
     627                 :             :  * Since: 2.30
     628                 :             :  */
     629                 :             : gboolean
     630                 :     1127383 : g_unichar_decompose (gunichar  ch,
     631                 :             :                      gunichar *a,
     632                 :             :                      gunichar *b)
     633                 :             : {
     634                 :     1127383 :   gint start = 0;
     635                 :     1127383 :   gint end = G_N_ELEMENTS (decomp_step_table);
     636                 :             : 
     637                 :     1127383 :   if (decompose_hangul_step (ch, a, b))
     638                 :       11176 :     return TRUE;
     639                 :             : 
     640                 :             :   /* TODO use bsearch() */
     641                 :     1116207 :   if (ch >= decomp_step_table[start].ch &&
     642                 :     1114979 :       ch <= decomp_step_table[end - 1].ch)
     643                 :             :     {
     644                 :             :       while (TRUE)
     645                 :     2154339 :         {
     646                 :     2350308 :           gint half = (start + end) / 2;
     647                 :     2350308 :           const decomposition_step *p = &(decomp_step_table[half]);
     648                 :     2350308 :           if (ch == p->ch)
     649                 :             :             {
     650                 :        2093 :               *a = p->a;
     651                 :        2093 :               *b = p->b;
     652                 :        2093 :               return TRUE;
     653                 :             :             }
     654                 :     2348215 :           else if (half == start)
     655                 :      193876 :             break;
     656                 :     2154339 :           else if (ch > p->ch)
     657                 :     1477643 :             start = half;
     658                 :             :           else
     659                 :      676696 :             end = half;
     660                 :             :         }
     661                 :             :     }
     662                 :             : 
     663                 :     1114114 :   *a = ch;
     664                 :     1114114 :   *b = 0;
     665                 :             : 
     666                 :     1114114 :   return FALSE;
     667                 :             : }
     668                 :             : 
     669                 :             : /**
     670                 :             :  * g_unichar_compose:
     671                 :             :  * @a: a Unicode character
     672                 :             :  * @b: a Unicode character
     673                 :             :  * @ch: (out) (not optional): return location for the composed character
     674                 :             :  *
     675                 :             :  * Performs a single composition step of the
     676                 :             :  * Unicode canonical composition algorithm.
     677                 :             :  *
     678                 :             :  * This function includes algorithmic Hangul Jamo composition,
     679                 :             :  * but it is not exactly the inverse of g_unichar_decompose().
     680                 :             :  * No composition can have either of @a or @b equal to zero.
     681                 :             :  * To be precise, this function composes if and only if
     682                 :             :  * there exists a Primary Composite P which is canonically
     683                 :             :  * equivalent to the sequence <@a,@b>.  See the Unicode
     684                 :             :  * Standard for the definition of Primary Composite.
     685                 :             :  *
     686                 :             :  * If @a and @b do not compose a new character, @ch is set to zero.
     687                 :             :  *
     688                 :             :  * See
     689                 :             :  * [UAX#15](http://unicode.org/reports/tr15/)
     690                 :             :  * for details.
     691                 :             :  *
     692                 :             :  * Returns: %TRUE if the characters could be composed
     693                 :             :  *
     694                 :             :  * Since: 2.30
     695                 :             :  */
     696                 :             : gboolean
     697                 :          45 : g_unichar_compose (gunichar  a,
     698                 :             :                    gunichar  b,
     699                 :             :                    gunichar *ch)
     700                 :             : {
     701                 :          45 :   if (combine (a, b, ch))
     702                 :          14 :     return TRUE;
     703                 :             : 
     704                 :          31 :   *ch = 0;
     705                 :          31 :   return FALSE;
     706                 :             : }
     707                 :             : 
     708                 :             : /**
     709                 :             :  * g_unichar_fully_decompose:
     710                 :             :  * @ch: a Unicode character.
     711                 :             :  * @compat: whether perform canonical or compatibility decomposition
     712                 :             :  * @result: (optional) (out caller-allocates): location to store decomposed result, or %NULL
     713                 :             :  * @result_len: length of @result
     714                 :             :  *
     715                 :             :  * Computes the canonical or compatibility decomposition of a
     716                 :             :  * Unicode character.  For compatibility decomposition,
     717                 :             :  * pass %TRUE for @compat; for canonical decomposition
     718                 :             :  * pass %FALSE for @compat.
     719                 :             :  *
     720                 :             :  * The decomposed sequence is placed in @result.  Only up to
     721                 :             :  * @result_len characters are written into @result.  The length
     722                 :             :  * of the full decomposition (irrespective of @result_len) is
     723                 :             :  * returned by the function.  For canonical decomposition,
     724                 :             :  * currently all decompositions are of length at most 4, but
     725                 :             :  * this may change in the future (very unlikely though).
     726                 :             :  * At any rate, Unicode does guarantee that a buffer of length
     727                 :             :  * 18 is always enough for both compatibility and canonical
     728                 :             :  * decompositions, so that is the size recommended. This is provided
     729                 :             :  * as %G_UNICHAR_MAX_DECOMPOSITION_LENGTH.
     730                 :             :  *
     731                 :             :  * See
     732                 :             :  * [UAX#15](http://unicode.org/reports/tr15/)
     733                 :             :  * for details.
     734                 :             :  *
     735                 :             :  * Returns: the length of the full decomposition.
     736                 :             :  *
     737                 :             :  * Since: 2.30
     738                 :             :  **/
     739                 :             : gsize
     740                 :     2228344 : g_unichar_fully_decompose (gunichar  ch,
     741                 :             :                            gboolean  compat,
     742                 :             :                            gunichar *result,
     743                 :             :                            gsize     result_len)
     744                 :             : {
     745                 :             :   const gchar *decomp;
     746                 :             :   const gchar *p;
     747                 :             : 
     748                 :             :   /* Hangul syllable */
     749                 :     2228344 :   if (ch >= SBase && ch < SBase + SCount)
     750                 :             :     {
     751                 :             :       gsize len, i;
     752                 :             :       gunichar buffer[3];
     753                 :       22372 :       decompose_hangul (ch, result ? buffer : NULL, &len);
     754                 :       22372 :       if (result)
     755                 :          66 :         for (i = 0; i < len && i < result_len; i++)
     756                 :          42 :           result[i] = buffer[i];
     757                 :       22372 :       return len;
     758                 :             :     }
     759                 :     2205972 :   else if ((decomp = find_decomposition (ch, compat)) != NULL)
     760                 :             :     {
     761                 :             :       /* Found it.  */
     762                 :             :       gsize len, i;
     763                 :             : 
     764                 :        8068 :       len = g_utf8_strlen (decomp, -1);
     765                 :             : 
     766                 :        8165 :       for (p = decomp, i = 0; i < len && i < result_len; p = g_utf8_next_char (p), i++)
     767                 :          97 :         result[i] = g_utf8_get_char (p);
     768                 :             : 
     769                 :        8068 :       return len;
     770                 :             :     }
     771                 :             : 
     772                 :             :   /* Does not decompose */
     773                 :     2197904 :   if (result && result_len >= 1)
     774                 :          14 :     *result = ch;
     775                 :     2197904 :   return 1;
     776                 :             : }
        

Generated by: LCOV version 2.0-1