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