File Coverage

lib/Net/IDN/Punycode.xs
Criterion Covered Total %
statement 104 104 100.0
branch 51 58 87.9
condition n/a
subroutine n/a
pod n/a
total 155 162 95.6


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4              
5             #ifdef XS_VERSION
6             #undef XS_VERSION
7             #endif
8             #define XS_VERSION "2.501"
9              
10             #define BASE 36
11             #define TMIN 1
12             #define TMAX 26
13             #define SKEW 38
14             #define DAMP 700
15             #define INITIAL_BIAS 72
16             #define INITIAL_N 128
17              
18             #define isBASE(x) UTF8_IS_INVARIANT((unsigned char)x)
19             #define DELIM '-'
20              
21             #define TMIN_MAX(t) (((t) < TMIN) ? (TMIN) : ((t) > TMAX) ? (TMAX) : (t))
22              
23             #ifndef utf8_to_uvchr_buf
24             #define utf8_to_uvchr_buf(in_p,in_e,u8) utf8_to_uvchr(in_p,u8);
25             #endif
26              
27             #ifndef uvchr_to_utf8_flags
28             #define uvchr_to_utf8_flags(d, uv, flags) uvuni_to_utf8_flags(d, uv, flags);
29             #endif
30              
31             static char enc_digit[BASE] = {
32             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
33             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
34             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
35             };
36              
37             static IV dec_digit[0x80] = {
38             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00..0F */
39             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10..1F */
40             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20..2F */
41             26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, /* 30..3F */
42             -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40..4F */
43             15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50..5F */
44             -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 60..6F */
45             15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 70..7F */
46             };
47              
48 22617           static int adapt(int delta, int numpoints, int first) {
49             int k;
50              
51 22617 100         delta /= first ? DAMP : 2;
52 22617           delta += delta/numpoints;
53              
54 41095 100         for(k=0; delta > ((BASE-TMIN) * TMAX)/2; k += BASE)
55 18478           delta /= BASE-TMIN;
56              
57 22617           return k + (((BASE-TMIN+1) * delta) / (delta+SKEW));
58             };
59              
60             static void
61 124690           grow_string(SV *const sv, char **start, char **current, char **end, STRLEN add)
62             {
63             STRLEN len;
64              
65 124690 100         if(*current + add <= *end)
66             return;
67              
68 22           len = (*current - *start);
69 22 50         *start = SvGROW(sv, (len + add + 15) & ~15);
    50          
70 22           *current = *start + len;
71 22           *end = *start + SvLEN(sv);
72             }
73              
74             MODULE = Net::IDN::Punycode PACKAGE = Net::IDN::Punycode
75              
76             SV*
77             encode_punycode(input)
78             SV * input
79             PREINIT:
80             UV c, m, n = INITIAL_N;
81             int k, q, t;
82             int bias = INITIAL_BIAS;
83             int delta = 0, skip_delta;
84              
85             const char *in_s, *in_p, *in_e, *skip_p;
86             char *re_s, *re_p, *re_e;
87             int first = 1;
88             STRLEN length_guess, len, h, u8;
89              
90             CODE:
91 1735           in_s = in_p = SvPVutf8(input, len);
92 1735           in_e = in_s + len;
93              
94             length_guess = len;
95 1735           if(length_guess < 64) length_guess = 64; /* optimise for maximum length of domain names */
96 1735           length_guess += 2; /* plus DELIM + '\0' */
97              
98 1735           RETVAL = NEWSV('P',length_guess);
99 1735           SvPOK_only(RETVAL);
100 1735           re_s = re_p = SvPV_nolen(RETVAL);
101 1735           re_e = re_s + SvLEN(RETVAL);
102             h = 0;
103              
104             /* copy basic code points */
105 18065 100         while(in_p < in_e) {
106 16330 100         if( isBASE(*in_p) ) {
107 7282           grow_string(RETVAL, &re_s, &re_p, &re_e, sizeof(char));
108 7282           *re_p++ = *in_p;
109 7282           h++;
110             }
111 16330           in_p++;
112             }
113              
114             /* add DELIM if needed */
115 1735 100         if(h) {
116 455           grow_string(RETVAL, &re_s, &re_p, &re_e, sizeof(char));
117 455           *re_p++ = DELIM;
118             }
119              
120             for(;;) {
121             /* find smallest code point not yet handled */
122 3258           m = UV_MAX;
123             q = skip_delta = 0;
124              
125 36383 100         for(in_p = skip_p = in_s; in_p < in_e;) {
126 31390           c = utf8_to_uvchr_buf((U8*)in_p, (U8*)in_e, &u8);
127             c = NATIVE_TO_UNI(c);
128              
129 31390 100         if(c >= n && c < m) {
130             m = c;
131             skip_p = in_p;
132             skip_delta = q;
133             }
134 31390 100         if(c < n)
135 24390           ++q;
136 31390           in_p += u8;
137             }
138 4993 100         if(m == UV_MAX)
139             break;
140              
141             /* increase delta to the state corresponding to
142             the m code point at the beginning of the string */
143 3258           delta += (m-n) * (h+1);
144             n = m;
145              
146             /* now find the chars to be encoded in this round */
147              
148 3258           delta += skip_delta;
149 16691 100         for(in_p = skip_p; in_p < in_e;) {
150 13433           c = utf8_to_uvchr_buf((U8*)in_p, (U8*)in_e, &u8);
151             c = NATIVE_TO_UNI(c);
152              
153 13433 100         if(c < n) {
154 7904           ++delta;
155 5529 100         } else if( c == n ) {
156             q = delta;
157              
158 6793           for(k = BASE;; k += BASE) {
159 16949           t = TMIN_MAX(k - bias);
160 10156 100         if(q < t) break;
161 6793           grow_string(RETVAL, &re_s, &re_p, &re_e, sizeof(char));
162 6793           *re_p++ = enc_digit[t + ((q-t) % (BASE-t))];
163 6793           q = (q-t) / (BASE-t);
164             }
165             if(q > BASE) croak("input exceeds punycode limit");
166 3363           grow_string(RETVAL, &re_s, &re_p, &re_e, sizeof(char));
167 3363           *re_p++ = enc_digit[q];
168 3363           bias = adapt(delta, h+1, first);
169             delta = first = 0;
170 3363           ++h;
171             }
172 13433           in_p += u8;
173             }
174 3258           ++delta;
175 3258           ++n;
176             }
177 1735           grow_string(RETVAL, &re_s, &re_p, &re_e, sizeof(char));
178 1735           *re_p = 0;
179 1735           SvCUR_set(RETVAL, re_p - re_s);
180             OUTPUT:
181             RETVAL
182              
183             SV*
184             decode_punycode(input)
185             SV * input
186             PREINIT:
187             UV c, n = INITIAL_N;
188             IV dc;
189             int i = 0, oldi, j, k, t, w;
190              
191             int bias = INITIAL_BIAS;
192             int delta = 0, skip_delta;
193              
194             const char *in_s, *in_p, *in_e, *skip_p;
195             char *re_s, *re_p, *re_e;
196             int first = 1;
197             STRLEN length_guess, len, h, u8;
198              
199             CODE:
200 8413           in_s = in_p = SvPV_nolen(input);
201 8413           in_e = SvEND(input);
202              
203 8413           length_guess = SvCUR(input) * 2;
204 8413           if(length_guess < 256) length_guess = 256;
205              
206 8413           RETVAL = NEWSV('D',length_guess);
207 8413           SvPOK_only(RETVAL);
208 8413           re_s = re_p = SvPV_nolen(RETVAL);
209 8413           re_e = re_s + SvLEN(RETVAL);
210              
211             skip_p = NULL;
212 85814 100         for(in_p = in_s; in_p < in_e; in_p++) {
213 77401           c = *in_p; /* we don't care whether it's UTF-8 */
214 77401 50         if(!isBASE(c)) croak("non-base character in input for decode_punycode");
215 77401 100         if(c == DELIM) skip_p = in_p;
216 77401           grow_string(RETVAL, &re_s, &re_p, &re_e, 1);
217 77401           *re_p++ = c; /* copy it */
218             }
219              
220 8413 100         if(skip_p) {
221 2061           h = skip_p - in_s; /* base chars handled */
222 2061           re_p = re_s + h; /* points to end of base chars */
223 2061           skip_p++; /* skip over DELIM */
224             } else {
225             h = 0; /* no base chars */
226 6352           re_p = re_s;
227             skip_p = in_s; /* read everything */
228             }
229              
230 27667 100         for(in_p = skip_p; in_p < in_e; i++) {
231             oldi = i;
232             w = 1;
233              
234 51325           for(k = BASE;; k+= BASE) {
235 70585 100         if(!(in_p < in_e)) croak("incomplete encoded code point in decode_punycode");
236 70579           dc = dec_digit[*in_p++]; /* we already know it's in 0..127 */
237 70579 50         if(dc < 0) croak("invalid digit in input for decode_punycode");
238 70579           c = (UV)dc;
239 70579           i += c * w;
240 70579           t = TMIN_MAX(k - bias);
241 70579 100         if(c < t) break;
242 51325           w *= BASE-t;
243             }
244 19254           h++;
245 19254           bias = adapt(i-oldi, h, first);
246             first = 0;
247 19254           n += i / h; /* code point n to insert */
248 19254           i = i % h; /* at position i */
249              
250 19254 50         u8 = UNISKIP(n); /* how many bytes we need */
    50          
251              
252             j = i;
253 36448 100         for(skip_p = re_s; j > 0; j--) /* find position in UTF-8 */
254 17194           skip_p+=UTF8SKIP(skip_p);
255              
256 19254           grow_string(RETVAL, &re_s, &re_p, &re_e, u8);
257 19254 100         if(skip_p < re_p) /* move succeeding chars */
258 7789           Move(skip_p, skip_p + u8, re_p - skip_p, char);
259 19254           re_p += u8;
260             uvchr_to_utf8_flags((U8*)skip_p, n, UNICODE_ALLOW_ANY);
261             }
262              
263 8407 50         if(!first) SvUTF8_on(RETVAL); /* UTF-8 chars have been inserted */
264 8407           grow_string(RETVAL, &re_s, &re_p, &re_e, 1);
265 8407           *re_p = 0;
266 8407           SvCUR_set(RETVAL, re_p - re_s);
267             OUTPUT:
268             RETVAL