This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade Time::Piece from 1.21 to 1.22
[perl5.git] / cpan / Unicode-Normalize / Normalize.xs
1
2 #define PERL_NO_GET_CONTEXT /* we want efficiency */
3
4 /* private functions which need pTHX_ and aTHX_
5     pv_cat_decompHangul
6     sv_2pvunicode
7     pv_utf8_decompose
8     pv_utf8_reorder
9     pv_utf8_compose
10 */
11
12 #include "EXTERN.h"
13 #include "perl.h"
14 #include "XSUB.h"
15
16 /* These 5 files are prepared by mkheader */
17 #include "unfcmb.h"
18 #include "unfcan.h"
19 #include "unfcpt.h"
20 #include "unfcmp.h"
21 #include "unfexc.h"
22
23 /* Perl 5.6.1 ? */
24 #ifndef uvuni_to_utf8
25 #define uvuni_to_utf8   uv_to_utf8
26 #endif /* uvuni_to_utf8 */
27
28 /* Perl 5.6.1 ? */
29 #ifndef utf8n_to_uvuni
30 #define utf8n_to_uvuni   utf8_to_uv
31 #endif /* utf8n_to_uvuni */
32
33 /* UTF8_ALLOW_BOM is used before Perl 5.8.0 */
34 #ifndef UTF8_ALLOW_BOM
35 #define UTF8_ALLOW_BOM  (0)
36 #endif /* UTF8_ALLOW_BOM */
37
38 #ifndef UTF8_ALLOW_SURROGATE
39 #define UTF8_ALLOW_SURROGATE  (0)
40 #endif /* UTF8_ALLOW_SURROGATE */
41
42 #ifndef UTF8_ALLOW_FE_FF
43 #define UTF8_ALLOW_FE_FF  (0)
44 #endif /* UTF8_ALLOW_FE_FF */
45
46 #ifndef UTF8_ALLOW_FFFF
47 #define UTF8_ALLOW_FFFF  (0)
48 #endif /* UTF8_ALLOW_FFFF */
49
50 #define AllowAnyUTF (UTF8_ALLOW_SURROGATE|UTF8_ALLOW_BOM|UTF8_ALLOW_FE_FF|UTF8_ALLOW_FFFF)
51
52 /* check if the string buffer is enough before uvuni_to_utf8(). */
53 /* dstart, d, and dlen should be defined outside before. */
54 #define Renew_d_if_not_enough_to(need)  STRLEN curlen = d - dstart;     \
55                 if (dlen < curlen + (need)) {   \
56                     dlen += (need);             \
57                     Renew(dstart, dlen+1, U8);  \
58                     d = dstart + curlen;        \
59                 }
60
61 /* if utf8n_to_uvuni() sets retlen to 0 (if broken?) */
62 #define ErrRetlenIsZero "panic (Unicode::Normalize %s): zero-length character"
63
64 /* utf8_hop() hops back before start. Maybe broken UTF-8 */
65 #define ErrHopBeforeStart "panic (Unicode::Normalize): hopping before start"
66
67 /* At present, char > 0x10ffff are unaffected without complaint, right? */
68 #define VALID_UTF_MAX    (0x10ffff)
69 #define OVER_UTF_MAX(uv) (VALID_UTF_MAX < (uv))
70
71 /* size of array for combining characters */
72 /* enough as an initial value? */
73 #define CC_SEQ_SIZE (10)
74 #define CC_SEQ_STEP  (5)
75
76 /* HANGUL begin */
77 #define Hangul_SBase  0xAC00
78 #define Hangul_SFinal 0xD7A3
79 #define Hangul_SCount  11172
80
81 #define Hangul_NCount    588
82
83 #define Hangul_LBase  0x1100
84 #define Hangul_LFinal 0x1112
85 #define Hangul_LCount     19
86
87 #define Hangul_VBase  0x1161
88 #define Hangul_VFinal 0x1175
89 #define Hangul_VCount     21
90
91 #define Hangul_TBase  0x11A7
92 #define Hangul_TFinal 0x11C2
93 #define Hangul_TCount     28
94
95 #define Hangul_IsS(u)  ((Hangul_SBase <= (u)) && ((u) <= Hangul_SFinal))
96 #define Hangul_IsN(u)  (((u) - Hangul_SBase) % Hangul_TCount == 0)
97 #define Hangul_IsLV(u) (Hangul_IsS(u) && Hangul_IsN(u))
98 #define Hangul_IsL(u)  ((Hangul_LBase <= (u)) && ((u) <= Hangul_LFinal))
99 #define Hangul_IsV(u)  ((Hangul_VBase <= (u)) && ((u) <= Hangul_VFinal))
100 #define Hangul_IsT(u)  ((Hangul_TBase  < (u)) && ((u) <= Hangul_TFinal))
101 /* HANGUL end */
102
103 /* this is used for canonical ordering of combining characters (c.c.). */
104 typedef struct {
105     U8 cc;      /* combining class */
106     UV uv;      /* codepoint */
107     STRLEN pos; /* position */
108 } UNF_cc;
109
110 static int compare_cc(const void *a, const void *b)
111 {
112     int ret_cc;
113     ret_cc = ((UNF_cc*) a)->cc - ((UNF_cc*) b)->cc;
114     if (ret_cc)
115         return ret_cc;
116
117     return ( ((UNF_cc*) a)->pos > ((UNF_cc*) b)->pos )
118          - ( ((UNF_cc*) a)->pos < ((UNF_cc*) b)->pos );
119 }
120
121 static U8* dec_canonical(UV uv)
122 {
123     U8 ***plane, **row;
124     if (OVER_UTF_MAX(uv))
125         return NULL;
126     plane = (U8***)UNF_canon[uv >> 16];
127     if (! plane)
128         return NULL;
129     row = plane[(uv >> 8) & 0xff];
130     return row ? row[uv & 0xff] : NULL;
131 }
132
133 static U8* dec_compat(UV uv)
134 {
135     U8 ***plane, **row;
136     if (OVER_UTF_MAX(uv))
137         return NULL;
138     plane = (U8***)UNF_compat[uv >> 16];
139     if (! plane)
140         return NULL;
141     row = plane[(uv >> 8) & 0xff];
142     return row ? row[uv & 0xff] : NULL;
143 }
144
145 static UV composite_uv(UV uv, UV uv2)
146 {
147     UNF_complist ***plane, **row, *cell, *i;
148
149     if (!uv2 || OVER_UTF_MAX(uv) || OVER_UTF_MAX(uv2))
150         return 0;
151
152     if (Hangul_IsL(uv) && Hangul_IsV(uv2)) {
153         UV lindex = uv  - Hangul_LBase;
154         UV vindex = uv2 - Hangul_VBase;
155         return(Hangul_SBase + (lindex * Hangul_VCount + vindex) *
156                Hangul_TCount);
157     }
158     if (Hangul_IsLV(uv) && Hangul_IsT(uv2)) {
159         UV tindex = uv2 - Hangul_TBase;
160         return(uv + tindex);
161     }
162     plane = UNF_compos[uv >> 16];
163     if (! plane)
164         return 0;
165     row = plane[(uv >> 8) & 0xff];
166     if (! row)
167         return 0;
168     cell = row[uv & 0xff];
169     if (! cell)
170         return 0;
171     for (i = cell; i->nextchar; i++) {
172         if (uv2 == i->nextchar)
173             return i->composite;
174     }
175     return 0;
176 }
177
178 static U8 getCombinClass(UV uv)
179 {
180     U8 **plane, *row;
181     if (OVER_UTF_MAX(uv))
182         return 0;
183     plane = (U8**)UNF_combin[uv >> 16];
184     if (! plane)
185         return 0;
186     row = plane[(uv >> 8) & 0xff];
187     return row ? row[uv & 0xff] : 0;
188 }
189
190 static U8* pv_cat_decompHangul(pTHX_ U8* d, UV uv)
191 {
192     UV sindex =  uv - Hangul_SBase;
193     UV lindex =  sindex / Hangul_NCount;
194     UV vindex = (sindex % Hangul_NCount) / Hangul_TCount;
195     UV tindex =  sindex % Hangul_TCount;
196
197     if (! Hangul_IsS(uv))
198         return d;
199
200     d = uvuni_to_utf8(d, (lindex + Hangul_LBase));
201     d = uvuni_to_utf8(d, (vindex + Hangul_VBase));
202     if (tindex)
203         d = uvuni_to_utf8(d, (tindex + Hangul_TBase));
204     return d;
205 }
206
207 static char* sv_2pvunicode(pTHX_ SV *sv, STRLEN *lp)
208 {
209     char *s;
210     STRLEN len;
211     s = SvPV(sv,len);
212     if (!SvUTF8(sv)) {
213         SV* tmpsv = sv_2mortal(newSVpvn(s, len));
214         if (!SvPOK(tmpsv))
215             s = SvPV_force(tmpsv,len);
216         sv_utf8_upgrade(tmpsv);
217         s = SvPV(tmpsv,len);
218     }
219     if (lp)
220         *lp = len;
221     return s;
222 }
223
224 static
225 U8* pv_utf8_decompose(pTHX_ U8* s, STRLEN slen, U8** dp, STRLEN dlen, bool iscompat)
226 {
227     U8* p = s;
228     U8* e = s + slen;
229     U8* dstart = *dp;
230     U8* d = dstart;
231
232     while (p < e) {
233         STRLEN retlen;
234         UV uv = utf8n_to_uvuni(p, e - p, &retlen, AllowAnyUTF);
235         if (!retlen)
236             croak(ErrRetlenIsZero, "decompose");
237         p += retlen;
238
239         if (Hangul_IsS(uv)) {
240             Renew_d_if_not_enough_to(UTF8_MAXLEN * 3)
241             d = pv_cat_decompHangul(aTHX_ d, uv);
242         }
243         else {
244             U8* r = iscompat ? dec_compat(uv) : dec_canonical(uv);
245
246             if (r) {
247                 STRLEN len = (STRLEN)strlen((char *)r);
248                 Renew_d_if_not_enough_to(len)
249                 while (len--)
250                     *d++ = *r++;
251             }
252             else {
253                 Renew_d_if_not_enough_to(UTF8_MAXLEN)
254                 d = uvuni_to_utf8(d, uv);
255             }
256         }
257     }
258     *dp = dstart;
259     return d;
260 }
261
262 static
263 U8* pv_utf8_reorder(pTHX_ U8* s, STRLEN slen, U8** dp, STRLEN dlen)
264 {
265     U8* p = s;
266     U8* e = s + slen;
267     U8* dstart = *dp;
268     U8* d = dstart;
269
270     UNF_cc  seq_ary[CC_SEQ_SIZE];
271     UNF_cc* seq_ptr = seq_ary; /* use array at the beginning */
272     UNF_cc* seq_ext = NULL; /* extend if need */
273     STRLEN seq_max = CC_SEQ_SIZE;
274     STRLEN cc_pos = 0;
275
276     while (p < e) {
277         U8 curCC;
278         STRLEN retlen;
279         UV uv = utf8n_to_uvuni(p, e - p, &retlen, AllowAnyUTF);
280         if (!retlen)
281             croak(ErrRetlenIsZero, "reorder");
282         p += retlen;
283
284         curCC = getCombinClass(uv);
285
286         if (curCC != 0) {
287             if (seq_max < cc_pos + 1) { /* extend if need */
288                 seq_max = cc_pos + CC_SEQ_STEP; /* new size */
289                 if (CC_SEQ_SIZE == cc_pos) { /* seq_ary full */
290                     STRLEN i;
291                     New(0, seq_ext, seq_max, UNF_cc);
292                     for (i = 0; i < cc_pos; i++)
293                         seq_ext[i] = seq_ary[i];
294                 }
295                 else {
296                     Renew(seq_ext, seq_max, UNF_cc);
297                 }
298                 seq_ptr = seq_ext; /* use seq_ext from now */
299             }
300
301             seq_ptr[cc_pos].cc  = curCC;
302             seq_ptr[cc_pos].uv  = uv;
303             seq_ptr[cc_pos].pos = cc_pos;
304             ++cc_pos;
305
306             if (p < e)
307                 continue;
308         }
309
310         /* output */
311         if (cc_pos) {
312             STRLEN i;
313
314             if (cc_pos > 1) /* reordered if there are two c.c.'s */
315                 qsort((void*)seq_ptr, cc_pos, sizeof(UNF_cc), compare_cc);
316
317             for (i = 0; i < cc_pos; i++) {
318                 Renew_d_if_not_enough_to(UTF8_MAXLEN)
319                 d = uvuni_to_utf8(d, seq_ptr[i].uv);
320             }
321             cc_pos = 0;
322         }
323
324         if (curCC == 0) {
325             Renew_d_if_not_enough_to(UTF8_MAXLEN)
326             d = uvuni_to_utf8(d, uv);
327         }
328     }
329     if (seq_ext)
330         Safefree(seq_ext);
331     *dp = dstart;
332     return d;
333 }
334
335 static
336 U8* pv_utf8_compose(pTHX_ U8* s, STRLEN slen, U8** dp, STRLEN dlen, bool iscontig)
337 {
338     U8* p = s;
339     U8* e = s + slen;
340     U8* dstart = *dp;
341     U8* d = dstart;
342
343     UV uvS = 0; /* code point of the starter */
344     bool valid_uvS = FALSE; /* if FALSE, uvS isn't initialized yet */
345     U8 preCC = 0;
346
347     UV  seq_ary[CC_SEQ_SIZE];
348     UV* seq_ptr = seq_ary; /* use array at the beginning */
349     UV* seq_ext = NULL; /* extend if need */
350     STRLEN seq_max = CC_SEQ_SIZE;
351     STRLEN cc_pos = 0;
352
353     while (p < e) {
354         U8 curCC;
355         STRLEN retlen;
356         UV uv = utf8n_to_uvuni(p, e - p, &retlen, AllowAnyUTF);
357         if (!retlen)
358             croak(ErrRetlenIsZero, "compose");
359         p += retlen;
360
361         curCC = getCombinClass(uv);
362
363         if (!valid_uvS) {
364             if (curCC == 0) {
365                 uvS = uv; /* the first Starter is found */
366                 valid_uvS = TRUE;
367                 if (p < e)
368                     continue;
369             }
370             else {
371                 Renew_d_if_not_enough_to(UTF8_MAXLEN)
372                 d = uvuni_to_utf8(d, uv);
373                 continue;
374             }
375         }
376         else {
377             bool composed;
378
379             /* blocked */
380             if ((iscontig && cc_pos) || /* discontiguous combination */
381                  (curCC != 0 && preCC == curCC) || /* blocked by same CC */
382                  (preCC > curCC)) /* blocked by higher CC: revised D2 */
383                 composed = FALSE;
384
385             /* not blocked:
386                  iscontig && cc_pos == 0      -- contiguous combination
387                  curCC == 0 && preCC == 0     -- starter + starter
388                  curCC != 0 && preCC < curCC  -- lower CC */
389             else {
390                 /* try composition */
391                 UV uvComp = composite_uv(uvS, uv);
392
393                 if (uvComp && !isExclusion(uvComp))  {
394                     uvS = uvComp;
395                     composed = TRUE;
396
397                     /* preCC should not be changed to curCC */
398                     /* e.g. 1E14 = 0045 0304 0300 where CC(0304) == CC(0300) */
399                     if (p < e)
400                         continue;
401                 }
402                 else
403                     composed = FALSE;
404             }
405
406             if (!composed) {
407                 preCC = curCC;
408                 if (curCC != 0 || !(p < e)) {
409                     if (seq_max < cc_pos + 1) { /* extend if need */
410                         seq_max = cc_pos + CC_SEQ_STEP; /* new size */
411                         if (CC_SEQ_SIZE == cc_pos) { /* seq_ary full */
412                             New(0, seq_ext, seq_max, UV);
413                             Copy(seq_ary, seq_ext, cc_pos, UV);
414                         }
415                         else {
416                             Renew(seq_ext, seq_max, UV);
417                         }
418                         seq_ptr = seq_ext; /* use seq_ext from now */
419                     }
420                     seq_ptr[cc_pos] = uv;
421                     ++cc_pos;
422                 }
423                 if (curCC != 0 && p < e)
424                     continue;
425             }
426         }
427
428         /* output */
429         {
430             Renew_d_if_not_enough_to(UTF8_MAXLEN)
431             d = uvuni_to_utf8(d, uvS); /* starter (composed or not) */
432         }
433
434         if (cc_pos) {
435             STRLEN i;
436
437             for (i = 0; i < cc_pos; i++) {
438                 Renew_d_if_not_enough_to(UTF8_MAXLEN)
439                 d = uvuni_to_utf8(d, seq_ptr[i]);
440             }
441             cc_pos = 0;
442         }
443
444         uvS = uv;
445     }
446     if (seq_ext)
447         Safefree(seq_ext);
448     *dp = dstart;
449     return d;
450 }
451
452 MODULE = Unicode::Normalize     PACKAGE = Unicode::Normalize
453
454 SV*
455 decompose(src, compat = &PL_sv_no)
456     SV * src
457     SV * compat
458   PROTOTYPE: $;$
459   PREINIT:
460     SV* dst;
461     U8 *s, *d, *dend;
462     STRLEN slen, dlen;
463   CODE:
464     s = (U8*)sv_2pvunicode(aTHX_ src,&slen);
465     dst = newSVpvn("", 0);
466     dlen = slen;
467     New(0, d, dlen+1, U8);
468     dend = pv_utf8_decompose(aTHX_ s, slen, &d, dlen, (bool)SvTRUE(compat));
469     sv_setpvn(dst, (char *)d, dend - d);
470     SvUTF8_on(dst);
471     Safefree(d);
472     RETVAL = dst;
473   OUTPUT:
474     RETVAL
475
476
477 SV*
478 reorder(src)
479     SV * src
480   PROTOTYPE: $
481   PREINIT:
482     SV* dst;
483     U8 *s, *d, *dend;
484     STRLEN slen, dlen;
485   CODE:
486     s = (U8*)sv_2pvunicode(aTHX_ src,&slen);
487     dst = newSVpvn("", 0);
488     dlen = slen;
489     New(0, d, dlen+1, U8);
490     dend = pv_utf8_reorder(aTHX_ s, slen, &d, dlen);
491     sv_setpvn(dst, (char *)d, dend - d);
492     SvUTF8_on(dst);
493     Safefree(d);
494     RETVAL = dst;
495   OUTPUT:
496     RETVAL
497
498
499 SV*
500 compose(src)
501     SV * src
502   PROTOTYPE: $
503   ALIAS:
504     composeContiguous = 1
505   PREINIT:
506     SV* dst;
507     U8 *s, *d, *dend;
508     STRLEN slen, dlen;
509   CODE:
510     s = (U8*)sv_2pvunicode(aTHX_ src,&slen);
511     dst = newSVpvn("", 0);
512     dlen = slen;
513     New(0, d, dlen+1, U8);
514     dend = pv_utf8_compose(aTHX_ s, slen, &d, dlen, (bool)ix);
515     sv_setpvn(dst, (char *)d, dend - d);
516     SvUTF8_on(dst);
517     Safefree(d);
518     RETVAL = dst;
519   OUTPUT:
520     RETVAL
521
522
523 SV*
524 NFD(src)
525     SV * src
526   PROTOTYPE: $
527   ALIAS:
528     NFKD = 1
529   PREINIT:
530     SV *dst;
531     U8 *s, *t, *tend, *d, *dend;
532     STRLEN slen, tlen, dlen;
533   CODE:
534     s = (U8*)sv_2pvunicode(aTHX_ src,&slen);
535
536     /* decompose */
537     tlen = slen;
538     New(0, t, tlen+1, U8);
539     tend = pv_utf8_decompose(aTHX_ s, slen, &t, tlen, (bool)(ix==1));
540     *tend = '\0';
541     tlen = tend - t; /* no longer know real size of t */
542
543     /* reorder */
544     dlen = tlen;
545     New(0, d, dlen+1, U8);
546     dend = pv_utf8_reorder(aTHX_ t, tlen, &d, dlen);
547     *dend = '\0';
548     dlen = dend - d; /* no longer know real size of d */
549
550     /* return */
551     dst = newSVpvn("", 0);
552     sv_setpvn(dst, (char *)d, dlen);
553     SvUTF8_on(dst);
554
555     Safefree(t);
556     Safefree(d);
557     RETVAL = dst;
558   OUTPUT:
559     RETVAL
560
561
562 SV*
563 NFC(src)
564     SV * src
565   PROTOTYPE: $
566   ALIAS:
567     NFKC = 1
568     FCC  = 2
569   PREINIT:
570     SV *dst;
571     U8 *s, *t, *tend, *u, *uend, *d, *dend;
572     STRLEN slen, tlen, ulen, dlen;
573   CODE:
574     s = (U8*)sv_2pvunicode(aTHX_ src,&slen);
575
576     /* decompose */
577     tlen = slen;
578     New(0, t, tlen+1, U8);
579     tend = pv_utf8_decompose(aTHX_ s, slen, &t, tlen, (bool)(ix==1));
580     *tend = '\0';
581     tlen = tend - t; /* no longer know real size of t */
582
583     /* reorder */
584     ulen = tlen;
585     New(0, u, ulen+1, U8);
586     uend = pv_utf8_reorder(aTHX_ t, tlen, &u, ulen);
587     *uend = '\0';
588     ulen = uend - u; /* no longer know real size of u */
589
590     /* compose */
591     dlen = ulen;
592     New(0, d, dlen+1, U8);
593     dend = pv_utf8_compose(aTHX_ u, ulen, &d, dlen, (bool)(ix==2));
594     *dend = '\0';
595     dlen = dend - d; /* no longer know real size of d */
596
597     /* return */
598     dst = newSVpvn("", 0);
599     sv_setpvn(dst, (char *)d, dlen);
600     SvUTF8_on(dst);
601
602     Safefree(t);
603     Safefree(u);
604     Safefree(d);
605     RETVAL = dst;
606   OUTPUT:
607     RETVAL
608
609
610 SV*
611 checkNFD(src)
612     SV * src
613   PROTOTYPE: $
614   ALIAS:
615     checkNFKD = 1
616   PREINIT:
617     STRLEN srclen, retlen;
618     U8 *s, *e, *p, curCC, preCC;
619     bool result = TRUE;
620   CODE:
621     s = (U8*)sv_2pvunicode(aTHX_ src,&srclen);
622     e = s + srclen;
623
624     preCC = 0;
625     for (p = s; p < e; p += retlen) {
626         UV uv = utf8n_to_uvuni(p, e - p, &retlen, AllowAnyUTF);
627         if (!retlen)
628             croak(ErrRetlenIsZero, "checkNFD or -NFKD");
629
630         curCC = getCombinClass(uv);
631         if (preCC > curCC && curCC != 0) { /* canonical ordering violated */
632             result = FALSE;
633             break;
634         }
635         if (Hangul_IsS(uv) || (ix ? dec_compat(uv) : dec_canonical(uv))) {
636             result = FALSE;
637             break;
638         }
639         preCC = curCC;
640     }
641     RETVAL = boolSV(result);
642   OUTPUT:
643     RETVAL
644
645
646 SV*
647 checkNFC(src)
648     SV * src
649   PROTOTYPE: $
650   ALIAS:
651     checkNFKC = 1
652   PREINIT:
653     STRLEN srclen, retlen;
654     U8 *s, *e, *p, curCC, preCC;
655     bool result = TRUE;
656     bool isMAYBE = FALSE;
657   CODE:
658     s = (U8*)sv_2pvunicode(aTHX_ src,&srclen);
659     e = s + srclen;
660
661     preCC = 0;
662     for (p = s; p < e; p += retlen) {
663         UV uv = utf8n_to_uvuni(p, e - p, &retlen, AllowAnyUTF);
664         if (!retlen)
665             croak(ErrRetlenIsZero, "checkNFC or -NFKC");
666
667         curCC = getCombinClass(uv);
668         if (preCC > curCC && curCC != 0) { /* canonical ordering violated */
669             result = FALSE;
670             break;
671         }
672
673         /* get NFC/NFKC property */
674         if (Hangul_IsS(uv)) /* Hangul syllables are canonical composites */
675             ; /* YES */
676         else if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv)) {
677             result = FALSE;
678             break;
679         }
680         else if (isComp2nd(uv))
681             isMAYBE = TRUE;
682         else if (ix) {
683             char *canon, *compat;
684           /* NFKC_NO when having compatibility mapping. */
685             canon  = (char *) dec_canonical(uv);
686             compat = (char *) dec_compat(uv);
687             if (compat && !(canon && strEQ(canon, compat))) {
688                 result = FALSE;
689                 break;
690             }
691         } /* end of get NFC/NFKC property */
692
693         preCC = curCC;
694     }
695     if (isMAYBE && result) /* NO precedes MAYBE */
696         XSRETURN_UNDEF;
697     RETVAL = boolSV(result);
698   OUTPUT:
699     RETVAL
700
701
702 SV*
703 checkFCD(src)
704     SV * src
705   PROTOTYPE: $
706   ALIAS:
707     checkFCC = 1
708   PREINIT:
709     STRLEN srclen, retlen;
710     U8 *s, *e, *p, curCC, preCC;
711     bool result = TRUE;
712     bool isMAYBE = FALSE;
713   CODE:
714     s = (U8*)sv_2pvunicode(aTHX_ src,&srclen);
715     e = s + srclen;
716     preCC = 0;
717     for (p = s; p < e; p += retlen) {
718         U8 *sCan;
719         UV uvLead;
720         STRLEN canlen = 0;
721         UV uv = utf8n_to_uvuni(p, e - p, &retlen, AllowAnyUTF);
722         if (!retlen)
723             croak(ErrRetlenIsZero, "checkFCD or -FCC");
724
725         sCan = (U8*) dec_canonical(uv);
726
727         if (sCan) {
728             STRLEN canret;
729             canlen = (STRLEN)strlen((char *) sCan);
730             uvLead = utf8n_to_uvuni(sCan, canlen, &canret, AllowAnyUTF);
731             if (!canret)
732                 croak(ErrRetlenIsZero, "checkFCD or -FCC");
733         }
734         else {
735             uvLead = uv;
736         }
737
738         curCC = getCombinClass(uvLead);
739
740         if (curCC != 0 && curCC < preCC) { /* canonical ordering violated */
741             result = FALSE;
742             break;
743         }
744
745         if (ix) {
746             if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv)) {
747                 result = FALSE;
748                 break;
749             }
750             else if (isComp2nd(uv))
751                 isMAYBE = TRUE;
752         }
753
754         if (sCan) {
755             STRLEN canret;
756             UV uvTrail;
757             U8* eCan = sCan + canlen;
758             U8* pCan = utf8_hop(eCan, -1);
759             if (pCan < sCan)
760                 croak(ErrHopBeforeStart);
761             uvTrail = utf8n_to_uvuni(pCan, eCan - pCan, &canret, AllowAnyUTF);
762             if (!canret)
763                 croak(ErrRetlenIsZero, "checkFCD or -FCC");
764             preCC = getCombinClass(uvTrail);
765         }
766         else {
767             preCC = curCC;
768         }
769     }
770     if (isMAYBE && result) /* NO precedes MAYBE */
771         XSRETURN_UNDEF;
772     RETVAL = boolSV(result);
773   OUTPUT:
774     RETVAL
775
776
777 U8
778 getCombinClass(uv)
779     UV uv
780   PROTOTYPE: $
781
782 bool
783 isExclusion(uv)
784     UV uv
785   PROTOTYPE: $
786
787 bool
788 isSingleton(uv)
789     UV uv
790   PROTOTYPE: $
791
792 bool
793 isNonStDecomp(uv)
794     UV uv
795   PROTOTYPE: $
796
797 bool
798 isComp2nd(uv)
799     UV uv
800   PROTOTYPE: $
801   ALIAS:
802     isNFC_MAYBE  = 1
803     isNFKC_MAYBE = 2
804
805
806
807 SV*
808 isNFD_NO(uv)
809     UV uv
810   PROTOTYPE: $
811   ALIAS:
812     isNFKD_NO = 1
813   PREINIT:
814     bool result = FALSE;
815   CODE:
816     if (Hangul_IsS(uv) || (ix ? dec_compat(uv) : dec_canonical(uv)))
817         result = TRUE; /* NFD_NO or NFKD_NO */
818     RETVAL = boolSV(result);
819   OUTPUT:
820     RETVAL
821
822
823 SV*
824 isComp_Ex(uv)
825     UV uv
826   PROTOTYPE: $
827   ALIAS:
828     isNFC_NO  = 0
829     isNFKC_NO = 1
830   PREINIT:
831     bool result = FALSE;
832   CODE:
833     if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv))
834         result = TRUE; /* NFC_NO or NFKC_NO */
835     else if (ix) {
836         char *canon, *compat;
837         canon  = (char *) dec_canonical(uv);
838         compat = (char *) dec_compat(uv);
839         if (compat && (!canon || strNE(canon, compat)))
840             result = TRUE; /* NFC_NO or NFKC_NO */
841     }
842     RETVAL = boolSV(result);
843   OUTPUT:
844     RETVAL
845
846 SV*
847 getComposite(uv, uv2)
848     UV uv
849     UV uv2
850   PROTOTYPE: $$
851   PREINIT:
852     UV composite;
853   CODE:
854     composite = composite_uv(uv, uv2);
855     RETVAL = composite ? newSVuv(composite) : &PL_sv_undef;
856   OUTPUT:
857     RETVAL
858
859
860
861 SV*
862 getCanon(uv)
863     UV uv
864   PROTOTYPE: $
865   ALIAS:
866     getCompat = 1
867   CODE:
868     if (Hangul_IsS(uv)) {
869         U8 tmp[3 * UTF8_MAXLEN + 1];
870         U8 *t = tmp;
871         U8 *e = pv_cat_decompHangul(aTHX_ t, uv);
872         RETVAL = newSVpvn((char *)t, e - t);
873     } else {
874         U8* rstr = ix ? dec_compat(uv) : dec_canonical(uv);
875         if (!rstr)
876             XSRETURN_UNDEF;
877         RETVAL = newSVpvn((char *)rstr, strlen((char *)rstr));
878     }
879     SvUTF8_on(RETVAL);
880   OUTPUT:
881     RETVAL
882
883
884 void
885 splitOnLastStarter(src)
886     SV * src
887   PREINIT:
888     SV *svp;
889     STRLEN srclen;
890     U8 *s, *e, *p;
891   PPCODE:
892     s = (U8*)sv_2pvunicode(aTHX_ src,&srclen);
893     e = s + srclen;
894     p = e;
895     while (s < p) {
896         UV uv;
897         p = utf8_hop(p, -1);
898         if (p < s)
899             croak(ErrHopBeforeStart);
900         uv = utf8n_to_uvuni(p, e - p, NULL, AllowAnyUTF);
901         if (getCombinClass(uv) == 0) /* Last Starter found */
902             break;
903     }
904
905     svp = sv_2mortal(newSVpvn((char*)s, p - s));
906     SvUTF8_on(svp);
907     XPUSHs(svp);
908
909     svp = sv_2mortal(newSVpvn((char*)p, e - p));
910     SvUTF8_on(svp);
911     XPUSHs(svp);
912