This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Sync with Unicode::Normalize 0.25
[perl5.git] / ext / Unicode / Normalize / Normalize.xs
1
2 #include "EXTERN.h"
3 #include "perl.h"
4 #include "XSUB.h"
5
6 /* These 5 files are prepared by mkheader */
7 #include "unfcmb.h"
8 #include "unfcan.h"
9 #include "unfcpt.h"
10 #include "unfcmp.h"
11 #include "unfexc.h"
12
13 /* Perl 5.6.1 ? */
14 #ifndef uvuni_to_utf8
15 #define uvuni_to_utf8   uv_to_utf8
16 #endif /* uvuni_to_utf8 */
17
18 /* Perl 5.6.1 ? */
19 #ifndef utf8n_to_uvuni
20 #define utf8n_to_uvuni  utf8_to_uv
21 #endif /* utf8n_to_uvuni */
22
23 /* if utf8n_to_uvuni() sets retlen to 0 when flags = 0 */
24 #define ErrRetlenIsZero "panic (Unicode::Normalize): zero-length character"
25
26 /* utf8_hop() hops back before start. Maybe broken UTF-8 */
27 #define ErrHopBeforeStart "panic (Unicode::Normalize): hopping before start"
28
29 /* At present, char > 0x10ffff are unaffected without complaint, right? */
30 #define VALID_UTF_MAX    (0x10ffff)
31 #define OVER_UTF_MAX(uv) (VALID_UTF_MAX < (uv))
32
33 /* HANGUL_H */
34 #define Hangul_SBase  0xAC00
35 #define Hangul_SFinal 0xD7A3
36 #define Hangul_SCount  11172
37
38 #define Hangul_NCount    588
39
40 #define Hangul_LBase  0x1100
41 #define Hangul_LFinal 0x1112
42 #define Hangul_LCount     19
43
44 #define Hangul_VBase  0x1161
45 #define Hangul_VFinal 0x1175
46 #define Hangul_VCount     21
47
48 #define Hangul_TBase  0x11A7
49 #define Hangul_TFinal 0x11C2
50 #define Hangul_TCount     28
51
52 #define Hangul_IsS(u)  ((Hangul_SBase <= (u)) && ((u) <= Hangul_SFinal))
53 #define Hangul_IsN(u)  (((u) - Hangul_SBase) % Hangul_TCount == 0)
54 #define Hangul_IsLV(u) (Hangul_IsS(u) && Hangul_IsN(u))
55 #define Hangul_IsL(u)  ((Hangul_LBase <= (u)) && ((u) <= Hangul_LFinal))
56 #define Hangul_IsV(u)  ((Hangul_VBase <= (u)) && ((u) <= Hangul_VFinal))
57 #define Hangul_IsT(u)  ((Hangul_TBase  < (u)) && ((u) <= Hangul_TFinal))
58 /* HANGUL_H */
59
60 /* this is used for canonical ordering of combining characters (c.c.). */
61 typedef struct {
62     U8 cc;      /* combining class */
63     UV uv;      /* codepoint */
64     STRLEN pos; /* position */
65 } UNF_cc;
66
67 int compare_cc (const void *a, const void *b)
68 {
69     int ret_cc;
70     ret_cc = ((UNF_cc*) a)->cc - ((UNF_cc*) b)->cc;
71     if (ret_cc)
72         return ret_cc;
73
74     return ( ((UNF_cc*) a)->pos > ((UNF_cc*) b)->pos )
75          - ( ((UNF_cc*) a)->pos < ((UNF_cc*) b)->pos );
76 }
77
78 U8* dec_canonical (UV uv)
79 {
80     U8 ***plane, **row;
81     if (OVER_UTF_MAX(uv))
82         return NULL;
83     plane = (U8***)UNF_canon[uv >> 16];
84     if (! plane)
85         return NULL;
86     row = plane[(uv >> 8) & 0xff];
87     return row ? row[uv & 0xff] : NULL;
88 }
89
90 U8* dec_compat (UV uv)
91 {
92     U8 ***plane, **row;
93     if (OVER_UTF_MAX(uv))
94         return NULL;
95     plane = (U8***)UNF_compat[uv >> 16];
96     if (! plane)
97         return NULL;
98     row = plane[(uv >> 8) & 0xff];
99     return row ? row[uv & 0xff] : NULL;
100 }
101
102 UV composite_uv (UV uv, UV uv2)
103 {
104     UNF_complist ***plane, **row, *cell, *i;
105
106     if (! uv2 || OVER_UTF_MAX(uv) || OVER_UTF_MAX(uv2))
107         return 0;
108
109     if (Hangul_IsL(uv) && Hangul_IsV(uv2)) {
110         uv  -= Hangul_LBase; /* lindex */
111         uv2 -= Hangul_VBase; /* vindex */
112         return(Hangul_SBase + (uv * Hangul_VCount + uv2) * Hangul_TCount);
113     }
114     if (Hangul_IsLV(uv) && Hangul_IsT(uv2)) {
115         uv2 -= Hangul_TBase; /* tindex */
116         return(uv + uv2);
117     }
118     plane = UNF_compos[uv >> 16];
119     if (! plane)
120         return 0;
121     row = plane[(uv >> 8) & 0xff];
122     if (! row)
123         return 0;
124     cell = row[uv & 0xff];
125     if (! cell)
126         return 0;
127     for (i = cell; i->nextchar; i++) {
128         if (uv2 == i->nextchar)
129             return i->composite;
130     }
131     return 0;
132 }
133
134 U8 getCombinClass (UV uv)
135 {
136     U8 **plane, *row;
137     if (OVER_UTF_MAX(uv))
138         return 0;
139     plane = (U8**)UNF_combin[uv >> 16];
140     if (! plane)
141         return 0;
142     row = plane[(uv >> 8) & 0xff];
143     return row ? row[uv & 0xff] : 0;
144 }
145
146 void sv_cat_decompHangul (SV* sv, UV uv)
147 {
148     UV sindex, lindex, vindex, tindex;
149     U8 *t, tmp[3 * UTF8_MAXLEN + 1];
150
151     if (! Hangul_IsS(uv))
152         return;
153
154     sindex =  uv - Hangul_SBase;
155     lindex =  sindex / Hangul_NCount;
156     vindex = (sindex % Hangul_NCount) / Hangul_TCount;
157     tindex =  sindex % Hangul_TCount;
158
159     t = tmp;
160     t = uvuni_to_utf8(t, (lindex + Hangul_LBase));
161     t = uvuni_to_utf8(t, (vindex + Hangul_VBase));
162     if (tindex)
163         t = uvuni_to_utf8(t, (tindex + Hangul_TBase));
164     *t = '\0';
165     sv_catpvn(sv, (char *)tmp, strlen((char *)tmp));
166 }
167
168 MODULE = Unicode::Normalize     PACKAGE = Unicode::Normalize
169
170 SV*
171 decompose(arg, compat = &PL_sv_no)
172     SV * arg
173     SV * compat
174   PROTOTYPE: $;$
175   PREINIT:
176     UV uv;
177     SV *src, *dst;
178     STRLEN srclen, retlen;
179     U8 *s, *e, *p, *r;
180     bool iscompat;
181   CODE:
182     if (SvUTF8(arg)) {
183         src = arg;
184     } else {
185         src = sv_mortalcopy(arg);
186         sv_utf8_upgrade(src);
187     }
188     iscompat = SvTRUE(compat);
189
190     dst = newSV(1);
191     (void)SvPOK_only(dst);
192     SvUTF8_on(dst);
193
194     s = (U8*)SvPV(src,srclen);
195     e = s + srclen;
196     for (p = s; p < e; p += retlen) {
197         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
198         if (!retlen)
199             croak(ErrRetlenIsZero);
200
201         if (Hangul_IsS(uv))
202             sv_cat_decompHangul(dst, uv);
203         else {
204             r = iscompat ? dec_compat(uv) : dec_canonical(uv);
205             if (r)
206                 sv_catpv(dst, (char *)r);
207             else
208                 sv_catpvn(dst, (char *)p, retlen);
209         }
210     }
211     RETVAL = dst;
212   OUTPUT:
213     RETVAL
214
215
216
217 SV*
218 reorder(arg)
219     SV * arg
220   PROTOTYPE: $
221   PREINIT:
222     SV *src, *dst;
223     STRLEN srclen, dstlen, retlen, stk_cc_max;
224     U8 *s, *e, *p, *d, curCC;
225     UV uv;
226     UNF_cc * stk_cc;
227   CODE:
228     if (SvUTF8(arg)) {
229         src = arg;
230     } else {
231         src = sv_mortalcopy(arg);
232         sv_utf8_upgrade(src);
233     }
234
235     s = (U8*)SvPV(src, srclen);
236
237     dstlen = srclen + 1;
238     dst = newSV(dstlen);
239     sv_setpvn(dst,(const char*)s,srclen);
240     SvUTF8_on(dst);
241
242     stk_cc_max = 10; /* enough as an initial value? */
243     New(0, stk_cc, stk_cc_max, UNF_cc);
244
245     d = (U8*)SvPV(dst,dstlen);
246     e = d + dstlen;
247
248     for (p = d; p < e;) {
249         U8 *cc_in;
250         STRLEN cc_len, cc_iter, cc_pos;
251
252         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
253         if (!retlen)
254             croak(ErrRetlenIsZero);
255         p += retlen;
256
257         
258
259         curCC = getCombinClass(uv);
260         if (! (curCC && p < e))
261             continue;
262         else
263             cc_in = p - retlen;
264
265         cc_pos = 0;
266         stk_cc[cc_pos].cc  = curCC;
267         stk_cc[cc_pos].uv  = uv;
268         stk_cc[cc_pos].pos = cc_pos;
269
270         while (p < e) {
271             uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
272             if (!retlen)
273                 croak(ErrRetlenIsZero);
274             p += retlen;
275
276             curCC = getCombinClass(uv);
277             if (!curCC)
278                 break;
279
280             cc_pos++;
281             if (stk_cc_max <= cc_pos) { /* extend if need */
282                 stk_cc_max = cc_pos + 1;
283                 Renew(stk_cc, stk_cc_max, UNF_cc);
284             }
285             stk_cc[cc_pos].cc  = curCC;
286             stk_cc[cc_pos].uv  = uv;
287             stk_cc[cc_pos].pos = cc_pos;
288         }
289
290          /* only one c.c. in cc_len from cc_in, no need of reordering */
291         if (!cc_pos)
292             continue;
293
294         qsort((void*)stk_cc, cc_pos + 1, sizeof(UNF_cc), compare_cc);
295
296         cc_len = p - cc_in;
297         p = cc_in;
298         for (cc_iter = 0; cc_iter <= cc_pos; cc_iter++) {
299             p = uvuni_to_utf8(p, stk_cc[cc_iter].uv);
300         }
301     }
302     Safefree(stk_cc);
303     RETVAL = dst;
304   OUTPUT:
305     RETVAL
306
307
308
309 SV*
310 compose(arg)
311     SV * arg
312   PROTOTYPE: $
313   ALIAS:
314     composeContiguous = 1
315   PREINIT:
316     SV  *src, *dst, *tmp;
317     U8  *s, *p, *e, *d, *t, *tmp_start, curCC, preCC;
318     UV uv, uvS, uvComp;
319     STRLEN srclen, dstlen, tmplen, retlen;
320     bool beginning = TRUE;
321   CODE:
322     if (SvUTF8(arg)) {
323         src = arg;
324     } else {
325         src = sv_mortalcopy(arg);
326         sv_utf8_upgrade(src);
327     }
328
329     s = (U8*)SvPV(src, srclen);
330     e = s + srclen;
331     dstlen = srclen + 1;
332     dst = newSV(dstlen);
333     (void)SvPOK_only(dst);
334     SvUTF8_on(dst);
335     d = (U8*)SvPVX(dst);
336
337   /* for uncomposed combining char */
338     tmp = sv_2mortal(newSV(dstlen));
339     (void)SvPOK_only(tmp);
340     SvUTF8_on(tmp);
341
342     for (p = s; p < e;) {
343         if (beginning) {
344             uvS = utf8n_to_uvuni(p, e - p, &retlen, 0);
345             if (!retlen)
346                 croak(ErrRetlenIsZero);
347             p += retlen;
348
349             if (getCombinClass(uvS)) { /* no Starter found yet */
350                 d = uvuni_to_utf8(d, uvS);
351                 continue;
352             }
353             beginning = FALSE;
354         }
355
356     /* Starter */
357         t = tmp_start = (U8*)SvPVX(tmp);
358         preCC = 0;
359
360     /* to the next Starter */
361         while (p < e) {
362             uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
363             if (!retlen)
364                 croak(ErrRetlenIsZero);
365             p += retlen;
366
367             curCC = getCombinClass(uv);
368
369             if (preCC && preCC == curCC) {
370                 preCC = curCC;
371                 t = uvuni_to_utf8(t, uv);
372             } else {
373                 uvComp = composite_uv(uvS, uv);
374
375                 if (uvComp && ! isExclusion(uvComp) &&
376                         (ix ? (t == tmp_start) : (preCC <= curCC))) {
377                     STRLEN leftcur, rightcur, dstcur;
378                     leftcur  = UNISKIP(uvComp);
379                     rightcur = UNISKIP(uvS) + UNISKIP(uv);
380
381                     if (leftcur > rightcur) {
382                         dstcur = d - (U8*)SvPVX(dst);
383                         dstlen += leftcur - rightcur;
384                         d = (U8*)SvGROW(dst,dstlen) + dstcur;
385                     }
386                     /* preCC not changed to curCC */
387                     uvS = uvComp;
388                 } else if (! curCC && p < e) { /* blocked */
389                     break;
390                 } else {
391                     preCC = curCC;
392                     t = uvuni_to_utf8(t, uv);
393                 }
394             }
395         }
396         d = uvuni_to_utf8(d, uvS); /* starter (composed or not) */
397         tmplen = t - tmp_start;
398         if (tmplen) { /* uncomposed combining char */
399             t = (U8*)SvPVX(tmp);
400             while (tmplen--)
401                 *d++ = *t++;
402         }
403         uvS = uv;
404     } /* for */
405     *d = '\0';
406     SvCUR_set(dst, d - (U8*)SvPVX(dst));
407     RETVAL = dst;
408   OUTPUT:
409     RETVAL
410
411
412 void
413 checkNFD(arg)
414     SV * arg
415   PROTOTYPE: $
416   ALIAS:
417     checkNFKD = 1
418   PREINIT:
419     UV uv;
420     SV *src;
421     STRLEN srclen, retlen;
422     U8 *s, *e, *p, curCC, preCC;
423   CODE:
424     if (SvUTF8(arg)) {
425         src = arg;
426     } else {
427         src = sv_mortalcopy(arg);
428         sv_utf8_upgrade(src);
429     }
430     
431     s = (U8*)SvPV(src,srclen);
432     e = s + srclen;
433
434     preCC = 0;
435     for (p = s; p < e; p += retlen) {
436         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
437         if (!retlen)
438             croak(ErrRetlenIsZero);
439
440         curCC = getCombinClass(uv);
441         if (preCC > curCC && curCC != 0) /* canonical ordering violated */
442             XSRETURN_NO;
443         if (Hangul_IsS(uv) || (ix ? dec_compat(uv) : dec_canonical(uv)))
444             XSRETURN_NO;
445         preCC = curCC;
446     }
447     XSRETURN_YES;
448
449
450
451 void
452 checkNFC(arg)
453     SV * arg
454   PROTOTYPE: $
455   ALIAS:
456     checkNFKC = 1
457   PREINIT:
458     UV uv;
459     SV *src;
460     STRLEN srclen, retlen;
461     U8 *s, *e, *p, curCC, preCC;
462     bool isMAYBE;
463   CODE:
464     if (SvUTF8(arg)) {
465         src = arg;
466     } else {
467         src = sv_mortalcopy(arg);
468         sv_utf8_upgrade(src);
469     }
470     
471     s = (U8*)SvPV(src,srclen);
472     e = s + srclen;
473
474     preCC = 0;
475     isMAYBE = FALSE;
476     for (p = s; p < e; p += retlen) {
477         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
478         if (!retlen)
479             croak(ErrRetlenIsZero);
480
481         curCC = getCombinClass(uv);
482
483         if (preCC > curCC && curCC != 0) /* canonical ordering violated */
484             XSRETURN_NO;
485
486         /* get NFC/NFKC property */
487         if (Hangul_IsS(uv)) /* Hangul syllables are canonical composites */
488             ; /* YES */
489         else if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv))
490             XSRETURN_NO;
491         else if (isComp2nd(uv))
492             isMAYBE = TRUE;
493         else if (ix) {
494             char *canon, *compat;
495           /* NFKC_NO when having compatibility mapping. */
496             canon  = (char *) dec_canonical(uv);
497             compat = (char *) dec_compat(uv);
498             if (compat && !(canon && strEQ(canon, compat)))
499                 XSRETURN_NO;
500         } /* end of get NFC/NFKC property */
501
502         preCC = curCC;
503     }
504     if (isMAYBE)
505         XSRETURN_UNDEF;
506     else
507         XSRETURN_YES;
508
509
510
511 void
512 checkFCD(arg)
513     SV * arg
514   PROTOTYPE: $
515   ALIAS:
516     checkFCC = 1
517   PREINIT:
518     UV uv, uvLead, uvTrail;
519     SV *src;
520     STRLEN srclen, retlen, canlen, canret;
521     U8 *s, *e, *p, curCC, preCC;
522     U8 *sCan, *pCan, *eCan;
523     bool isMAYBE;
524   CODE:
525     if (SvUTF8(arg)) {
526         src = arg;
527     } else {
528         src = sv_mortalcopy(arg);
529         sv_utf8_upgrade(src);
530     }
531     
532     s = (U8*)SvPV(src,srclen);
533     e = s + srclen;
534
535     preCC = 0;
536     isMAYBE = FALSE;
537     for (p = s; p < e; p += retlen) {
538         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
539         if (!retlen)
540             croak(ErrRetlenIsZero);
541
542         sCan = (U8*) dec_canonical(uv);
543
544         if (sCan) {
545             canlen = (STRLEN)strlen((char *) sCan);
546             uvLead = utf8n_to_uvuni(sCan, canlen, &canret, 0);
547         }
548         else {
549             uvLead = uv;
550         }
551
552         curCC = getCombinClass(uvLead);
553
554         if (curCC != 0 && curCC < preCC) /* canonical ordering violated */
555             XSRETURN_NO;
556
557         if (ix) {
558             if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv))
559                 XSRETURN_NO;
560             else if (isComp2nd(uv))
561                 isMAYBE = TRUE;
562         }
563
564         if (sCan) {
565             eCan = sCan + canlen;
566             pCan = utf8_hop(eCan, -1);
567             if (pCan < sCan)
568                 croak(ErrHopBeforeStart);
569             uvTrail = utf8n_to_uvuni(pCan, eCan - pCan, &canret, 0);
570             preCC = getCombinClass(uvTrail);
571         }
572         else {
573             preCC = curCC;
574         }
575     }
576     if (isMAYBE)
577         XSRETURN_UNDEF;
578     else
579         XSRETURN_YES;
580
581
582
583 U8
584 getCombinClass(uv)
585     UV uv
586   PROTOTYPE: $
587
588 bool
589 isExclusion(uv)
590     UV uv
591   PROTOTYPE: $
592
593 bool
594 isSingleton(uv)
595     UV uv
596   PROTOTYPE: $
597
598 bool
599 isNonStDecomp(uv)
600     UV uv
601   PROTOTYPE: $
602
603 bool
604 isComp2nd(uv)
605     UV uv
606   PROTOTYPE: $
607   ALIAS:
608     isNFC_MAYBE  = 1
609     isNFKC_MAYBE = 2
610
611
612
613 void
614 isNFD_NO(uv)
615     UV uv
616   PROTOTYPE: $
617   ALIAS:
618     isNFKD_NO = 1
619   CODE:
620     if (Hangul_IsS(uv) || (ix ? dec_compat(uv) : dec_canonical(uv)))
621         XSRETURN_YES; /* NFD_NO or NFKD_NO */
622     else
623         XSRETURN_NO;
624
625
626
627 void
628 isComp_Ex(uv)
629     UV uv
630   PROTOTYPE: $
631   ALIAS:
632     isNFC_NO  = 0
633     isNFKC_NO = 1
634   CODE:
635     if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv))
636         XSRETURN_YES; /* NFC_NO or NFKC_NO */
637     else if (ix) {
638         char *canon, *compat;
639         canon  = (char *) dec_canonical(uv);
640         compat = (char *) dec_compat(uv);
641         if (compat && (!canon || strNE(canon, compat)))
642             XSRETURN_YES; /* NFC_NO or NFKC_NO */
643         else
644             XSRETURN_NO;
645     }
646     else
647         XSRETURN_NO;
648
649
650
651 SV*
652 getComposite(uv, uv2)
653     UV uv
654     UV uv2
655   PROTOTYPE: $$
656   PREINIT:
657     UV composite;
658   CODE:
659     composite = composite_uv(uv, uv2);
660     RETVAL = composite ? newSVuv(composite) : &PL_sv_undef;
661   OUTPUT:
662     RETVAL
663
664
665
666 SV*
667 getCanon(uv)
668     UV uv
669   PROTOTYPE: $
670   ALIAS:
671     getCompat = 1
672   PREINIT:
673     U8 * rstr;
674   CODE:
675     if (Hangul_IsS(uv)) {
676         SV * dst;
677         dst = newSV(1);
678         (void)SvPOK_only(dst);
679         sv_cat_decompHangul(dst, uv);
680         RETVAL = dst;
681     } else {
682         rstr = ix ? dec_compat(uv) : dec_canonical(uv);
683         if (!rstr)
684             XSRETURN_UNDEF;
685         RETVAL = newSVpvn((char *)rstr, strlen((char *)rstr));
686     }
687     SvUTF8_on(RETVAL);
688   OUTPUT:
689     RETVAL
690
691
692 void
693 splitOnLastStarter(arg)
694     SV * arg
695   PREINIT:
696     UV uv;
697     SV *src, *svp;
698     STRLEN srclen, retlen;
699     U8 *s, *e, *p;
700   PPCODE:
701     if (SvUTF8(arg)) {
702         src = arg;
703     } else {
704         src = sv_mortalcopy(arg);
705         sv_utf8_upgrade(src);
706     }
707
708     s = (U8*)SvPV(src,srclen);
709     e = s + srclen;
710
711     for (p = e; s < p; ) {
712         p = utf8_hop(p, -1);
713         if (p < s)
714             croak(ErrHopBeforeStart);
715         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
716         if (getCombinClass(uv) == 0) /* Last Starter found */
717             break;
718     }
719
720     svp = sv_2mortal(newSVpvn((char*)s, p - s));
721     SvUTF8_on(svp);
722     XPUSHs(svp);
723
724     svp = sv_2mortal(newSVpvn((char*)p, e - p));
725     SvUTF8_on(svp);
726     XPUSHs(svp);
727