chiark / gitweb /
sync with disorder.dev
[disorder] / lib / unicode.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2007 Richard Kettlewell
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20 /** @file lib/unicode.c
21  * @brief Unicode support functions
22  *
23  * Here by UTF-8 and UTF-8 we mean the encoding forms of those names (not the
24  * encoding schemes).  The primary encoding form is UTF-32 but convenience
25  * wrappers using UTF-8 are provided for a number of functions.
26  *
27  * The idea is that all the strings that hit the database will be in a
28  * particular normalization form, and for the search and tags database
29  * in case-folded form, so they can be naively compared within the
30  * database code.
31  *
32  * As the code stands this guarantee is not well met!
33  *
34  * Subpages:
35  * - @ref utf32props
36  * - @ref utftransform
37  * - @ref utf32iterator
38  * - @ref utf32
39  * - @ref utf8
40  */
41
42 #include <config.h>
43 #include "types.h"
44
45 #include <string.h>
46 #include <stdio.h>              /* TODO */
47
48 #include "mem.h"
49 #include "vector.h"
50 #include "unicode.h"
51 #include "unidata.h"
52
53 /** @defgroup utf32props Unicode Code Point Properties */
54 /*@{*/
55
56 static const struct unidata *utf32__unidata_hard(uint32_t c);
57
58 /** @brief Find definition of code point @p c
59  * @param c Code point
60  * @return Pointer to @ref unidata structure for @p c
61  *
62  * @p c can be any 32-bit value, a sensible value will be returned regardless.
63  * The returned pointer is NOT guaranteed to be unique to @p c.
64  */
65 static inline const struct unidata *utf32__unidata(uint32_t c) {
66   /* The bottom half of the table contains almost everything of interest
67    * and we can just return the right thing straight away */
68   if(c < UNICODE_BREAK_START)
69     return &unidata[c / UNICODE_MODULUS][c % UNICODE_MODULUS];
70   else
71     return utf32__unidata_hard(c);
72 }
73
74 /** @brief Find definition of code point @p c
75  * @param c Code point
76  * @return Pointer to @ref unidata structure for @p c
77  *
78  * @p c can be any 32-bit value, a sensible value will be returned regardless.
79  * The returned pointer is NOT guaranteed to be unique to @p c.
80  *
81  * Don't use this function (although it will work fine) - use utf32__unidata()
82  * instead.
83  */
84 static const struct unidata *utf32__unidata_hard(uint32_t c) {
85   if(c < UNICODE_BREAK_START)
86     return &unidata[c / UNICODE_MODULUS][c % UNICODE_MODULUS];
87   /* Within the break everything is unassigned */
88   if(c < UNICODE_BREAK_END)
89     return utf32__unidata(0xFFFF);      /* guaranteed to be Cn */
90   /* Planes 15 and 16 are (mostly) private use */
91   if((c >= 0xF0000 && c <= 0xFFFFD)
92      || (c >= 0x100000 && c <= 0x10FFFD))
93     return utf32__unidata(0xE000);      /* first Co code point */
94   /* Everything else above the break top is unassigned */
95   if(c >= UNICODE_BREAK_TOP)
96     return utf32__unidata(0xFFFF);      /* guaranteed to be Cn */
97   /* Currently the rest is language tags and variation selectors */
98   c -= (UNICODE_BREAK_END - UNICODE_BREAK_START);
99   return &unidata[c / UNICODE_MODULUS][c % UNICODE_MODULUS];
100 }
101
102 /** @brief Return the combining class of @p c
103  * @param c Code point
104  * @return Combining class of @p c
105  *
106  * @p c can be any 32-bit value, a sensible value will be returned regardless.
107  */
108 static inline int utf32__combining_class(uint32_t c) {
109   return utf32__unidata(c)->ccc;
110 }
111
112 /** @brief Return the General_Category value for @p c
113  * @param c Code point
114  * @return General_Category property value
115  *
116  * @p c can be any 32-bit value, a sensible value will be returned regardless.
117  */
118 static inline enum unicode_General_Category utf32__general_category(uint32_t c) {
119   return utf32__unidata(c)->general_category;
120 }
121
122 /** @brief Determine Grapheme_Break property
123  * @param c Code point
124  * @return Grapheme_Break property value of @p c
125  *
126  * @p c can be any 32-bit value, a sensible value will be returned regardless.
127  */
128 static inline enum unicode_Grapheme_Break utf32__grapheme_break(uint32_t c) {
129   return utf32__unidata(c)->grapheme_break;
130 }
131
132 /** @brief Determine Word_Break property
133  * @param c Code point
134  * @return Word_Break property value of @p c
135  *
136  * @p c can be any 32-bit value, a sensible value will be returned regardless.
137  */
138 static inline enum unicode_Word_Break utf32__word_break(uint32_t c) {
139   return utf32__unidata(c)->word_break;
140 }
141
142 /** @brief Determine Sentence_Break property
143  * @param c Code point
144  * @return Word_Break property value of @p c
145  *
146  * @p c can be any 32-bit value, a sensible value will be returned regardless.
147  */
148 static inline enum unicode_Sentence_Break utf32__sentence_break(uint32_t c) {
149   return utf32__unidata(c)->sentence_break;
150 }
151
152 /** @brief Return true if @p c is ignorable for boundary specifications
153  * @param wb Word break property value
154  * @return non-0 if @p wb is unicode_Word_Break_Extend or unicode_Word_Break_Format
155  */
156 static inline int utf32__boundary_ignorable(enum unicode_Word_Break wb) {
157   return (wb == unicode_Word_Break_Extend
158           || wb == unicode_Word_Break_Format);
159 }
160
161 /** @brief Return the canonical decomposition of @p c
162  * @param c Code point
163  * @return 0-terminated canonical decomposition, or 0
164  */
165 static inline const uint32_t *utf32__decomposition_canon(uint32_t c) {
166   const struct unidata *const data = utf32__unidata(c);
167   const uint32_t *const decomp = data->decomp;
168
169   if(decomp && !(data->flags & unicode_compatibility_decomposition))
170     return decomp;
171   else
172     return 0;
173 }
174
175 /** @brief Return the compatibility decomposition of @p c
176  * @param c Code point
177  * @return 0-terminated decomposition, or 0
178  */
179 static inline const uint32_t *utf32__decomposition_compat(uint32_t c) {
180   return utf32__unidata(c)->decomp;
181 }
182
183 /*@}*/
184 /** @defgroup utftransform Functions that transform between different Unicode encoding forms */
185 /*@{*/
186
187 /** @brief Convert UTF-32 to UTF-8
188  * @param s Source string
189  * @param ns Length of source string in code points
190  * @param ndp Where to store length of destination string (or NULL)
191  * @return Newly allocated destination string or NULL on error
192  *
193  * If the UTF-32 is not valid then NULL is returned.  A UTF-32 code point is
194  * invalid if:
195  * - it codes for a UTF-16 surrogate
196  * - it codes for a value outside the unicode code space
197  *
198  * The return value is always 0-terminated.  The value returned via @p *ndp
199  * does not include the terminator.
200  */
201 char *utf32_to_utf8(const uint32_t *s, size_t ns, size_t *ndp) {
202   struct dynstr d;
203   uint32_t c;
204
205   dynstr_init(&d);
206   while(ns > 0) {
207     c = *s++;
208     if(c < 0x80)
209       dynstr_append(&d, c);
210     else if(c < 0x0800) {
211       dynstr_append(&d, 0xC0 | (c >> 6));
212       dynstr_append(&d, 0x80 | (c & 0x3F));
213     } else if(c < 0x10000) {
214       if(c >= 0xD800 && c <= 0xDFFF)
215         goto error;
216       dynstr_append(&d, 0xE0 | (c >> 12));
217       dynstr_append(&d, 0x80 | ((c >> 6) & 0x3F));
218       dynstr_append(&d, 0x80 | (c & 0x3F));
219     } else if(c < 0x110000) {
220       dynstr_append(&d, 0xF0 | (c >> 18));
221       dynstr_append(&d, 0x80 | ((c >> 12) & 0x3F));
222       dynstr_append(&d, 0x80 | ((c >> 6) & 0x3F));
223       dynstr_append(&d, 0x80 | (c & 0x3F));
224     } else
225       goto error;
226     --ns;
227   }
228   dynstr_terminate(&d);
229   if(ndp)
230     *ndp = d.nvec;
231   return d.vec;
232 error:
233   xfree(d.vec);
234   return 0;
235 }
236
237 /** @brief Convert UTF-8 to UTF-32
238  * @param s Source string
239  * @param ns Length of source string in code points
240  * @param ndp Where to store length of destination string (or NULL)
241  * @return Newly allocated destination string or NULL on error
242  *
243  * The return value is always 0-terminated.  The value returned via @p *ndp
244  * does not include the terminator.
245  *
246  * If the UTF-8 is not valid then NULL is returned.  A UTF-8 sequence
247  * for a code point is invalid if:
248  * - it is not the shortest possible sequence for the code point
249  * - it codes for a UTF-16 surrogate
250  * - it codes for a value outside the unicode code space
251  */
252 uint32_t *utf8_to_utf32(const char *s, size_t ns, size_t *ndp) {
253   struct dynstr_ucs4 d;
254   uint32_t c32;
255   const uint8_t *ss = (const uint8_t *)s;
256   int n;
257
258   dynstr_ucs4_init(&d);
259   while(ns > 0) {
260     const struct unicode_utf8_row *const r = &unicode_utf8_valid[*ss];
261     if(r->count <= ns) {
262       switch(r->count) {
263       case 1:
264         c32 = *ss;
265         break;
266       case 2:
267         if(ss[1] < r->min2 || ss[1] > r->max2)
268           goto error;
269         c32 = *ss & 0x1F;
270         break;
271       case 3:
272         if(ss[1] < r->min2 || ss[1] > r->max2)
273           goto error;
274         c32 = *ss & 0x0F;
275         break;
276       case 4:
277         if(ss[1] < r->min2 || ss[1] > r->max2)
278           goto error;
279         c32 = *ss & 0x07;
280         break;
281       default:
282         goto error;
283       }
284     } else
285       goto error;
286     for(n = 1; n < r->count; ++n) {
287       if(ss[n] < 0x80 || ss[n] > 0xBF)
288         goto error;
289       c32 = (c32 << 6) | (ss[n] & 0x3F);
290     }
291     dynstr_ucs4_append(&d, c32);
292     ss += r->count;
293     ns -= r->count;
294   }
295   dynstr_ucs4_terminate(&d);
296   if(ndp)
297     *ndp = d.nvec;
298   return d.vec;
299 error:
300   xfree(d.vec);
301   return 0;
302 }
303
304 /** @brief Test whether [s,s+ns) is valid UTF-8
305  * @param s Start of string
306  * @param ns Length of string
307  * @return non-0 if @p s is valid UTF-8, 0 if it is not valid
308  *
309  * This function is intended to be much faster than calling utf8_to_utf32() and
310  * throwing away the result.
311  */
312 int utf8_valid(const char *s, size_t ns) {
313   const uint8_t *ss = (const uint8_t *)s;
314   while(ns > 0) {
315     const struct unicode_utf8_row *const r = &unicode_utf8_valid[*ss];
316     if(r->count <= ns) {
317       switch(r->count) {
318       case 1:
319         break;
320       case 2:
321         if(ss[1] < r->min2 || ss[1] > r->max2)
322           return 0;
323         break;
324       case 3:
325         if(ss[1] < r->min2 || ss[1] > r->max2)
326           return 0;
327         if(ss[2] < 0x80 || ss[2] > 0xBF)
328           return 0;
329         break;
330       case 4:
331         if(ss[1] < r->min2 || ss[1] > r->max2)
332           return 0;
333         if(ss[2] < 0x80 || ss[2] > 0xBF)
334           return 0;
335         if(ss[3] < 0x80 || ss[3] > 0xBF)
336           return 0;
337         break;
338       default:
339         return 0;
340       }
341     } else
342       return 0;
343     ss += r->count;
344     ns -= r->count;
345   }
346   return 1;
347 }
348
349 /*@}*/
350 /** @defgroup utf32iterator UTF-32 string iterators */
351 /*@{*/
352
353 struct utf32_iterator_data {
354   /** @brief Start of string */
355   const uint32_t *s;
356
357   /** @brief Length of string */
358   size_t ns;
359
360   /** @brief Current position */
361   size_t n;
362
363   /** @brief Last two non-ignorable characters or (uint32_t)-1
364    *
365    * last[1] is the non-Extend/Format character just before position @p n;
366    * last[0] is the one just before that.
367    *
368    * Exception 1: if there is no such non-Extend/Format character then an
369    * Extend/Format character is accepted instead.
370    *
371    * Exception 2: if there is no such character even taking that into account
372    * the value is (uint32_t)-1.
373    */
374   uint32_t last[2];
375 };
376
377 /** @brief Create a new iterator pointing at the start of a string
378  * @param s Start of string
379  * @param ns Length of string
380  * @return New iterator
381  */
382 utf32_iterator utf32_iterator_new(const uint32_t *s, size_t ns) {
383   utf32_iterator it = xmalloc(sizeof *it);
384   it->s = s;
385   it->ns = ns;
386   it->n = 0;
387   it->last[0] = it->last[1] = -1;
388   return it;
389 }
390
391 /** @brief Initialize an internal private iterator
392  * @param it Iterator
393  * @param s Start of string
394  * @param ns Length of string
395  * @param n Absolute position
396  */
397 static void utf32__iterator_init(utf32_iterator it,
398                                  const uint32_t *s, size_t ns, size_t n) {
399   it->s = s;
400   it->ns = ns;
401   it->n = 0;
402   it->last[0] = it->last[1] = -1;
403   utf32_iterator_set(it, n);
404 }
405
406 /** @brief Destroy an iterator
407  * @param it Iterator
408  */
409 void utf32_iterator_destroy(utf32_iterator it) {
410   xfree(it);
411 }
412
413 /** @brief Find the current position of an interator
414  * @param it Iterator
415  */
416 size_t utf32_iterator_where(utf32_iterator it) {
417   return it->n;
418 }
419
420 /** @brief Set an iterator's absolute position
421  * @param it Iterator
422  * @param n Absolute position
423  * @return 0 on success, non-0 on error
424  *
425  * It is an error to position the iterator outside the string (but acceptable
426  * to point it at the hypothetical post-final character).  If an invalid value
427  * of @p n is specified then the iterator is not changed.
428  *
429  * This function works by backing up and then advancing to reconstruct the
430  * iterator's internal state for position @p n.  The worst case will be O(n)
431  * time complexity (with a worse constant factor that utf32_iterator_advance())
432  * but the typical case is essentially constant-time.
433  */
434 int utf32_iterator_set(utf32_iterator it, size_t n) {
435   /* We can't just jump to position @p n; the @p last[] values will be wrong.
436    * What we need is to jump a bit behind @p n and then advance forward,
437    * updating @p last[] along the way.  How far back?  We need to cross two
438    * non-ignorable code points as we advance forwards, so we'd better pass two
439    * such characters on the way back (if such are available).
440    */
441   size_t m;
442
443   if(n > it->ns)                        /* range check */
444     return -1;
445   /* Walk backwards skipping ignorable code points */
446   m = n;
447   while(m > 0 && (utf32__boundary_ignorable(utf32__word_break(it->s[m-1]))))
448     --m;
449   /* Either m=0 or s[m-1] is not ignorable */
450   if(m > 0) {
451     --m;
452     /* s[m] is our first non-ignorable code; look for a second in the same
453        way **/
454     while(m > 0 && (utf32__boundary_ignorable(utf32__word_break(it->s[m-1]))))
455       --m;
456     /* Either m=0 or s[m-1] is not ignorable */
457     if(m > 0)
458       --m;
459   }
460   it->last[0] = it->last[1] = -1;
461   it->n = m;
462   return utf32_iterator_advance(it, n - m);
463 }
464
465 /** @brief Advance an iterator
466  * @param it Iterator
467  * @param count Number of code points to advance by
468  * @return 0 on success, non-0 on error
469  *
470  * It is an error to advance an iterator beyond the hypothetical post-final
471  * character of the string.  If an invalid value of @p n is specified then the
472  * iterator is not changed.
473  *
474  * This function has O(n) time complexity: it works by advancing naively
475  * forwards through the string.
476  */
477 int utf32_iterator_advance(utf32_iterator it, size_t count) {
478   if(count <= it->ns - it->n) {
479     while(count > 0) {
480       const uint32_t c = it->s[it->n];
481       const enum unicode_Word_Break wb = utf32__word_break(c);
482       if(it->last[1] == (uint32_t)-1
483          || !utf32__boundary_ignorable(wb)) {
484         it->last[0] = it->last[1];
485         it->last[1] = c;
486       }
487       ++it->n;
488       --count;
489     }
490     return 0;
491   } else
492     return -1;
493 }
494
495 /** @brief Find the current code point
496  * @param it Iterator
497  * @return Current code point or 0
498  *
499  * If the iterator points at the hypothetical post-final character of the
500  * string then 0 is returned.  NB that this doesn't mean that there aren't any
501  * 0 code points inside the string!
502  */
503 uint32_t utf32_iterator_code(utf32_iterator it) {
504   if(it->n < it->ns)
505     return it->s[it->n];
506   else
507     return 0;
508 }
509
510 /** @brief Test for a grapheme boundary
511  * @param it Iterator
512  * @return Non-0 if pointing just after a grapheme boundary, otherwise 0
513  *
514  * This function identifies default grapheme cluster boundaries as described in
515  * UAX #29 s3.  It returns non-0 if @p it points at the code point just after a
516  * grapheme cluster boundary (including the hypothetical code point just after
517  * the end of the string).
518  */
519 int utf32_iterator_grapheme_boundary(utf32_iterator it) {
520   uint32_t before, after;
521   enum unicode_Grapheme_Break gbbefore, gbafter;
522   /* GB1 and GB2 */
523   if(it->n == 0 || it->n == it->ns)
524     return 1;
525   /* Now we know that s[n-1] and s[n] are safe to inspect */
526   /* GB3 */
527   before = it->s[it->n-1];
528   after = it->s[it->n];
529   if(before == 0x000D && after == 0x000A)
530     return 0;
531   gbbefore = utf32__grapheme_break(before);
532   gbafter = utf32__grapheme_break(after);
533   /* GB4 */
534   if(gbbefore == unicode_Grapheme_Break_Control
535      || before == 0x000D
536      || before == 0x000A)
537     return 1;
538   /* GB5 */
539   if(gbafter == unicode_Grapheme_Break_Control
540      || after == 0x000D
541      || after == 0x000A)
542     return 1;
543   /* GB6 */
544   if(gbbefore == unicode_Grapheme_Break_L
545      && (gbafter == unicode_Grapheme_Break_L
546          || gbafter == unicode_Grapheme_Break_V
547          || gbafter == unicode_Grapheme_Break_LV
548          || gbafter == unicode_Grapheme_Break_LVT))
549     return 0;
550   /* GB7 */
551   if((gbbefore == unicode_Grapheme_Break_LV
552       || gbbefore == unicode_Grapheme_Break_V)
553      && (gbafter == unicode_Grapheme_Break_V
554          || gbafter == unicode_Grapheme_Break_T))
555     return 0;
556   /* GB8 */
557   if((gbbefore == unicode_Grapheme_Break_LVT
558       || gbbefore == unicode_Grapheme_Break_T)
559      && gbafter == unicode_Grapheme_Break_T)
560     return 0;
561   /* GB9 */
562   if(gbafter == unicode_Grapheme_Break_Extend)
563     return 0;
564   /* GB10 */
565   return 1;
566
567 }
568
569 /** @brief Test for a word boundary
570  * @param it Iterator
571  * @return Non-0 if pointing just after a word boundary, otherwise 0
572  *
573  * This function identifies default word boundaries as described in UAX #29 s4.
574  * It returns non-0 if @p it points at the code point just after a word
575  * boundary (including the hypothetical code point just after the end of the
576  * string) and 0 otherwise.
577  */
578 int utf32_iterator_word_boundary(utf32_iterator it) {
579   enum unicode_Word_Break twobefore, before, after, twoafter;
580   size_t nn;
581
582   /* WB1 and WB2 */
583   if(it->n == 0 || it->n == it->ns)
584     return 1;
585   /* WB3 */
586   if(it->s[it->n-1] == 0x000D && it->s[it->n] == 0x000A)
587     return 0;
588   /* WB4 */
589   /* (!Sep) x (Extend|Format) as in UAX #29 s6.2 */
590   if(utf32__sentence_break(it->s[it->n-1]) != unicode_Sentence_Break_Sep
591      && utf32__boundary_ignorable(utf32__word_break(it->s[it->n])))
592     return 0;
593   /* Gather the property values we'll need for the rest of the test taking the
594    * s6.2 changes into account */
595   /* First we look at the code points after the proposed boundary */
596   nn = it->n;                           /* <it->ns */
597   after = utf32__word_break(it->s[nn++]);
598   if(!utf32__boundary_ignorable(after)) {
599     /* X (Extend|Format)* -> X */
600     while(nn < it->ns
601           && utf32__boundary_ignorable(utf32__word_break(it->s[nn])))
602       ++nn;
603   }
604   /* It's possible now that nn=ns */
605   if(nn < it->ns)
606     twoafter = utf32__word_break(it->s[nn]);
607   else
608     twoafter = unicode_Word_Break_Other;
609
610   /* We've already recorded the non-ignorable code points before the proposed
611    * boundary */
612   before = utf32__word_break(it->last[1]);
613   twobefore = utf32__word_break(it->last[0]);
614
615   /* WB5 */
616   if(before == unicode_Word_Break_ALetter
617      && after == unicode_Word_Break_ALetter)
618     return 0;
619   /* WB6 */
620   if(before == unicode_Word_Break_ALetter
621      && after == unicode_Word_Break_MidLetter
622      && twoafter == unicode_Word_Break_ALetter)
623     return 0;
624   /* WB7 */
625   if(twobefore == unicode_Word_Break_ALetter
626      && before == unicode_Word_Break_MidLetter
627      && after == unicode_Word_Break_ALetter)
628     return 0;
629   /* WB8 */  
630   if(before == unicode_Word_Break_Numeric
631      && after == unicode_Word_Break_Numeric)
632     return 0;
633   /* WB9 */
634   if(before == unicode_Word_Break_ALetter
635      && after == unicode_Word_Break_Numeric)
636     return 0;
637   /* WB10 */
638   if(before == unicode_Word_Break_Numeric
639      && after == unicode_Word_Break_ALetter)
640     return 0;
641    /* WB11 */
642   if(twobefore == unicode_Word_Break_Numeric
643      && before == unicode_Word_Break_MidNum
644      && after == unicode_Word_Break_Numeric)
645     return 0;
646   /* WB12 */
647   if(before == unicode_Word_Break_Numeric
648      && after == unicode_Word_Break_MidNum
649      && twoafter == unicode_Word_Break_Numeric)
650     return 0;
651   /* WB13 */
652   if(before == unicode_Word_Break_Katakana
653      && after == unicode_Word_Break_Katakana)
654     return 0;
655   /* WB13a */
656   if((before == unicode_Word_Break_ALetter
657       || before == unicode_Word_Break_Numeric
658       || before == unicode_Word_Break_Katakana
659       || before == unicode_Word_Break_ExtendNumLet)
660      && after == unicode_Word_Break_ExtendNumLet)
661     return 0;
662   /* WB13b */
663   if(before == unicode_Word_Break_ExtendNumLet
664      && (after == unicode_Word_Break_ALetter
665          || after == unicode_Word_Break_Numeric
666          || after == unicode_Word_Break_Katakana))
667     return 0;
668   /* WB14 */
669   return 1;
670 }
671
672 /*@}*/
673 /** @defgroup utf32 Functions that operate on UTF-32 strings */
674 /*@{*/
675
676 /** @brief Return the length of a 0-terminated UTF-32 string
677  * @param s Pointer to 0-terminated string
678  * @return Length of string in code points (excluding terminator)
679  *
680  * Unlike the conversion functions no validity checking is done on the string.
681  */
682 size_t utf32_len(const uint32_t *s) {
683   const uint32_t *t = s;
684
685   while(*t)
686     ++t;
687   return (size_t)(t - s);
688 }
689
690 /** @brief Stably sort [s,s+ns) into descending order of combining class
691  * @param s Start of array
692  * @param ns Number of elements, must be at least 1
693  * @param buffer Buffer of at least @p ns elements
694  */
695 static void utf32__sort_ccc(uint32_t *s, size_t ns, uint32_t *buffer) {
696   uint32_t *a, *b, *bp;
697   size_t na, nb;
698
699   switch(ns) {
700   case 1:                       /* 1-element array is always sorted */
701     return;
702   case 2:                       /* 2-element arrays are trivial to sort */
703     if(utf32__combining_class(s[0]) > utf32__combining_class(s[1])) {
704       uint32_t tmp = s[0];
705       s[0] = s[1];
706       s[1] = tmp;
707     }
708     return;
709   default:
710     /* Partition the array */
711     na = ns / 2;
712     nb = ns - na;
713     a = s;
714     b = s + na;
715     /* Sort the two halves of the array */
716     utf32__sort_ccc(a, na, buffer);
717     utf32__sort_ccc(b, nb, buffer);
718     /* Merge them back into one, via the buffer */
719     bp = buffer;
720     while(na > 0 && nb > 0) {
721       /* We want ascending order of combining class (hence <)
722        * and we want stability within combining classes (hence <=)
723        */
724       if(utf32__combining_class(*a) <= utf32__combining_class(*b)) {
725         *bp++ = *a++;
726         --na;
727       } else {
728         *bp++ = *b++;
729         --nb;
730       }
731     }
732     while(na > 0) {
733       *bp++ = *a++;
734       --na;
735     }
736     while(nb > 0) {
737       *bp++ = *b++;
738       --nb;
739     }
740     memcpy(s, buffer,  ns * sizeof(uint32_t));
741     return;
742   }
743 }
744
745 /** @brief Put combining characters into canonical order
746  * @param s Pointer to UTF-32 string
747  * @param ns Length of @p s
748  * @return 0 on success, non-0 on error
749  *
750  * @p s is modified in-place.  See Unicode 5.0 s3.11 for details of the
751  * ordering.
752  *
753  * Currently we only support a maximum of 1024 combining characters after each
754  * base character.  If this limit is exceeded then a non-0 value is returned.
755  */
756 static int utf32__canonical_ordering(uint32_t *s, size_t ns) {
757   size_t nc;
758   uint32_t buffer[1024];
759
760   /* The ordering amounts to a stable sort of each contiguous group of
761    * characters with non-0 combining class. */
762   while(ns > 0) {
763     /* Skip non-combining characters */
764     if(utf32__combining_class(*s) == 0) {
765       ++s;
766       --ns;
767       continue;
768     }
769     /* We must now have at least one combining character; see how many
770      * there are */
771     for(nc = 1; nc < ns && utf32__combining_class(s[nc]) != 0; ++nc)
772       ;
773     if(nc > 1024)
774       return -1;
775     /* Sort the array */
776     utf32__sort_ccc(s, nc, buffer);
777     s += nc;
778     ns -= nc;
779   }
780   return 0;
781 }
782
783 /* Magic numbers from UAX #15 s16 */
784 #define SBase 0xAC00
785 #define LBase 0x1100
786 #define VBase 0x1161
787 #define TBase 0x11A7
788 #define LCount 19
789 #define VCount 21
790 #define TCount 28
791 #define NCount (VCount * TCount)
792 #define SCount (LCount * NCount)
793
794 /** @brief Guts of the decomposition lookup functions */
795 #define utf32__decompose_one_generic(WHICH) do {                        \
796   const uint32_t *dc = utf32__decomposition_##WHICH(c);                 \
797   if(dc) {                                                              \
798     /* Found a canonical decomposition in the table */                  \
799     while(*dc)                                                          \
800       utf32__decompose_one_##WHICH(d, *dc++);                           \
801   } else if(c >= SBase && c < SBase + SCount) {                         \
802     /* Mechanically decomposable Hangul syllable (UAX #15 s16) */       \
803     const uint32_t SIndex = c - SBase;                                  \
804     const uint32_t L = LBase + SIndex / NCount;                         \
805     const uint32_t V = VBase + (SIndex % NCount) / TCount;              \
806     const uint32_t T = TBase + SIndex % TCount;                         \
807     dynstr_ucs4_append(d, L);                                           \
808     dynstr_ucs4_append(d, V);                                           \
809     if(T != TBase)                                                      \
810       dynstr_ucs4_append(d, T);                                         \
811   } else                                                                \
812     /* Equal to own canonical decomposition */                          \
813     dynstr_ucs4_append(d, c);                                           \
814 } while(0)
815
816 /** @brief Recursively compute the canonical decomposition of @p c
817  * @param d Dynamic string to store decomposition in
818  * @param c Code point to decompose (must be a valid!)
819  * @return 0 on success, non-0 on error
820  */
821 static void utf32__decompose_one_canon(struct dynstr_ucs4 *d, uint32_t c) {
822   utf32__decompose_one_generic(canon);
823 }
824
825 /** @brief Recursively compute the compatibility decomposition of @p c
826  * @param d Dynamic string to store decomposition in
827  * @param c Code point to decompose (must be a valid!)
828  * @return 0 on success, non-0 on error
829  */
830 static void utf32__decompose_one_compat(struct dynstr_ucs4 *d, uint32_t c) {
831   utf32__decompose_one_generic(compat);
832 }
833
834 /** @brief Magic utf32__compositions() return value for Hangul Choseong */
835 static const uint32_t utf32__hangul_L[1];
836
837 /** @brief Return the list of compositions that @p c starts
838  * @param c Starter code point
839  * @return Composition list or NULL
840  *
841  * For Hangul leading (Choseong) jamo we return the special value
842  * utf32__hangul_L.  These code points are not listed as the targets of
843  * canonical decompositions (make-unidata checks) so there is no confusion with
844  * real decompositions here.
845  */
846 static const uint32_t *utf32__compositions(uint32_t c) {
847   const uint32_t *compositions = utf32__unidata(c)->composed;
848
849   if(compositions)
850     return compositions;
851   /* Special-casing for Hangul */
852   switch(utf32__grapheme_break(c)) {
853   default:
854     return 0;
855   case unicode_Grapheme_Break_L:
856     return utf32__hangul_L;
857   }
858 }
859
860 /** @brief Composition step
861  * @param s Start of string
862  * @param ns Length of string
863  * @return New length of string
864  *
865  * This is called from utf32__decompose_generic() to compose the result string
866  * in place.
867  */
868 static size_t utf32__compose(uint32_t *s, size_t ns) {
869   const uint32_t *compositions;
870   uint32_t *start = s, *t = s, *tt, cc;
871
872   while(ns > 0) {
873     uint32_t starter = *s++;
874     int block_starters = 0;
875     --ns;
876     /* We don't attempt to compose the following things:
877      * - final characters whatever kind they are
878      * - non-starter characters
879      * - starters that don't take part in a canonical decomposition mapping
880      */
881     if(ns == 0
882        || utf32__combining_class(starter)
883        || !(compositions = utf32__compositions(starter))) {
884       *t++ = starter;
885       continue;
886     }
887     if(compositions != utf32__hangul_L) {
888       /* Where we'll put the eventual starter */
889       tt = t++;
890       do {
891         /* See if we can find composition of starter+*s */
892         const uint32_t cchar = *s, *cp = compositions;
893         while((cc = *cp++)) {
894           const uint32_t *decomp = utf32__decomposition_canon(cc);
895           /* We know decomp[0] == starter */
896           if(decomp[1] == cchar)
897             break;
898         }
899         if(cc) {
900           /* Found a composition: cc decomposes to starter,*s */
901           starter = cc;
902           compositions = utf32__compositions(starter);
903           ++s;
904           --ns;
905         } else {
906           /* No composition found. */
907           const int class = utf32__combining_class(*s);
908           if(class) {
909             /* Transfer the uncomposable combining character to the output */
910             *t++ = *s++;
911             --ns;
912             /* All the combining characters of the same class of the
913              * uncomposable character are blocked by it, but there may be
914              * others of higher class later.  We eat the uncomposable and
915              * blocked characters and go back round the loop for that higher
916              * class. */
917             while(ns > 0 && utf32__combining_class(*s) == class) {
918               *t++ = *s++;
919               --ns;
920             }
921             /* Block any subsequent starters */
922             block_starters = 1;
923           } else {
924             /* The uncombinable character is itself a starter, so we don't
925              * transfer it to the output but instead go back round the main
926              * loop. */
927             break;
928           }
929         }
930         /* Keep going while there are still characters and the starter takes
931          * part in some composition */
932       } while(ns > 0 && compositions
933               && (!block_starters || utf32__combining_class(*s)));
934       /* Store any remaining combining characters */
935       while(ns > 0 && utf32__combining_class(*s)) {
936         *t++ = *s++;
937         --ns;
938       }
939       /* Store the resulting starter */
940       *tt = starter;
941     } else {
942       /* Special-casing for Hangul
943        *
944        * If there are combining characters between the L and the V then they
945        * will block the V and so no composition happens.  Similarly combining
946        * characters between V and T will block the T and so we only get as far
947        * as LV.
948        */
949       if(utf32__grapheme_break(*s) == unicode_Grapheme_Break_V) {
950         const uint32_t V = *s++;
951         const uint32_t LIndex = starter - LBase;
952         const uint32_t VIndex = V - VBase;
953         uint32_t TIndex;
954         --ns;
955         if(ns > 0
956            && utf32__grapheme_break(*s) == unicode_Grapheme_Break_T) {
957           /* We have an L V T sequence */
958           const uint32_t T = *s++;
959           TIndex = T - TBase;
960           --ns;
961         } else
962           /* It's just L V */
963           TIndex = 0;
964         /* Compose to LVT or LV as appropriate */
965         starter = (LIndex * VCount + VIndex) * TCount + TIndex + SBase;
966       } /* else we only have L or LV and no V or T */
967       *t++ = starter;
968       /* There could be some combining characters that belong to the V or T.
969        * These will be treated as non-starter characters at the top of the loop
970        * and thuss transferred to the output. */
971     }
972   }
973   return t - start;
974 }
975
976 /** @brief Guts of the composition and decomposition functions
977  * @param WHICH @c canon or @c compat to choose decomposition
978  * @param COMPOSE @c 0 or @c 1 to compose
979  */
980 #define utf32__decompose_generic(WHICH, COMPOSE) do {   \
981   struct dynstr_ucs4 d;                                 \
982   uint32_t c;                                           \
983                                                         \
984   dynstr_ucs4_init(&d);                                 \
985   while(ns) {                                           \
986     c = *s++;                                           \
987     if((c >= 0xD800 && c <= 0xDFFF) || c > 0x10FFFF)    \
988       goto error;                                       \
989     utf32__decompose_one_##WHICH(&d, c);                \
990     --ns;                                               \
991   }                                                     \
992   if(utf32__canonical_ordering(d.vec, d.nvec))          \
993     goto error;                                         \
994   if(COMPOSE)                                           \
995     d.nvec = utf32__compose(d.vec, d.nvec);             \
996   dynstr_ucs4_terminate(&d);                            \
997   if(ndp)                                               \
998     *ndp = d.nvec;                                      \
999   return d.vec;                                         \
1000 error:                                                  \
1001   xfree(d.vec);                                         \
1002   return 0;                                             \
1003 } while(0)
1004
1005 /** @brief Canonically decompose @p [s,s+ns)
1006  * @param s Pointer to string
1007  * @param ns Length of string
1008  * @param ndp Where to store length of result
1009  * @return Pointer to result string, or NULL on error
1010  *
1011  * Computes NFD (Normalization Form D) of the string at @p s.  This implies
1012  * performing all canonical decompositions and then normalizing the order of
1013  * combining characters.
1014  *
1015  * Returns NULL if the string is not valid for either of the following reasons:
1016  * - it codes for a UTF-16 surrogate
1017  * - it codes for a value outside the unicode code space
1018  *
1019  * See also:
1020  * - utf32_decompose_compat()
1021  * - utf32_compose_canon()
1022  */
1023 uint32_t *utf32_decompose_canon(const uint32_t *s, size_t ns, size_t *ndp) {
1024   utf32__decompose_generic(canon, 0);
1025 }
1026
1027 /** @brief Compatibility decompose @p [s,s+ns)
1028  * @param s Pointer to string
1029  * @param ns Length of string
1030  * @param ndp Where to store length of result
1031  * @return Pointer to result string, or NULL on error
1032  *
1033  * Computes NFKD (Normalization Form KD) of the string at @p s.  This implies
1034  * performing all canonical and compatibility decompositions and then
1035  * normalizing the order of combining characters.
1036  *
1037  * Returns NULL if the string is not valid for either of the following reasons:
1038  * - it codes for a UTF-16 surrogate
1039  * - it codes for a value outside the unicode code space
1040  *
1041  * See also:
1042  * - utf32_decompose_canon()
1043  * - utf32_compose_compat()
1044  */
1045 uint32_t *utf32_decompose_compat(const uint32_t *s, size_t ns, size_t *ndp) {
1046   utf32__decompose_generic(compat, 0);
1047 }
1048
1049 /** @brief Canonically compose @p [s,s+ns)
1050  * @param s Pointer to string
1051  * @param ns Length of string
1052  * @param ndp Where to store length of result
1053  * @return Pointer to result string, or NULL on error
1054  *
1055  * Computes NFC (Normalization Form C) of the string at @p s.  This implies
1056  * performing all canonical decompositions, normalizing the order of combining
1057  * characters and then composing all unblocked primary compositables.
1058  *
1059  * Returns NULL if the string is not valid for either of the following reasons:
1060  * - it codes for a UTF-16 surrogate
1061  * - it codes for a value outside the unicode code space
1062  *
1063  * See also:
1064  * - utf32_compose_compat()
1065  * - utf32_decompose_canon()
1066  */
1067 uint32_t *utf32_compose_canon(const uint32_t *s, size_t ns, size_t *ndp) {
1068   utf32__decompose_generic(canon, 1);
1069 }
1070
1071 /** @brief Compatibility compose @p [s,s+ns)
1072  * @param s Pointer to string
1073  * @param ns Length of string
1074  * @param ndp Where to store length of result
1075  * @return Pointer to result string, or NULL on error
1076  *
1077  * Computes NFKC (Normalization Form KC) of the string at @p s.  This implies
1078  * performing all canonical and compatibility decompositions, normalizing the
1079  * order of combining characters and then composing all unblocked primary
1080  * compositables.
1081  *
1082  * Returns NULL if the string is not valid for either of the following reasons:
1083  * - it codes for a UTF-16 surrogate
1084  * - it codes for a value outside the unicode code space
1085  *
1086  * See also:
1087  * - utf32_compose_canon()
1088  * - utf32_decompose_compat()
1089  */
1090 uint32_t *utf32_compose_compat(const uint32_t *s, size_t ns, size_t *ndp) {
1091   utf32__decompose_generic(compat, 1);
1092 }
1093
1094 /** @brief Single-character case-fold and decompose operation */
1095 #define utf32__casefold_one(WHICH) do {                                 \
1096   const uint32_t *cf = utf32__unidata(c)->casefold;                     \
1097   if(cf) {                                                              \
1098     /* Found a case-fold mapping in the table */                        \
1099     while(*cf)                                                          \
1100       utf32__decompose_one_##WHICH(&d, *cf++);                          \
1101   } else                                                                \
1102     utf32__decompose_one_##WHICH(&d, c);                                \
1103 } while(0)
1104
1105 /** @brief Case-fold @p [s,s+ns)
1106  * @param s Pointer to string
1107  * @param ns Length of string
1108  * @param ndp Where to store length of result
1109  * @return Pointer to result string, or NULL on error
1110  *
1111  * Case-fold the string at @p s according to full default case-folding rules
1112  * (s3.13) for caseless matching.  The result will be in NFD.
1113  *
1114  * Returns NULL if the string is not valid for either of the following reasons:
1115  * - it codes for a UTF-16 surrogate
1116  * - it codes for a value outside the unicode code space
1117  */
1118 uint32_t *utf32_casefold_canon(const uint32_t *s, size_t ns, size_t *ndp) {
1119   struct dynstr_ucs4 d;
1120   uint32_t c;
1121   size_t n;
1122   uint32_t *ss = 0;
1123
1124   /* If the canonical decomposition of the string includes any combining
1125    * character that case-folds to a non-combining character then we must
1126    * normalize before we fold.  In Unicode 5.0.0 this means 0345 COMBINING
1127    * GREEK YPOGEGRAMMENI in its decomposition and the various characters that
1128    * canonically decompose to it. */
1129   for(n = 0; n < ns; ++n)
1130     if(utf32__unidata(s[n])->flags & unicode_normalize_before_casefold)
1131       break;
1132   if(n < ns) {
1133     /* We need a preliminary decomposition */
1134     if(!(ss = utf32_decompose_canon(s, ns, &ns)))
1135       return 0;
1136     s = ss;
1137   }
1138   dynstr_ucs4_init(&d);
1139   while(ns) {
1140     c = *s++;
1141     if((c >= 0xD800 && c <= 0xDFFF) || c > 0x10FFFF)
1142       goto error;
1143     utf32__casefold_one(canon);
1144     --ns;
1145   }
1146   if(utf32__canonical_ordering(d.vec, d.nvec))
1147     goto error;
1148   dynstr_ucs4_terminate(&d);
1149   if(ndp)
1150     *ndp = d.nvec;
1151   return d.vec;
1152 error:
1153   xfree(d.vec);
1154   xfree(ss);
1155   return 0;
1156 }
1157
1158 /** @brief Compatibility case-fold @p [s,s+ns)
1159  * @param s Pointer to string
1160  * @param ns Length of string
1161  * @param ndp Where to store length of result
1162  * @return Pointer to result string, or NULL on error
1163  *
1164  * Case-fold the string at @p s according to full default case-folding rules
1165  * (s3.13) for compatibility caseless matching.  The result will be in NFKD.
1166  *
1167  * Returns NULL if the string is not valid for either of the following reasons:
1168  * - it codes for a UTF-16 surrogate
1169  * - it codes for a value outside the unicode code space
1170  */
1171 uint32_t *utf32_casefold_compat(const uint32_t *s, size_t ns, size_t *ndp) {
1172   struct dynstr_ucs4 d;
1173   uint32_t c;
1174   size_t n;
1175   uint32_t *ss = 0;
1176
1177   for(n = 0; n < ns; ++n)
1178     if(utf32__unidata(s[n])->flags & unicode_normalize_before_casefold)
1179       break;
1180   if(n < ns) {
1181     /* We need a preliminary _canonical_ decomposition */
1182     if(!(ss = utf32_decompose_canon(s, ns, &ns)))
1183       return 0;
1184     s = ss;
1185   }
1186   /* This computes NFKD(toCaseFold(s)) */
1187 #define compat_casefold_middle() do {                   \
1188   dynstr_ucs4_init(&d);                                 \
1189   while(ns) {                                           \
1190     c = *s++;                                           \
1191     if((c >= 0xD800 && c <= 0xDFFF) || c > 0x10FFFF)    \
1192       goto error;                                       \
1193     utf32__casefold_one(compat);                        \
1194     --ns;                                               \
1195   }                                                     \
1196   if(utf32__canonical_ordering(d.vec, d.nvec))          \
1197     goto error;                                         \
1198 } while(0)
1199   /* Do the inner (NFKD o toCaseFold) */
1200   compat_casefold_middle();
1201   /* We can do away with the NFD'd copy of the input now */
1202   xfree(ss);
1203   s = ss = d.vec;
1204   ns = d.nvec;
1205   /* Do the outer (NFKD o toCaseFold) */
1206   compat_casefold_middle();
1207   /* That's all */
1208   dynstr_ucs4_terminate(&d);
1209   if(ndp)
1210     *ndp = d.nvec;
1211   return d.vec;
1212 error:
1213   xfree(d.vec);
1214   xfree(ss);
1215   return 0;
1216 }
1217
1218 /** @brief Order a pair of UTF-32 strings
1219  * @param a First 0-terminated string
1220  * @param b Second 0-terminated string
1221  * @return -1, 0 or 1 for a less than, equal to or greater than b
1222  *
1223  * "Comparable to strcmp() at its best."
1224  */
1225 int utf32_cmp(const uint32_t *a, const uint32_t *b) {
1226   while(*a && *b && *a == *b) {
1227     ++a;
1228     ++b;
1229   }
1230   return *a < *b ? -1 : (*a > *b ? 1 : 0);
1231 }
1232
1233 /** @brief Identify a grapheme cluster boundary
1234  * @param s Start of string (must be NFD)
1235  * @param ns Length of string
1236  * @param n Index within string (in [0,ns].)
1237  * @return 1 at a grapheme cluster boundary, 0 otherwise
1238  *
1239  * This function identifies default grapheme cluster boundaries as described in
1240  * UAX #29 s3.  It returns non-0 if @p n points at the code point just after a
1241  * grapheme cluster boundary (including the hypothetical code point just after
1242  * the end of the string).
1243  *
1244  * This function uses utf32_iterator_set() internally; see that function for
1245  * remarks on performance.
1246  */
1247 int utf32_is_grapheme_boundary(const uint32_t *s, size_t ns, size_t n) {
1248   struct utf32_iterator_data it[1];
1249
1250   utf32__iterator_init(it, s, ns, n);
1251   return utf32_iterator_grapheme_boundary(it);
1252 }
1253
1254 /** @brief Identify a word boundary
1255  * @param s Start of string (must be NFD)
1256  * @param ns Length of string
1257  * @param n Index within string (in [0,ns].)
1258  * @return 1 at a word boundary, 0 otherwise
1259  *
1260  * This function identifies default word boundaries as described in UAX #29 s4.
1261  * It returns non-0 if @p n points at the code point just after a word boundary
1262  * (including the hypothetical code point just after the end of the string).
1263  *
1264  * This function uses utf32_iterator_set() internally; see that function for
1265  * remarks on performance.
1266  */
1267 int utf32_is_word_boundary(const uint32_t *s, size_t ns, size_t n) {
1268   struct utf32_iterator_data it[1];
1269
1270   utf32__iterator_init(it, s, ns, n);
1271   return utf32_iterator_word_boundary(it);
1272 }
1273
1274 /*@}*/
1275 /** @defgroup utf8 Functions that operate on UTF-8 strings */
1276 /*@{*/
1277
1278 /** @brief Wrapper to transform a UTF-8 string using the UTF-32 function */
1279 #define utf8__transform(FN) do {                                \
1280   uint32_t *to32 = 0, *decomp32 = 0;                            \
1281   size_t nto32, ndecomp32;                                      \
1282   char *decomp8 = 0;                                            \
1283                                                                 \
1284   if(!(to32 = utf8_to_utf32(s, ns, &nto32))) goto error;        \
1285   if(!(decomp32 = FN(to32, nto32, &ndecomp32))) goto error;     \
1286   decomp8 = utf32_to_utf8(decomp32, ndecomp32, ndp);            \
1287 error:                                                          \
1288   xfree(to32);                                                  \
1289   xfree(decomp32);                                              \
1290   return decomp8;                                               \
1291 } while(0)
1292
1293 /** @brief Canonically decompose @p [s,s+ns)
1294  * @param s Pointer to string
1295  * @param ns Length of string
1296  * @param ndp Where to store length of result
1297  * @return Pointer to result string, or NULL on error
1298  *
1299  * Computes NFD (Normalization Form D) of the string at @p s.  This implies
1300  * performing all canonical decompositions and then normalizing the order of
1301  * combining characters.
1302  *
1303  * Returns NULL if the string is not valid; see utf8_to_utf32() for reasons why
1304  * this might be.
1305  *
1306  * See also:
1307  * - utf32_decompose_canon().
1308  * - utf8_decompose_compat()
1309  * - utf8_compose_canon()
1310  */
1311 char *utf8_decompose_canon(const char *s, size_t ns, size_t *ndp) {
1312   utf8__transform(utf32_decompose_canon);
1313 }
1314
1315 /** @brief Compatibility decompose @p [s,s+ns)
1316  * @param s Pointer to string
1317  * @param ns Length of string
1318  * @param ndp Where to store length of result
1319  * @return Pointer to result string, or NULL on error
1320  *
1321  * Computes NFKD (Normalization Form KD) of the string at @p s.  This implies
1322  * performing all canonical and compatibility decompositions and then
1323  * normalizing the order of combining characters.
1324  *
1325  * Returns NULL if the string is not valid; see utf8_to_utf32() for reasons why
1326  * this might be.
1327  *
1328  * See also:
1329  * - utf32_decompose_compat().
1330  * - utf8_decompose_canon()
1331  * - utf8_compose_compat()
1332  */
1333 char *utf8_decompose_compat(const char *s, size_t ns, size_t *ndp) {
1334   utf8__transform(utf32_decompose_compat);
1335 }
1336
1337 /** @brief Canonically compose @p [s,s+ns)
1338  * @param s Pointer to string
1339  * @param ns Length of string
1340  * @param ndp Where to store length of result
1341  * @return Pointer to result string, or NULL on error
1342  *
1343  * Computes NFC (Normalization Form C) of the string at @p s.  This implies
1344  * performing all canonical decompositions, normalizing the order of combining
1345  * characters and then composing all unblocked primary compositables.
1346  *
1347  * Returns NULL if the string is not valid; see utf8_to_utf32() for reasons why
1348  * this might be.
1349  *
1350  * See also:
1351  * - utf32_compose_canon()
1352  * - utf8_compose_compat()
1353  * - utf8_decompose_canon()
1354  */
1355 char *utf8_compose_canon(const char *s, size_t ns, size_t *ndp) {
1356   utf8__transform(utf32_compose_canon);
1357 }
1358
1359 /** @brief Compatibility compose @p [s,s+ns)
1360  * @param s Pointer to string
1361  * @param ns Length of string
1362  * @param ndp Where to store length of result
1363  * @return Pointer to result string, or NULL on error
1364  *
1365  * Computes NFKC (Normalization Form KC) of the string at @p s.  This implies
1366  * performing all canonical and compatibility decompositions, normalizing the
1367  * order of combining characters and then composing all unblocked primary
1368  * compositables.
1369  *
1370  * Returns NULL if the string is not valid; see utf8_to_utf32() for reasons why
1371  * this might be.
1372  *
1373  * See also:
1374  * - utf32_compose_compat()
1375  * - utf8_compose_canon()
1376  * - utf8_decompose_compat()
1377  */
1378 char *utf8_compose_compat(const char *s, size_t ns, size_t *ndp) {
1379   utf8__transform(utf32_compose_compat);
1380 }
1381
1382 /** @brief Case-fold @p [s,s+ns)
1383  * @param s Pointer to string
1384  * @param ns Length of string
1385  * @param ndp Where to store length of result
1386  * @return Pointer to result string, or NULL on error
1387  *
1388  * Case-fold the string at @p s according to full default case-folding rules
1389  * (s3.13).  The result will be in NFD.
1390  *
1391  * Returns NULL if the string is not valid; see utf8_to_utf32() for reasons why
1392  * this might be.
1393  */
1394 char *utf8_casefold_canon(const char *s, size_t ns, size_t *ndp) {
1395   utf8__transform(utf32_casefold_canon);
1396 }
1397
1398 /** @brief Compatibility case-fold @p [s,s+ns)
1399  * @param s Pointer to string
1400  * @param ns Length of string
1401  * @param ndp Where to store length of result
1402  * @return Pointer to result string, or NULL on error
1403  *
1404  * Case-fold the string at @p s according to full default case-folding rules
1405  * (s3.13).  The result will be in NFKD.
1406  *
1407  * Returns NULL if the string is not valid; see utf8_to_utf32() for reasons why
1408  * this might be.
1409  */
1410 char *utf8_casefold_compat(const char *s, size_t ns, size_t *ndp) {
1411   utf8__transform(utf32_casefold_compat);
1412 }
1413
1414 /*@}*/
1415
1416 /*
1417 Local Variables:
1418 c-basic-offset:2
1419 comment-column:40
1420 fill-column:79
1421 indent-tabs-mode:nil
1422 End:
1423 */