File Coverage

UTF8.xs
Criterion Covered Total %
statement 198 241 82.1
branch 108 172 62.7
condition n/a
subroutine n/a
pod n/a
total 306 413 74.0


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #define NEED_sv_2pv_flags
6             #include "ppport.h"
7              
8             #include "utf8_dfa32.h"
9             #include "utf8_valid.h"
10              
11             static inline STRLEN
12 2586           xs_utf8_check(const U8 *src, STRLEN len) {
13             STRLEN off;
14 2586           utf8_check_ascii((const char *)src, len, &off);
15 2586           return off;
16             };
17              
18             static void
19 3072           xs_report_unmappable(pTHX_ const UV cp, const STRLEN pos) {
20             const char *fmt;
21             U32 cat;
22              
23 3072 100         if (cp > 0x10FFFF) {
24 1024           fmt = "Can't represent super code point \\x{%"UVXf"} in position %"UVuf;
25 1024           cat = WARN_NON_UNICODE;
26             }
27 2048 50         else if ((cp & 0xF800) == 0xD800) {
28 2048           fmt = "Can't represent surrogate code point U+%"UVXf" in position %"UVuf;
29 2048           cat = WARN_SURROGATE;
30             }
31             else {
32 0           fmt = "Can't represent code point U+%04"UVXf" in position %"UVuf;
33 0           cat = WARN_UTF8;
34             }
35              
36             #if PERL_REVISION == 5 && PERL_VERSION >= 14
37 3072           Perl_ck_warner_d(aTHX_ packWARN(cat), fmt, cp, (UV)pos);
38             #else
39             Perl_warner(aTHX_ packWARN(cat), fmt, cp, (UV)pos);
40             #endif
41 0           }
42              
43             static void
44 2622           xs_report_illformed(pTHX_ const U8 *s, STRLEN len, const char *enc, STRLEN pos, const bool fatal) {
45             static const char *fmt = "Can't decode ill-formed %s octet sequence <%s> in position %"UVuf;
46             static const char *hex = "0123456789ABCDEF";
47             char seq[20 * 3 + 4];
48 2622           char *d = seq, *dstop = d + sizeof(seq) - 4;
49              
50 6350 100         while (len-- > 0) {
51 3728           const U8 c = *s++;
52 3728           *d++ = hex[c >> 4];
53 3728           *d++ = hex[c & 15];
54 3728 100         if (len) {
55 1106           *d++ = ' ';
56 1106 50         if (d == dstop) {
57 0           *d++ = '.', *d++ = '.', *d++ = '.';
58 0           break;
59             }
60             }
61             }
62 2622           *d = 0;
63              
64 2622 100         if (fatal)
65 287           Perl_croak(aTHX_ fmt, enc, seq, (UV)pos);
66             else
67 2335           Perl_warner(aTHX_ packWARN(WARN_UTF8), fmt, enc, seq, (UV)pos);
68 0           }
69              
70             static void
71             xs_utf8_encode_native(pTHX_ SV *, const U8 *, STRLEN, const bool);
72              
73             static void
74 21           xs_handle_fallback(pTHX_ SV *dsv, CV *fallback, SV *val, UV usv, STRLEN pos) {
75 21           dSP;
76             SV *str;
77             const char *src;
78             STRLEN len;
79             int count;
80              
81 21           ENTER;
82 21           SAVETMPS;
83              
84 21 50         PUSHMARK(SP);
85 21 50         EXTEND(SP, 2);
86 21           mPUSHs(val);
87 21           mPUSHu(usv);
88 21           mPUSHu((UV)pos);
89 21           PUTBACK;
90              
91 21           count = call_sv((SV *)fallback, G_SCALAR);
92              
93 21           SPAGAIN;
94              
95 21 50         if (count != 1)
96 0           croak("expected 1 return value from fallback sub, got %d\n", count);
97              
98 21           str = POPs;
99 21           src = SvPV_const(str, len);
100 21 100         if (SvUTF8(str))
101 9           sv_catpvn_nomg(dsv, src, len); /* XXX validate? */
102             else
103 12           xs_utf8_encode_native(aTHX_ dsv, (const U8 *)src, len, TRUE);
104              
105 21           PUTBACK;
106 21 50         FREETMPS;
107 21           LEAVE;
108 21           }
109              
110             static void
111 3943           xs_utf8_decode_replace(pTHX_ SV *dsv, const U8 *src, STRLEN len, STRLEN off, CV *fallback) {
112 3943           const bool do_warn = ckWARN_d(WARN_UTF8);
113              
114 3943           STRLEN pos = 0;
115             STRLEN skip;
116              
117 3943 100         (void)SvUPGRADE(dsv, SVt_PV);
118 3943 100         (void)SvGROW(dsv, off + 1);
    100          
119 3943           SvCUR_set(dsv, 0);
120 3943           SvPOK_only(dsv);
121              
122             do {
123 4913           src += off;
124 4913           len -= off;
125 4913           pos += off;
126              
127 4913           skip = utf8_maximal_subpart((const char *)src, len);
128              
129 4913 100         if (do_warn) {
130 2335           xs_report_illformed(aTHX_ src, skip, "UTF-8", pos, FALSE);
131             }
132              
133 2578           sv_catpvn_nomg(dsv, (const char *)src - off, off);
134              
135 2578 100         if (fallback) {
136 16           SV *octets = newSVpvn((const char *)src, skip);
137 16           xs_handle_fallback(aTHX_ dsv, fallback, octets, 0, pos);
138             }
139             else
140 2562           sv_catpvn_nomg(dsv, "\xEF\xBF\xBD", 3);
141              
142 2578           src += skip;
143 2578           len -= skip;
144 2578           pos += skip;
145              
146 2578           off = xs_utf8_check(src, len);
147 2578 100         if (off == len) {
148 1608           sv_catpvn_nomg(dsv, (const char *)src, off);
149 1608           break;
150             }
151 970 50         } while (len);
152 1608           }
153              
154             static void
155 3363           xs_utf8_encode_replace(pTHX_ SV *dsv, const U8 *src, STRLEN len, STRLEN off, CV *fallback) {
156             #if PERL_REVISION == 5 && PERL_VERSION >= 14
157 3363           const bool do_warn = ckWARN4_d(WARN_UTF8, WARN_NONCHAR, WARN_SURROGATE, WARN_NON_UNICODE);
158             #else
159             const bool do_warn = ckWARN_d(WARN_UTF8);
160             #endif
161 3363           STRLEN pos = 0;
162             STRLEN skip;
163             UV v;
164              
165 3363 100         (void)SvUPGRADE(dsv, SVt_PV);
166 3363 50         (void)SvGROW(dsv, off + 1);
    100          
167 3363           SvCUR_set(dsv, 0);
168 3363           SvPOK_only(dsv);
169              
170             do {
171 3367           src += off;
172 3367           len -= off;
173 3367           pos += utf8_length(src - off, src);
174              
175 3367           v = utf8n_to_uvuni(src, len, &skip, (UTF8_ALLOW_ANYUV|UTF8_CHECK_ONLY) & ~UTF8_ALLOW_LONG);
176 3367 100         if (skip == (STRLEN) -1) {
177 287           skip = 1;
178 287 100         if (UTF8_IS_START(*src)) {
179 286           STRLEN n = UTF8SKIP(src);
180 286 100         if (n > len)
181 272           n = len;
182 865 100         while (skip < n && UTF8_IS_CONTINUATION(src[skip]))
    50          
183 579           skip++;
184             }
185 287           xs_report_illformed(aTHX_ src, skip, "UTF-X", pos, TRUE);
186             }
187 3080 100         if (do_warn)
188 3072           xs_report_unmappable(aTHX_ v, pos);
189              
190 8           sv_catpvn_nomg(dsv, (const char *)src - off, off);
191              
192 8 100         if (fallback) {
193 5           SV *codepoint = newSVuv(v);
194 5 50         UV usv = (v <= 0x10FFFF && (v & 0xF800) != 0xD800) ? v : 0;
    0          
195 5           xs_handle_fallback(aTHX_ dsv, fallback, codepoint, usv, pos);
196             }
197             else
198 3           sv_catpvn_nomg(dsv, "\xEF\xBF\xBD", 3);
199              
200 8           src += skip;
201 8           len -= skip;
202 8           pos += 1;
203              
204 8           off = xs_utf8_check(src, len);
205 8 100         if (off == len) {
206 4           sv_catpvn_nomg(dsv, (const char *)src, off);
207 4           break;
208             }
209 4 50         } while (len);
210 4           }
211              
212             static void
213 13           xs_utf8_encode_native(pTHX_ SV *dsv, const U8 *src, STRLEN len, const bool append) {
214 13           const U8 *end = src + len;
215             U8 *d;
216 13           STRLEN off = 0;
217              
218 13 100         if (append)
219 12           off = SvCUR(dsv);
220              
221 13 100         (void)SvUPGRADE(dsv, SVt_PV);
222 13 50         (void)SvGROW(dsv, off + len * 2 + 1);
    100          
223 13           d = (U8 *)SvPVX(dsv) + off;
224              
225 44 100         for (; src < end; src++) {
226 31           const U8 c = *src;
227 31 100         if (c < 0x80)
228 23           *d++ = c;
229             else {
230 8           *d++ = (U8)(0xC0 | ((c >> 6) & 0x1F));
231 8           *d++ = (U8)(0x80 | ((c ) & 0x3F));
232             }
233             }
234 13           *d = 0;
235 13           SvCUR_set(dsv, d - (U8 *)SvPVX(dsv));
236 13           SvPOK_only(dsv);
237 13           }
238              
239             static void
240 0           xs_utf8_encode_native_inplace(pTHX_ SV *sv, const U8 *s, STRLEN len) {
241 0           const U8 *p = s;
242 0           const U8 *e = s + len;
243              
244 0 0         while (p < e && *p < 0x80)
    0          
245 0           p++;
246              
247 0 0         if (p != e) {
248             STRLEN size, off;
249             U8 *d;
250              
251 0           off = p - s;
252 0           size = len;
253 0 0         while (p < e)
254 0           size += (*p++ > 0x7F);
255              
256 0 0         if (SvLEN(sv) < size + 1) {
257 0           (void)sv_grow(sv, size + 1);
258 0           s = (const U8 *)SvPVX(sv);
259 0           e = s + len;
260             }
261 0           d = (U8 *)SvPVX(sv) + size;
262 0           *d = 0;
263 0 0         for (s += off, e--; e >= s; e--) {
264 0           const U8 c = *e;
265 0 0         if (c < 0x80)
266 0           *--d = c;
267             else {
268 0           *--d = (U8)(0x80 | ((c ) & 0x3F));
269 0           *--d = (U8)(0xC0 | ((c >> 6) & 0x1F));
270             }
271             }
272 0           SvCUR_set(sv, size);
273             }
274 0           SvPOK_only(sv);
275 0           }
276              
277             static void
278 3           xs_utf8_downgrade(pTHX_ SV *dsv, const U8 *s, STRLEN len) {
279 3           const U8 *e = s + len - 1;
280             U8 *d, c, v;
281              
282 3 50         (void)SvUPGRADE(dsv, SVt_PV);
283 3 50         (void)SvGROW(dsv, len + 1);
    50          
284 3           d = (U8 *)SvPVX(dsv);
285              
286 28 100         while (s < e) {
287 26           c = *s++;
288 26 100         if (c < 0x80)
289 14           *d++ = c;
290             else {
291 12 100         if ((c & 0xFE) != 0xC2)
292 1           goto error;
293 11           v = (c & 0x1F) << 6;
294 11           c = *s++;
295 11 50         if ((c & 0xC0) != 0x80)
296 0           goto error;
297 11           *d++ = (U8)(v | (c & 0x3F));
298             }
299             }
300 2 100         if (s < e + 1) {
301 1 50         if (*s < 0x80)
302 1           *d++ = *s;
303             else {
304 0           error:
305 1           croak("Can't decode a wide character string");
306             }
307             }
308 2           *d = 0;
309 2           SvCUR_set(dsv, d - (U8 *)SvPVX(dsv));
310 2           SvPOK_only(dsv);
311 2           }
312              
313             /* SVt_PV, SVt_PVIV, SVt_PVNV, SVt_PVMG */
314             #define SvPV_stealable(sv) \
315             ((SvFLAGS(sv) & ~(SVTYPEMASK|SVf_UTF8)) == (SVs_TEMP|SVf_POK|SVp_POK) && \
316             (SvTYPE(sv) >= SVt_PV && SvTYPE(sv) <= SVt_PVMG) && SvREFCNT(sv) == 1)
317              
318             MODULE = Unicode::UTF8 PACKAGE = Unicode::UTF8
319              
320             PROTOTYPES: DISABLE
321              
322             void
323             decode_utf8(octets, fallback=NULL)
324             SV *octets
325             CV *fallback
326             PREINIT:
327             const U8 *src;
328             STRLEN len, off;
329             bool reuse_sv;
330             PPCODE:
331 4289           src = (const U8 *)SvPV_const(octets, len);
332 4289 50         reuse_sv = SvPV_stealable(octets);
    0          
    0          
    0          
333 4289 100         if (SvUTF8(octets)) {
334 3 50         if (!reuse_sv) {
335 3           octets = sv_newmortal();
336 3           reuse_sv = TRUE;
337             }
338 3           xs_utf8_downgrade(aTHX_ octets, src, len);
339 2 50         if (SvCUR(octets) == len) {
340 0           ST(0) = octets;
341 0           SvUTF8_on(octets);
342 0           XSRETURN(1);
343             }
344 2           src = (const U8 *)SvPV_const(octets, len);
345             }
346 4288 100         if (utf8_check_ascii((const char *)src, len, &off)) {
347 345 100         if (reuse_sv) {
348 2           ST(0) = octets;
349 2           SvUTF8_on(octets);
350 2           XSRETURN(1);
351             }
352             else {
353 343 50         dXSTARG;
354 343           sv_setpvn(TARG, (const char *)src, len);
355 343           SvUTF8_on(TARG);
356 343 50         PUSHTARG;
357             }
358             }
359             else {
360 3943 50         dXSTARG;
361 3943           xs_utf8_decode_replace(aTHX_ TARG, src, len, off, fallback);
362 1608           SvUTF8_on(TARG);
363 1608 50         PUSHTARG;
364             }
365              
366             void
367             encode_utf8(string, fallback=NULL)
368             SV *string
369             CV *fallback
370             PREINIT:
371             const U8 *src;
372             STRLEN len;
373             bool reuse_sv;
374             PPCODE:
375 3706           src = (const U8 *)SvPV_const(string, len);
376 3706 50         reuse_sv = SvPV_stealable(string);
    0          
    0          
    0          
377 3706 100         if (!SvUTF8(string)) {
378 1 50         if (reuse_sv) {
379 0           xs_utf8_encode_native_inplace(aTHX_ string, src, len);
380 0           ST(0) = string;
381 0           XSRETURN(1);
382             }
383             else {
384 1 50         dXSTARG;
385 1           xs_utf8_encode_native(aTHX_ TARG, src, len, FALSE);
386 1 50         SvTAINT(TARG);
    0          
387 1 50         PUSHTARG;
388             }
389             }
390             else {
391             STRLEN off;
392 3705 100         if (utf8_check_ascii((const char *)src, len, &off)) {
393 342 50         if (reuse_sv) {
394 0           ST(0) = string;
395 0           SvUTF8_off(string);
396 0           XSRETURN(1);
397             }
398             else {
399 342 50         dXSTARG;
400 342           sv_setpvn(TARG, (const char *)src, len);
401 342           SvUTF8_off(TARG);
402 342 50         PUSHTARG;
403             }
404             }
405             else {
406 3363 50         dXSTARG;
407 3363           xs_utf8_encode_replace(aTHX_ TARG, src, len, off, fallback);
408 4 50         PUSHTARG;
409             }
410             }
411              
412             void
413             valid_utf8(octets)
414             SV *octets
415             PREINIT:
416             const char *src;
417             STRLEN len;
418             PPCODE:
419 2676           src = SvPV_const(octets, len);
420 2676 100         if (SvUTF8(octets)) {
421 2           octets = sv_mortalcopy(octets);
422 2 100         if (!sv_utf8_downgrade(octets, TRUE))
423 1           croak("Can't validate a wide character string");
424 1           src = SvPV_const(octets, len);
425             }
426 2675 100         ST(0) = boolSV(utf8_valid_ascii(src, len));
427 2675           XSRETURN(1);
428