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 : : }
|