This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Update to Unicode::Normalize 0.15 (+ the EBCDIC guards)
[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 /* At present, char > 0x10ffff are unaffected without complaint, right? */
24 #define VALID_UTF_MAX    (0x10ffff)
25 #define OVER_UTF_MAX(uv) (VALID_UTF_MAX < (uv))
26
27 /* HANGUL_H */
28 #define Hangul_SBase  0xAC00
29 #define Hangul_SFinal 0xD7A3
30 #define Hangul_SCount  11172
31
32 #define Hangul_NCount    588
33
34 #define Hangul_LBase  0x1100
35 #define Hangul_LFinal 0x1112
36 #define Hangul_LCount     19
37
38 #define Hangul_VBase  0x1161
39 #define Hangul_VFinal 0x1175
40 #define Hangul_VCount     21
41
42 #define Hangul_TBase  0x11A7
43 #define Hangul_TFinal 0x11C2
44 #define Hangul_TCount     28
45
46 #define Hangul_IsS(u)  ((Hangul_SBase <= (u)) && ((u) <= Hangul_SFinal))
47 #define Hangul_IsN(u)  (((u) - Hangul_SBase) % Hangul_TCount == 0)
48 #define Hangul_IsLV(u) (Hangul_IsS(u) && Hangul_IsN(u))
49 #define Hangul_IsL(u)  ((Hangul_LBase <= (u)) && ((u) <= Hangul_LFinal))
50 #define Hangul_IsV(u)  ((Hangul_VBase <= (u)) && ((u) <= Hangul_VFinal))
51 #define Hangul_IsT(u)  ((Hangul_TBase  < (u)) && ((u) <= Hangul_TFinal))
52 /* HANGUL_H */
53
54 /* this is used for canonical ordering of combining characters (c.c.). */
55 typedef struct {
56     U8 cc;      /* combining class */
57     UV uv;      /* codepoint */
58     STRLEN pos; /* position */
59 } UNF_cc;
60
61 int compare_cc(const void *a, const void *b)
62 {
63     int ret_cc;
64     ret_cc = (*(UNF_cc*)a).cc - (*(UNF_cc*)b).cc;
65     if (ret_cc)
66         return ret_cc;
67     return (*(UNF_cc*)a).pos - (*(UNF_cc*)b).pos;
68 }
69
70 U8* dec_canonical (UV uv)
71 {
72     U8 ***plane, **row;
73     if (OVER_UTF_MAX(uv))
74         return NULL;
75     plane = (U8***)UNF_canon[uv >> 16];
76     if (! plane)
77         return NULL;
78     row = plane[(uv >> 8) & 0xff];
79     return row ? row[uv & 0xff] : NULL;
80 }
81
82 U8* dec_compat (UV uv)
83 {
84     U8 ***plane, **row;
85     if (OVER_UTF_MAX(uv))
86         return NULL;
87     plane = (U8***)UNF_compat[uv >> 16];
88     if (! plane)
89         return NULL;
90     row = plane[(uv >> 8) & 0xff];
91     return row ? row[uv & 0xff] : NULL;
92 }
93
94 UV composite_uv (UV uv, UV uv2)
95 {
96     UNF_complist ***plane, **row, *cell, *i;
97
98     if (! uv2 || OVER_UTF_MAX(uv) || OVER_UTF_MAX(uv2))
99         return 0;
100
101     if (Hangul_IsL(uv) && Hangul_IsV(uv2)) {
102         uv  -= Hangul_LBase; /* lindex */
103         uv2 -= Hangul_VBase; /* vindex */
104         return(Hangul_SBase + (uv * Hangul_VCount + uv2) * Hangul_TCount);
105     }
106     if (Hangul_IsLV(uv) && Hangul_IsT(uv2)) {
107         uv2 -= Hangul_TBase; /* tindex */
108         return(uv + uv2);
109     }
110     plane = UNF_compos[uv >> 16];
111     if (! plane)
112         return 0;
113     row = plane[(uv >> 8) & 0xff];
114     if (! row)
115         return 0;
116     cell = row[uv & 0xff];
117     if (! cell)
118         return 0;
119     for (i = cell; i->nextchar; i++) {
120         if (uv2 == i->nextchar)
121             return i->composite;
122     }
123     return 0;
124 }
125
126 U8 getCombinClass (UV uv)
127 {
128     U8 **plane, *row;
129     if (OVER_UTF_MAX(uv))
130         return 0;
131     plane = (U8**)UNF_combin[uv >> 16];
132     if (! plane)
133         return 0;
134     row = plane[(uv >> 8) & 0xff];
135     return row ? row[uv & 0xff] : 0;
136 }
137
138 void sv_cat_decompHangul (SV* sv, UV uv)
139 {
140     UV sindex, lindex, vindex, tindex;
141     U8 *t, tmp[3 * UTF8_MAXLEN + 1];
142
143     if (! Hangul_IsS(uv))
144         return;
145
146     sindex =  uv - Hangul_SBase;
147     lindex =  sindex / Hangul_NCount;
148     vindex = (sindex % Hangul_NCount) / Hangul_TCount;
149     tindex =  sindex % Hangul_TCount;
150
151     t = tmp;
152     t = uvuni_to_utf8(t, (lindex + Hangul_LBase));
153     t = uvuni_to_utf8(t, (vindex + Hangul_VBase));
154     if (tindex)
155         t = uvuni_to_utf8(t, (tindex + Hangul_TBase));
156     *t = '\0';
157     sv_catpvn(sv, (char *)tmp, strlen((char *)tmp));
158 }
159
160 MODULE = Unicode::Normalize     PACKAGE = Unicode::Normalize
161
162 SV*
163 decompose(arg, compat = &PL_sv_no)
164     SV * arg
165     SV * compat
166   PROTOTYPE: $;$
167   PREINIT:
168     UV uv;
169     SV *src, *dst;
170     STRLEN srclen, retlen;
171     U8 *s, *e, *p, *r;
172     bool iscompat;
173   CODE:
174     if (SvUTF8(arg)) {
175         src = arg;
176     } else {
177         src = sv_mortalcopy(arg);
178         sv_utf8_upgrade(src);
179     }
180     iscompat = SvTRUE(compat);
181
182     dst = newSV(1);
183     (void)SvPOK_only(dst);
184     SvUTF8_on(dst);
185
186     s = (U8*)SvPV(src,srclen);
187     e = s + srclen;
188     for (p = s; p < e;) {
189         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
190         p += retlen;
191         if (Hangul_IsS(uv))
192             sv_cat_decompHangul(dst, uv);
193         else {
194             r = iscompat ? dec_compat(uv) : dec_canonical(uv);
195             if (r)
196                 sv_catpv(dst, (char *)r);
197             else
198                 sv_catpvn(dst, (char *)p - retlen, retlen);
199         }
200     }
201     RETVAL = dst;
202   OUTPUT:
203     RETVAL
204
205
206
207 SV*
208 reorder(arg)
209     SV * arg
210   PROTOTYPE: $
211   PREINIT:
212     SV *src, *dst;
213     STRLEN srclen, dstlen, retlen, stk_cc_max;
214     U8 *s, *e, *p, *d, curCC;
215     UV uv;
216     UNF_cc * stk_cc;
217   CODE:
218     if (SvUTF8(arg)) {
219         src = arg;
220     } else {
221         src = sv_mortalcopy(arg);
222         sv_utf8_upgrade(src);
223     }
224
225     s = (U8*)SvPV(src, srclen);
226
227     dstlen = srclen + 1;
228     dst = newSV(dstlen);
229     sv_setpvn(dst,s,srclen);
230     SvUTF8_on(dst);
231
232     stk_cc_max = 10; /* enough as an initial value? */
233     New(0, stk_cc, stk_cc_max, UNF_cc);
234
235     d = (U8*)SvPV(dst,dstlen);
236     e = d + dstlen;
237
238     for (p = d; p < e;) {
239         U8 *cc_in;
240         STRLEN cc_len, cc_iter, cc_pos;
241
242         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
243         curCC = getCombinClass(uv);
244         p += retlen;
245
246         if (! (curCC && p < e))
247             continue;
248         else
249             cc_in = p - retlen;
250
251         cc_pos = 0;
252         stk_cc[cc_pos].cc  = curCC;
253         stk_cc[cc_pos].uv  = uv;
254         stk_cc[cc_pos].pos = cc_pos;
255
256         while (p < e) {
257             uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
258             curCC = getCombinClass(uv);
259             if (!curCC)
260                 break;
261             p += retlen;
262             cc_pos++;
263             if (stk_cc_max <= cc_pos) { /* extend if need */
264                 stk_cc_max = cc_pos + 1;
265                 Renew(stk_cc, stk_cc_max, UNF_cc);
266             }
267             stk_cc[cc_pos].cc  = curCC;
268             stk_cc[cc_pos].uv  = uv;
269             stk_cc[cc_pos].pos = cc_pos;
270         }
271
272          /* only one c.c. in cc_len from cc_in, no need of reordering */
273         if (!cc_pos)
274             continue;
275
276         qsort((void*)stk_cc, cc_pos + 1, sizeof(UNF_cc), compare_cc);
277
278         cc_len = p - cc_in;
279         p = cc_in;
280         for (cc_iter = 0; cc_iter <= cc_pos; cc_iter++) {
281             p = uvuni_to_utf8(p, stk_cc[cc_iter].uv);
282         }
283     }
284     Safefree(stk_cc);
285     RETVAL = dst;
286   OUTPUT:
287     RETVAL
288
289
290
291 SV*
292 compose(arg)
293     SV * arg
294   PROTOTYPE: $
295   PREINIT:
296     SV  *src, *dst, *tmp;
297     U8  *s, *p, *e, *d, *t, *tmp_start, curCC, preCC;
298     UV uv, uvS, uvComp;
299     STRLEN srclen, dstlen, tmplen, retlen;
300     bool beginning = TRUE;
301   CODE:
302     if (SvUTF8(arg)) {
303         src = arg;
304     } else {
305         src = sv_mortalcopy(arg);
306         sv_utf8_upgrade(src);
307     }
308
309     s = (U8*)SvPV(src, srclen);
310     e = s + srclen;
311     dstlen = srclen + 1;
312     dst = newSV(dstlen);
313     (void)SvPOK_only(dst);
314     SvUTF8_on(dst);
315     d = (U8*)SvPVX(dst);
316
317   /* for uncomposed combining char */
318     tmp = sv_2mortal(newSV(dstlen));
319     (void)SvPOK_only(tmp);
320     SvUTF8_on(tmp);
321
322     for (p = s; p < e;) {
323         if (beginning) {
324             uvS = utf8n_to_uvuni(p, e - p, &retlen, 0);
325             p += retlen;
326
327             if (getCombinClass(uvS)) { /* no Starter found yet */
328                 d = uvuni_to_utf8(d, uvS);
329                 continue;
330             }
331             beginning = FALSE;
332         }
333
334     /* Starter */
335         t = tmp_start = (U8*)SvPVX(tmp);
336         preCC = 0;
337
338     /* to the next Starter */
339         while (p < e) {
340             uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
341             p += retlen;
342             curCC = getCombinClass(uv);
343
344             if (preCC && preCC == curCC) {
345                 preCC = curCC;
346                 t = uvuni_to_utf8(t, uv);
347             } else {
348                 uvComp = composite_uv(uvS, uv);
349
350                 if (uvComp && ! isExclusion(uvComp) && preCC <= curCC) {
351                     STRLEN leftcur, rightcur, dstcur;
352                     leftcur  = UNISKIP(uvComp);
353                     rightcur = UNISKIP(uvS) + UNISKIP(uv);
354
355                     if (leftcur > rightcur) {
356                         dstcur = d - (U8*)SvPVX(dst);
357                         dstlen += leftcur - rightcur;
358                         d = (U8*)SvGROW(dst,dstlen) + dstcur;
359                     }
360                     /* preCC not changed to curCC */
361                     uvS = uvComp;
362                 } else if (! curCC && p < e) { /* blocked */
363                     break;
364                 } else {
365                     preCC = curCC;
366                     t = uvuni_to_utf8(t, uv);
367                 }
368             }
369         }
370         d = uvuni_to_utf8(d, uvS); /* starter (composed or not) */
371         tmplen = t - tmp_start;
372         if (tmplen) { /* uncomposed combining char */
373             t = (U8*)SvPVX(tmp);
374             while (tmplen--)
375                 *d++ = *t++;
376         }
377         uvS = uv;
378     } /* for */
379     SvCUR_set(dst, d - (U8*)SvPVX(dst));
380     RETVAL = dst;
381   OUTPUT:
382     RETVAL
383
384
385
386 void
387 checkNFD(arg)
388     SV * arg
389   PROTOTYPE: $
390   ALIAS:
391     checkNFKD = 1
392   PREINIT:
393     UV uv;
394     SV *src;
395     STRLEN srclen, retlen;
396     U8 *s, *e, *p, curCC, preCC;
397   PPCODE:
398     if (SvUTF8(arg)) {
399         src = arg;
400     } else {
401         src = sv_mortalcopy(arg);
402         sv_utf8_upgrade(src);
403     }
404     
405     s = (U8*)SvPV(src,srclen);
406     e = s + srclen;
407
408     preCC = 0;
409     for (p = s; p < e; p += retlen) {
410         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
411         curCC = getCombinClass(uv);
412         if (preCC > curCC && curCC != 0) /* canonical ordering violated */
413             XSRETURN_NO;
414         if (Hangul_IsS(uv) || (ix ? dec_compat(uv) : dec_canonical(uv)))
415             XSRETURN_NO;
416         preCC = curCC;
417     }
418     XSRETURN_YES;
419
420
421
422 void
423 checkNFC(arg)
424     SV * arg
425   PROTOTYPE: $
426   ALIAS:
427     checkNFKC = 1
428   PREINIT:
429     UV uv;
430     SV *src;
431     STRLEN srclen, retlen;
432     U8 *s, *e, *p, curCC, preCC;
433     bool isMAYBE;
434   PPCODE:
435     if (SvUTF8(arg)) {
436         src = arg;
437     } else {
438         src = sv_mortalcopy(arg);
439         sv_utf8_upgrade(src);
440     }
441     
442     s = (U8*)SvPV(src,srclen);
443     e = s + srclen;
444
445     preCC = 0;
446     isMAYBE = FALSE;
447     for (p = s; p < e; p += retlen) {
448         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
449         curCC = getCombinClass(uv);
450
451         if (preCC > curCC && curCC != 0) /* canonical ordering violated */
452             XSRETURN_NO;
453
454         /* get NFC/NFKC property */
455         if (Hangul_IsS(uv)) /* Hangul syllables are canonical composites */
456             ; /* YES */
457         else if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv))
458             XSRETURN_NO;
459         else if (isComp2nd(uv))
460             isMAYBE = TRUE;
461         else if (ix) {
462             char *canon, *compat;
463          /*
464           * NFKC_NO when having compatibility mapping;
465           * i.e. dec_compat(uv) defined & different with dec_canonical(uv).
466           */
467             canon  = (char *) dec_canonical(uv);
468             compat = (char *) dec_compat(uv);
469             if (compat && (!canon || strNE(canon, compat)))
470                 XSRETURN_NO;
471         } /* end of get NFC/NFKC property */
472
473         preCC = curCC;
474     }
475     if (isMAYBE)
476         XSRETURN_UNDEF;
477     else
478         XSRETURN_YES;
479
480
481
482 U8
483 getCombinClass(uv)
484     UV uv
485   PROTOTYPE: $
486
487 bool
488 isExclusion(uv)
489     UV uv
490   PROTOTYPE: $
491
492 bool
493 isSingleton(uv)
494     UV uv
495   PROTOTYPE: $
496
497 bool
498 isNonStDecomp(uv)
499     UV uv
500   PROTOTYPE: $
501
502 bool
503 isComp2nd(uv)
504     UV uv
505   PROTOTYPE: $
506   ALIAS:
507     isNFC_MAYBE  = 1
508     isNFKC_MAYBE = 2
509
510
511
512 void
513 isNFD_NO(uv)
514     UV uv
515   PROTOTYPE: $
516   ALIAS:
517     isNFKD_NO = 1
518   PPCODE:
519     if (Hangul_IsS(uv) || (ix ? dec_compat(uv) : dec_canonical(uv)))
520         XSRETURN_YES; /* NFD_NO or NFKD_NO */
521     else
522         XSRETURN_NO;
523
524
525
526 void
527 isComp_Ex(uv)
528     UV uv
529   PROTOTYPE: $
530   ALIAS:
531     isNFC_NO  = 0
532     isNFKC_NO = 1
533   PPCODE:
534     if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv))
535         XSRETURN_YES; /* NFC_NO or NFKC_NO */
536     else if (ix) {
537         char *canon, *compat;
538         canon  = (char *) dec_canonical(uv);
539         compat = (char *) dec_compat(uv);
540         if (compat && (!canon || strNE(canon, compat)))
541             XSRETURN_YES; /* NFC_NO or NFKC_NO */
542         else
543             XSRETURN_NO;
544     }
545     else
546         XSRETURN_NO;
547
548
549
550 SV*
551 getComposite(uv, uv2)
552     UV uv
553     UV uv2
554   PROTOTYPE: $$
555   PREINIT:
556     UV comp;
557   CODE:
558     comp = composite_uv(uv, uv2);
559     RETVAL = comp ? newSVuv(comp) : &PL_sv_undef;
560   OUTPUT:
561     RETVAL
562
563
564
565 SV*
566 getCanon(uv)
567     UV uv
568   PROTOTYPE: $
569   ALIAS:
570     getCompat = 1
571   PREINIT:
572     U8 * rstr;
573   CODE:
574     if (Hangul_IsS(uv)) {
575         SV * dst;
576         dst = newSV(1);
577         (void)SvPOK_only(dst);
578         sv_cat_decompHangul(dst, uv);
579         RETVAL = dst;
580     } else {
581         rstr = ix ? dec_compat(uv) : dec_canonical(uv);
582         if (!rstr)
583             XSRETURN_UNDEF;
584         RETVAL = newSVpvn((char *)rstr, strlen((char *)rstr));
585     }
586     SvUTF8_on(RETVAL);
587   OUTPUT:
588     RETVAL
589