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