This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
987a839162c2eeb61eb421972f42ffd97473e6d1
[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
68     return ( ((UNF_cc*) a)->pos > ((UNF_cc*) b)->pos )
69          - ( ((UNF_cc*) a)->pos < ((UNF_cc*) b)->pos );
70 }
71
72 U8* dec_canonical (UV uv)
73 {
74     U8 ***plane, **row;
75     if (OVER_UTF_MAX(uv))
76         return NULL;
77     plane = (U8***)UNF_canon[uv >> 16];
78     if (! plane)
79         return NULL;
80     row = plane[(uv >> 8) & 0xff];
81     return row ? row[uv & 0xff] : NULL;
82 }
83
84 U8* dec_compat (UV uv)
85 {
86     U8 ***plane, **row;
87     if (OVER_UTF_MAX(uv))
88         return NULL;
89     plane = (U8***)UNF_compat[uv >> 16];
90     if (! plane)
91         return NULL;
92     row = plane[(uv >> 8) & 0xff];
93     return row ? row[uv & 0xff] : NULL;
94 }
95
96 UV composite_uv (UV uv, UV uv2)
97 {
98     UNF_complist ***plane, **row, *cell, *i;
99
100     if (! uv2 || OVER_UTF_MAX(uv) || OVER_UTF_MAX(uv2))
101         return 0;
102
103     if (Hangul_IsL(uv) && Hangul_IsV(uv2)) {
104         uv  -= Hangul_LBase; /* lindex */
105         uv2 -= Hangul_VBase; /* vindex */
106         return(Hangul_SBase + (uv * Hangul_VCount + uv2) * Hangul_TCount);
107     }
108     if (Hangul_IsLV(uv) && Hangul_IsT(uv2)) {
109         uv2 -= Hangul_TBase; /* tindex */
110         return(uv + uv2);
111     }
112     plane = UNF_compos[uv >> 16];
113     if (! plane)
114         return 0;
115     row = plane[(uv >> 8) & 0xff];
116     if (! row)
117         return 0;
118     cell = row[uv & 0xff];
119     if (! cell)
120         return 0;
121     for (i = cell; i->nextchar; i++) {
122         if (uv2 == i->nextchar)
123             return i->composite;
124     }
125     return 0;
126 }
127
128 U8 getCombinClass (UV uv)
129 {
130     U8 **plane, *row;
131     if (OVER_UTF_MAX(uv))
132         return 0;
133     plane = (U8**)UNF_combin[uv >> 16];
134     if (! plane)
135         return 0;
136     row = plane[(uv >> 8) & 0xff];
137     return row ? row[uv & 0xff] : 0;
138 }
139
140 void sv_cat_decompHangul (SV* sv, UV uv)
141 {
142     UV sindex, lindex, vindex, tindex;
143     U8 *t, tmp[3 * UTF8_MAXLEN + 1];
144
145     if (! Hangul_IsS(uv))
146         return;
147
148     sindex =  uv - Hangul_SBase;
149     lindex =  sindex / Hangul_NCount;
150     vindex = (sindex % Hangul_NCount) / Hangul_TCount;
151     tindex =  sindex % Hangul_TCount;
152
153     t = tmp;
154     t = uvuni_to_utf8(t, (lindex + Hangul_LBase));
155     t = uvuni_to_utf8(t, (vindex + Hangul_VBase));
156     if (tindex)
157         t = uvuni_to_utf8(t, (tindex + Hangul_TBase));
158     *t = '\0';
159     sv_catpvn(sv, (char *)tmp, strlen((char *)tmp));
160 }
161
162 MODULE = Unicode::Normalize     PACKAGE = Unicode::Normalize
163
164 SV*
165 decompose(arg, compat = &PL_sv_no)
166     SV * arg
167     SV * compat
168   PROTOTYPE: $;$
169   PREINIT:
170     UV uv;
171     SV *src, *dst;
172     STRLEN srclen, retlen;
173     U8 *s, *e, *p, *r;
174     bool iscompat;
175   CODE:
176     if (SvUTF8(arg)) {
177         src = arg;
178     } else {
179         src = sv_mortalcopy(arg);
180         sv_utf8_upgrade(src);
181     }
182     iscompat = SvTRUE(compat);
183
184     dst = newSV(1);
185     (void)SvPOK_only(dst);
186     SvUTF8_on(dst);
187
188     s = (U8*)SvPV(src,srclen);
189     e = s + srclen;
190     for (p = s; p < e;) {
191         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
192         p += retlen;
193         if (Hangul_IsS(uv))
194             sv_cat_decompHangul(dst, uv);
195         else {
196             r = iscompat ? dec_compat(uv) : dec_canonical(uv);
197             if (r)
198                 sv_catpv(dst, (char *)r);
199             else
200                 sv_catpvn(dst, (char *)p - retlen, retlen);
201         }
202     }
203     RETVAL = dst;
204   OUTPUT:
205     RETVAL
206
207
208
209 SV*
210 reorder(arg)
211     SV * arg
212   PROTOTYPE: $
213   PREINIT:
214     SV *src, *dst;
215     STRLEN srclen, dstlen, retlen, stk_cc_max;
216     U8 *s, *e, *p, *d, curCC;
217     UV uv;
218     UNF_cc * stk_cc;
219   CODE:
220     if (SvUTF8(arg)) {
221         src = arg;
222     } else {
223         src = sv_mortalcopy(arg);
224         sv_utf8_upgrade(src);
225     }
226
227     s = (U8*)SvPV(src, srclen);
228
229     dstlen = srclen + 1;
230     dst = newSV(dstlen);
231     sv_setpvn(dst,(const char*)s,srclen);
232     SvUTF8_on(dst);
233
234     stk_cc_max = 10; /* enough as an initial value? */
235     New(0, stk_cc, stk_cc_max, UNF_cc);
236
237     d = (U8*)SvPV(dst,dstlen);
238     e = d + dstlen;
239
240     for (p = d; p < e;) {
241         U8 *cc_in;
242         STRLEN cc_len, cc_iter, cc_pos;
243
244         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
245         curCC = getCombinClass(uv);
246         p += retlen;
247
248         if (! (curCC && p < e))
249             continue;
250         else
251             cc_in = p - retlen;
252
253         cc_pos = 0;
254         stk_cc[cc_pos].cc  = curCC;
255         stk_cc[cc_pos].uv  = uv;
256         stk_cc[cc_pos].pos = cc_pos;
257
258         while (p < e) {
259             uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
260             curCC = getCombinClass(uv);
261             if (!curCC)
262                 break;
263             p += retlen;
264             cc_pos++;
265             if (stk_cc_max <= cc_pos) { /* extend if need */
266                 stk_cc_max = cc_pos + 1;
267                 Renew(stk_cc, stk_cc_max, UNF_cc);
268             }
269             stk_cc[cc_pos].cc  = curCC;
270             stk_cc[cc_pos].uv  = uv;
271             stk_cc[cc_pos].pos = cc_pos;
272         }
273
274          /* only one c.c. in cc_len from cc_in, no need of reordering */
275         if (!cc_pos)
276             continue;
277
278         qsort((void*)stk_cc, cc_pos + 1, sizeof(UNF_cc), compare_cc);
279
280         cc_len = p - cc_in;
281         p = cc_in;
282         for (cc_iter = 0; cc_iter <= cc_pos; cc_iter++) {
283             p = uvuni_to_utf8(p, stk_cc[cc_iter].uv);
284         }
285     }
286     Safefree(stk_cc);
287     RETVAL = dst;
288   OUTPUT:
289     RETVAL
290
291
292
293 SV*
294 compose(arg)
295     SV * arg
296   PROTOTYPE: $
297   PREINIT:
298     SV  *src, *dst, *tmp;
299     U8  *s, *p, *e, *d, *t, *tmp_start, curCC, preCC;
300     UV uv, uvS, uvComp;
301     STRLEN srclen, dstlen, tmplen, retlen;
302     bool beginning = TRUE;
303   CODE:
304     if (SvUTF8(arg)) {
305         src = arg;
306     } else {
307         src = sv_mortalcopy(arg);
308         sv_utf8_upgrade(src);
309     }
310
311     s = (U8*)SvPV(src, srclen);
312     e = s + srclen;
313     dstlen = srclen + 1;
314     dst = newSV(dstlen);
315     (void)SvPOK_only(dst);
316     SvUTF8_on(dst);
317     d = (U8*)SvPVX(dst);
318
319   /* for uncomposed combining char */
320     tmp = sv_2mortal(newSV(dstlen));
321     (void)SvPOK_only(tmp);
322     SvUTF8_on(tmp);
323
324     for (p = s; p < e;) {
325         if (beginning) {
326             uvS = utf8n_to_uvuni(p, e - p, &retlen, 0);
327             p += retlen;
328
329             if (getCombinClass(uvS)) { /* no Starter found yet */
330                 d = uvuni_to_utf8(d, uvS);
331                 continue;
332             }
333             beginning = FALSE;
334         }
335
336     /* Starter */
337         t = tmp_start = (U8*)SvPVX(tmp);
338         preCC = 0;
339
340     /* to the next Starter */
341         while (p < e) {
342             uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
343             p += retlen;
344             curCC = getCombinClass(uv);
345
346             if (preCC && preCC == curCC) {
347                 preCC = curCC;
348                 t = uvuni_to_utf8(t, uv);
349             } else {
350                 uvComp = composite_uv(uvS, uv);
351
352                 if (uvComp && ! isExclusion(uvComp) && preCC <= curCC) {
353                     STRLEN leftcur, rightcur, dstcur;
354                     leftcur  = UNISKIP(uvComp);
355                     rightcur = UNISKIP(uvS) + UNISKIP(uv);
356
357                     if (leftcur > rightcur) {
358                         dstcur = d - (U8*)SvPVX(dst);
359                         dstlen += leftcur - rightcur;
360                         d = (U8*)SvGROW(dst,dstlen) + dstcur;
361                     }
362                     /* preCC not changed to curCC */
363                     uvS = uvComp;
364                 } else if (! curCC && p < e) { /* blocked */
365                     break;
366                 } else {
367                     preCC = curCC;
368                     t = uvuni_to_utf8(t, uv);
369                 }
370             }
371         }
372         d = uvuni_to_utf8(d, uvS); /* starter (composed or not) */
373         tmplen = t - tmp_start;
374         if (tmplen) { /* uncomposed combining char */
375             t = (U8*)SvPVX(tmp);
376             while (tmplen--)
377                 *d++ = *t++;
378         }
379         uvS = uv;
380     } /* for */
381     *d = '\0';
382     SvCUR_set(dst, d - (U8*)SvPVX(dst));
383     RETVAL = dst;
384   OUTPUT:
385     RETVAL
386
387
388
389 void
390 checkNFD(arg)
391     SV * arg
392   PROTOTYPE: $
393   ALIAS:
394     checkNFKD = 1
395   PREINIT:
396     UV uv;
397     SV *src;
398     STRLEN srclen, retlen;
399     U8 *s, *e, *p, curCC, preCC;
400   PPCODE:
401     if (SvUTF8(arg)) {
402         src = arg;
403     } else {
404         src = sv_mortalcopy(arg);
405         sv_utf8_upgrade(src);
406     }
407     
408     s = (U8*)SvPV(src,srclen);
409     e = s + srclen;
410
411     preCC = 0;
412     for (p = s; p < e; p += retlen) {
413         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
414         curCC = getCombinClass(uv);
415         if (preCC > curCC && curCC != 0) /* canonical ordering violated */
416             XSRETURN_NO;
417         if (Hangul_IsS(uv) || (ix ? dec_compat(uv) : dec_canonical(uv)))
418             XSRETURN_NO;
419         preCC = curCC;
420     }
421     XSRETURN_YES;
422
423
424
425 void
426 checkNFC(arg)
427     SV * arg
428   PROTOTYPE: $
429   ALIAS:
430     checkNFKC = 1
431   PREINIT:
432     UV uv;
433     SV *src;
434     STRLEN srclen, retlen;
435     U8 *s, *e, *p, curCC, preCC;
436     bool isMAYBE;
437   PPCODE:
438     if (SvUTF8(arg)) {
439         src = arg;
440     } else {
441         src = sv_mortalcopy(arg);
442         sv_utf8_upgrade(src);
443     }
444     
445     s = (U8*)SvPV(src,srclen);
446     e = s + srclen;
447
448     preCC = 0;
449     isMAYBE = FALSE;
450     for (p = s; p < e; p += retlen) {
451         uv = utf8n_to_uvuni(p, e - p, &retlen, 0);
452         curCC = getCombinClass(uv);
453
454         if (preCC > curCC && curCC != 0) /* canonical ordering violated */
455             XSRETURN_NO;
456
457         /* get NFC/NFKC property */
458         if (Hangul_IsS(uv)) /* Hangul syllables are canonical composites */
459             ; /* YES */
460         else if (isExclusion(uv) || isSingleton(uv) || isNonStDecomp(uv))
461             XSRETURN_NO;
462         else if (isComp2nd(uv))
463             isMAYBE = TRUE;
464         else if (ix) {
465             char *canon, *compat;
466           /* NFKC_NO when having compatibility mapping. */
467             canon  = (char *) dec_canonical(uv);
468             compat = (char *) dec_compat(uv);
469             if (compat && !(canon && strEQ(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 composite;
557   CODE:
558     composite = composite_uv(uv, uv2);
559     RETVAL = composite ? newSVuv(composite) : &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