Use G_N_ELEMENTS rather than a custom macro.
[platform/upstream/glib.git] / glib / gunidecomp.c
1 /* decomp.c - Character decomposition.
2  *
3  *  Copyright (C) 1999, 2000 Tom Tromey
4  *  Copyright 2000 Red Hat, Inc.
5  *
6  * The Gnome Library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * The Gnome Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  *   Boston, MA 02111-1307, USA.
20  */
21
22 #include "glib.h"
23 #include "gunidecomp.h"
24 #include "gunicomp.h"
25
26 #include <config.h>
27
28 #include <stdlib.h>
29
30 /* We cheat a bit and cast type values to (char *).  We detect these
31    using the &0xff trick.  */
32 #define CC(Page, Char) \
33   ((((GPOINTER_TO_INT(combining_class_table[Page])) & 0xff) \
34     == GPOINTER_TO_INT(combining_class_table[Page])) \
35    ? GPOINTER_TO_INT(combining_class_table[Page]) \
36    : (combining_class_table[Page][Char]))
37
38 #define COMBINING_CLASS(Char) \
39      (((Char) > (G_UNICODE_LAST_CHAR)) ? 0 : CC((Char) >> 8, (Char) & 0xff))
40
41 /* Compute the canonical ordering of a string in-place.  */
42 void
43 g_unicode_canonical_ordering (gunichar *string,
44                               size_t len)
45 {
46   size_t i;
47   int swap = 1;
48
49   while (swap)
50     {
51       int last;
52       swap = 0;
53       last = COMBINING_CLASS (string[0]);
54       for (i = 0; i < len - 1; ++i)
55         {
56           int next = COMBINING_CLASS (string[i + 1]);
57           if (next != 0 && last > next)
58             {
59               size_t j;
60               /* Percolate item leftward through string.  */
61               for (j = i; j > 0; --j)
62                 {
63                   gunichar t;
64                   if (COMBINING_CLASS (string[j]) <= next)
65                     break;
66                   t = string[j + 1];
67                   string[j + 1] = string[j];
68                   string[j] = t;
69                   swap = 1;
70                 }
71               /* We're re-entering the loop looking at the old
72                  character again.  */
73               next = last;
74             }
75           last = next;
76         }
77     }
78 }
79
80 guchar *
81 find_decomposition (gunichar ch,
82                     gboolean compat)
83 {
84   int start = 0;
85   int end = G_N_ELEMENTS (decomp_table);
86   
87   if (ch >= decomp_table[start].ch &&
88       ch <= decomp_table[end - 1].ch)
89     {
90       while (TRUE)
91         {
92           int half = (start + end) / 2;
93           if (ch == decomp_table[half].ch)
94             {
95               int offset;
96
97               if (compat)
98                 {
99                   offset = decomp_table[half].compat_offset;
100                   if (offset == 0xff)
101                     offset = decomp_table[half].canon_offset;
102                 }
103               else
104                 {
105                   offset = decomp_table[half].canon_offset;
106                   if (offset == 0xff)
107                     return NULL;
108                 }
109               
110               return decomp_table[half].expansion + offset;
111             }
112           else if (half == start)
113             break;
114           else if (ch > decomp_table[half].ch)
115             start = half;
116           else
117             end = half;
118         }
119     }
120
121   return NULL;
122 }
123
124 gunichar *
125 g_unicode_canonical_decomposition (gunichar ch,
126                                    size_t  *result_len)
127 {
128   guchar *decomp = find_decomposition (ch, FALSE);
129   gunichar *r;
130
131   if (decomp)
132     {
133       /* Found it.  */
134       int i, len;
135       /* We store as a double-nul terminated string.  */
136       for (len = 0; (decomp[len] || decomp[len + 1]);
137            len += 2)
138         ;
139       
140       /* We've counted twice as many bytes as there are
141          characters.  */
142       *result_len = len / 2;
143       r = malloc (len / 2 * sizeof (gunichar));
144       
145       for (i = 0; i < len; i += 2)
146         {
147           r[i / 2] = (decomp[i] << 8 | decomp[i + 1]);
148         }
149     }
150   else
151     {
152       /* Not in our table.  */
153       r = malloc (sizeof (gunichar));
154       *r = ch;
155       *result_len = 1;
156     }
157
158   /* Supposedly following the Unicode 2.1.9 table means that the
159      decompositions come out in canonical order.  I haven't tested
160      this, but we rely on it here.  */
161   return r;
162 }
163
164 #define CI(Page, Char) \
165   ((((GPOINTER_TO_INT(compose_table[Page])) & 0xff) \
166     == GPOINTER_TO_INT(compose_table[Page])) \
167    ? GPOINTER_TO_INT(compose_table[Page]) \
168    : (compose_table[Page][Char]))
169
170 #define COMPOSE_INDEX(Char) \
171      (((Char) > (G_UNICODE_LAST_CHAR)) ? 0 : CI((Char) >> 8, (Char) & 0xff))
172
173 gboolean
174 combine (gunichar  a,
175          gunichar  b,
176          gunichar *result)
177 {
178   gushort index_a, index_b;
179
180   index_a = COMPOSE_INDEX(a);
181   if (index_a >= COMPOSE_FIRST_SINGLE_START && index_a < COMPOSE_SECOND_START)
182     {
183       if (b == compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][0])
184         {
185           *result = compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][1];
186           return TRUE;
187         }
188       else
189         return FALSE;
190     }
191   
192   index_b = COMPOSE_INDEX(b);
193   if (index_b >= COMPOSE_SECOND_SINGLE_START)
194     {
195       if (a == compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][0])
196         {
197           *result = compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][1];
198           return TRUE;
199         }
200       else
201         return FALSE;
202     }
203
204   if (index_a >= COMPOSE_FIRST_START && index_a < COMPOSE_FIRST_SINGLE_START &&
205       index_b >= COMPOSE_SECOND_START && index_a < COMPOSE_SECOND_SINGLE_START)
206     {
207       gunichar res = compose_array[index_a - COMPOSE_FIRST_START][index_b - COMPOSE_SECOND_START];
208
209       if (res)
210         {
211           *result = res;
212           return TRUE;
213         }
214     }
215
216   return FALSE;
217 }
218
219 gunichar *
220 _g_utf8_normalize_wc (const gchar    *str,
221                       GNormalizeMode  mode)
222 {
223   gsize n_wc;
224   gunichar *wc_buffer;
225   const char *p;
226   gsize last_start;
227   gboolean do_compat = (mode == G_NORMALIZE_NFKC ||
228                         mode == G_NORMALIZE_NFKD);
229   gboolean do_compose = (mode == G_NORMALIZE_NFC ||
230                          mode == G_NORMALIZE_NFKC);
231
232   n_wc = 0;
233   p = str;
234   while (*p)
235     {
236       gunichar wc = g_utf8_get_char (p);
237
238       guchar *decomp = find_decomposition (wc, do_compat);
239
240       if (decomp)
241         {
242           int len;
243           /* We store as a double-nul terminated string.  */
244           for (len = 0; (decomp[len] || decomp[len + 1]);
245                len += 2)
246             ;
247           n_wc += len / 2;
248         }
249       else
250         n_wc++;
251
252       p = g_utf8_next_char (p);
253     }
254
255   wc_buffer = g_new (gunichar, n_wc + 1);
256
257   last_start = 0;
258   n_wc = 0;
259   p = str;
260   while (*p)
261     {
262       gunichar wc = g_utf8_get_char (p);
263       guchar *decomp;
264       int cc;
265       size_t old_n_wc = n_wc;
266           
267       decomp = find_decomposition (wc, do_compat);
268           
269       if (decomp)
270         {
271           int len;
272           /* We store as a double-nul terminated string.  */
273           for (len = 0; (decomp[len] || decomp[len + 1]);
274                len += 2)
275             wc_buffer[n_wc++] = (decomp[len] << 8 | decomp[len + 1]);
276         }
277       else
278         wc_buffer[n_wc++] = wc;
279
280       if (n_wc > 0)
281         {
282           cc = COMBINING_CLASS (wc_buffer[old_n_wc]);
283
284           if (cc == 0)
285             {
286               g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
287               last_start = old_n_wc;
288             }
289         }
290       
291       p = g_utf8_next_char (p);
292     }
293
294   if (n_wc > 0)
295     {
296       g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
297       last_start = n_wc;
298     }
299           
300   wc_buffer[n_wc] = 0;
301
302   /* All decomposed and reordered */ 
303
304
305   if (do_compose && n_wc > 0)
306     {
307       gsize i, j;
308       int last_cc = 0;
309       last_start = 0;
310       
311       for (i = 0; i < n_wc; i++)
312         {
313           int cc = COMBINING_CLASS (wc_buffer[i]);
314
315           if (i > 0 &&
316               (last_cc == 0 || last_cc != cc) &&
317               combine (wc_buffer[last_start], wc_buffer[i],
318                        &wc_buffer[last_start]))
319             {
320               for (j = i + 1; j < n_wc; j++)
321                 wc_buffer[j-1] = wc_buffer[j];
322               n_wc--;
323               i--;
324               
325               if (i == last_start)
326                 last_cc = 0;
327               else
328                 last_cc = COMBINING_CLASS (wc_buffer[i-1]);
329               
330               continue;
331             }
332
333           if (cc == 0)
334             last_start = i;
335
336           last_cc = cc;
337         }
338     }
339
340   wc_buffer[n_wc] = 0;
341
342   return wc_buffer;
343 }
344
345 /**
346  * g_utf8_normalize:
347  * @str: a UTF-8 encoded string.
348  * @mode: the type of normalization to perform.
349  * 
350  * Convert a string into canonical form, standardizing
351  * such issues as whether a character with an accent
352  * is represented as a base character and combining
353  * accent or as a single precomposed characters. You
354  * should generally call g_utf8_normalize before
355  * comparing two Unicode strings.
356  *
357  * The normalization mode %G_NORMALIZE_DEFAULT only
358  * standardizes differences that do not affect the
359  * text content, such as the above-mentioned accent
360  * representation. %G_NORMALIZE_ALL also standardizes
361  * the "compatibility" characters in Unicode, such
362  * as SUPERSCRIPT THREE to the standard forms
363  * (in this case DIGIT THREE). Formatting information
364  * may be lost but for most text operations such
365  * characters should be considered the same.
366  * For example, g_utf8_collate() normalizes
367  * with %G_NORMALIZE_ALL as its first step.
368  *
369  * %G_NORMALIZE_DEFAULT_COMPOSE and %G_NORMALIZE_ALL_COMPOSE
370  * are like %G_NORMALIZE_DEFAULT and %G_NORMALIZE_ALL,
371  * but returned a result with composed forms rather
372  * than a maximally decomposed form. This is often
373  * useful if you intend to convert the string to
374  * a legacy encoding or pass it to a system with
375  * less capable Unicode handling.
376  * 
377  * Return value: the string in normalized form
378  **/
379 gchar *
380 g_utf8_normalize (const gchar    *str,
381                   GNormalizeMode  mode)
382 {
383   gunichar *result_wc = _g_utf8_normalize_wc (str, mode);
384   gchar *result;
385   
386   result = g_ucs4_to_utf8 (result_wc, -1, NULL, NULL, NULL);
387   g_free (result_wc);
388
389   return result;
390 }