File Coverage

UTF8.xs
Criterion Covered Total %
statement 344 391 87.9
branch 190 262 72.5
condition n/a
subroutine n/a
pod n/a
total 534 653 81.7


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             #include "utf8_valid_stream.h"
11             #include "utf8_distance_unsafe.h"
12             #include "utf8_advance_forward_unsafe.h"
13              
14             #ifndef WARN_NON_UNICODE
15             # define WARN_NON_UNICODE WARN_UTF8
16             # define WARN_NONCHAR WARN_UTF8
17             # define WARN_SURROGATE WARN_UTF8
18             #endif
19              
20             #ifndef SvPVCLEAR
21             # define SvPVCLEAR(sv) sv_setpvs((sv), "")
22             #endif
23              
24             static inline STRLEN
25 6100           xs_utf8_check(const U8 *src, STRLEN len) {
26             STRLEN off;
27 6100           utf8_check_ascii((const char *)src, len, &off);
28 6100           return off;
29             };
30              
31             static void
32 3072           xs_report_unmappable(pTHX_ const UV cp, const STRLEN pos) {
33             const char *fmt;
34             U32 cat;
35              
36 3072 100         if (cp > 0x10FFFF) {
37 1024           fmt = "Can't represent super code point \\x{%"UVXf"} in position %"UVuf;
38 1024           cat = WARN_NON_UNICODE;
39             }
40 2048 50         else if ((cp & 0xF800) == 0xD800) {
41 2048           fmt = "Can't represent surrogate code point U+%"UVXf" in position %"UVuf;
42 2048           cat = WARN_SURROGATE;
43             }
44             else {
45 0           fmt = "Can't represent code point U+%04"UVXf" in position %"UVuf;
46 0           cat = WARN_UTF8;
47             }
48              
49             #if PERL_REVISION == 5 && PERL_VERSION >= 14
50 3072           Perl_ck_warner_d(aTHX_ packWARN(cat), fmt, cp, (UV)pos);
51             #else
52             Perl_warner(aTHX_ packWARN(cat), fmt, cp, (UV)pos);
53             #endif
54 0           }
55              
56             static void
57 6136           xs_report_illformed(pTHX_ const U8 *s, STRLEN len, const char *enc, STRLEN pos, const bool fatal) {
58             static const char *fmt = "Can't decode ill-formed %s octet sequence <%s> in position %"UVuf;
59             static const char *hex = "0123456789ABCDEF";
60             char seq[20 * 3 + 4];
61 6136           char *d = seq, *dstop = d + sizeof(seq) - 4;
62              
63 13420 100         while (len-- > 0) {
64 7284           const U8 c = *s++;
65 7284           *d++ = hex[c >> 4];
66 7284           *d++ = hex[c & 15];
67 7284 100         if (len) {
68 1148           *d++ = ' ';
69 1148 50         if (d == dstop) {
70 0           *d++ = '.', *d++ = '.', *d++ = '.';
71 0           break;
72             }
73             }
74             }
75 6136           *d = 0;
76              
77 6136 100         if (fatal)
78 287           Perl_croak(aTHX_ fmt, enc, seq, (UV)pos);
79             else
80 5849           Perl_warner(aTHX_ packWARN(WARN_UTF8), fmt, enc, seq, (UV)pos);
81 3514           }
82              
83             static void
84 70682           xs_report_illformed_read(pTHX_ const char* cur, STRLEN len, bool eof) {
85             static const char hex[] = "0123456789ABCDEF";
86             char seq[4 * 3];
87 70682           char* d = seq;
88 70682           STRLEN n = len;
89 70682           const char* fmt =
90             eof ? "Can't decode ill-formed UTF-8 octet sequence <%s> at end of file"
91 70682 100         : "Can't decode ill-formed UTF-8 octet sequence <%s>";
92              
93 141616 100         while (n-- > 0) {
94 70934           const U8 c = (U8)*cur++;
95 70934           *d++ = hex[c >> 4];
96 70934           *d++ = hex[c & 0xF];
97 70934 100         if (n)
98 252           *d++ = ' ';
99             }
100 70682           *d = '\0';
101 70682           Perl_warner(aTHX_ packWARN(WARN_UTF8), fmt, seq);
102 70682           }
103              
104             static void
105             xs_utf8_encode_native(pTHX_ SV *, const U8 *, STRLEN, const bool);
106              
107             static void
108 21           xs_handle_fallback(pTHX_ SV *dsv, CV *fallback, SV *val, UV usv, STRLEN pos) {
109 21           dSP;
110             SV *str;
111             const char *src;
112             STRLEN len;
113             int count;
114              
115 21           ENTER;
116 21           SAVETMPS;
117              
118 21 50         PUSHMARK(SP);
119 21 50         EXTEND(SP, 2);
120 21           mPUSHs(val);
121 21           mPUSHu(usv);
122 21           mPUSHu((UV)pos);
123 21           PUTBACK;
124              
125 21           count = call_sv((SV *)fallback, G_SCALAR);
126              
127 21           SPAGAIN;
128              
129 21 50         if (count != 1)
130 0           croak("expected 1 return value from fallback sub, got %d\n", count);
131              
132 21           str = POPs;
133 21           src = SvPV_const(str, len);
134 21 100         if (SvUTF8(str))
135 9           sv_catpvn_nomg(dsv, src, len); /* XXX validate? */
136             else
137 12           xs_utf8_encode_native(aTHX_ dsv, (const U8 *)src, len, TRUE);
138              
139 21           PUTBACK;
140 21 50         FREETMPS;
141 21           LEAVE;
142 21           }
143              
144             static void
145 4142           xs_utf8_decode_replace(pTHX_ SV *dsv, const U8 *src, STRLEN len, STRLEN off, CV *fallback) {
146 4142           const bool do_warn = ckWARN_d(WARN_UTF8);
147              
148 4142           STRLEN pos = 0;
149             STRLEN skip;
150              
151 4142 100         (void)SvUPGRADE(dsv, SVt_PV);
152 4142 100         (void)SvGROW(dsv, off + 1);
    100          
153 4142           SvCUR_set(dsv, 0);
154 4142           SvPOK_only(dsv);
155              
156             do {
157 8427           src += off;
158 8427           len -= off;
159 8427           pos += off;
160              
161 8427           skip = utf8_maximal_subpart((const char *)src, len);
162              
163 8427 100         if (do_warn) {
164 5849           xs_report_illformed(aTHX_ src, skip, "UTF-8", pos, FALSE);
165             }
166              
167 6092           sv_catpvn_nomg(dsv, (const char *)src - off, off);
168              
169 6092 100         if (fallback) {
170 16           SV *octets = newSVpvn((const char *)src, skip);
171 16           xs_handle_fallback(aTHX_ dsv, fallback, octets, 0, pos);
172             }
173             else
174 6076           sv_catpvn_nomg(dsv, "\xEF\xBF\xBD", 3);
175              
176 6092           src += skip;
177 6092           len -= skip;
178 6092           pos += skip;
179              
180 6092           off = xs_utf8_check(src, len);
181 6092 100         if (off == len) {
182 1807           sv_catpvn_nomg(dsv, (const char *)src, off);
183 1807           break;
184             }
185 4285 50         } while (len);
186 1807           }
187              
188             static void
189 3363           xs_utf8_encode_replace(pTHX_ SV *dsv, const U8 *src, STRLEN len, STRLEN off, CV *fallback) {
190             #if PERL_REVISION == 5 && PERL_VERSION >= 14
191 3363           const bool do_warn = ckWARN4_d(WARN_UTF8, WARN_NONCHAR, WARN_SURROGATE, WARN_NON_UNICODE);
192             #else
193             const bool do_warn = ckWARN_d(WARN_UTF8);
194             #endif
195 3363           STRLEN pos = 0;
196             STRLEN skip;
197             UV v;
198              
199 3363 100         (void)SvUPGRADE(dsv, SVt_PV);
200 3363 50         (void)SvGROW(dsv, off + 1);
    100          
201 3363           SvCUR_set(dsv, 0);
202 3363           SvPOK_only(dsv);
203              
204             do {
205 3367           src += off;
206 3367           len -= off;
207 3367           pos += utf8_length(src - off, src);
208              
209 3367           v = utf8n_to_uvchr(src, len, &skip, (UTF8_ALLOW_ANYUV|UTF8_CHECK_ONLY) & ~UTF8_ALLOW_LONG);
210 3367 100         if (skip == (STRLEN) -1) {
211 287           skip = 1;
212 287 100         if (UTF8_IS_START(*src)) {
213 286           STRLEN n = UTF8SKIP(src);
214 286 100         if (n > len)
215 272           n = len;
216 865 100         while (skip < n && UTF8_IS_CONTINUATION(src[skip]))
    50          
217 579           skip++;
218             }
219 287           xs_report_illformed(aTHX_ src, skip, "UTF-X", pos, TRUE);
220             }
221 3080 100         if (do_warn)
222 3072           xs_report_unmappable(aTHX_ v, pos);
223              
224 8           sv_catpvn_nomg(dsv, (const char *)src - off, off);
225              
226 8 100         if (fallback) {
227 5           SV *codepoint = newSVuv(v);
228 5 50         UV usv = (v <= 0x10FFFF && (v & 0xF800) != 0xD800) ? v : 0;
    0          
229 5           xs_handle_fallback(aTHX_ dsv, fallback, codepoint, usv, pos);
230             }
231             else
232 3           sv_catpvn_nomg(dsv, "\xEF\xBF\xBD", 3);
233              
234 8           src += skip;
235 8           len -= skip;
236 8           pos += 1;
237              
238 8           off = xs_utf8_check(src, len);
239 8 100         if (off == len) {
240 4           sv_catpvn_nomg(dsv, (const char *)src, off);
241 4           break;
242             }
243 4 50         } while (len);
244 4           }
245              
246             static void
247 15           xs_utf8_encode_native(pTHX_ SV *dsv, const U8 *src, STRLEN len, const bool append) {
248 15           const U8 *end = src + len;
249             U8 *d;
250 15           STRLEN off = 0;
251              
252 15 100         if (append)
253 12           off = SvCUR(dsv);
254              
255 15 100         (void)SvUPGRADE(dsv, SVt_PV);
256 15 50         (void)SvGROW(dsv, off + len * 2 + 1);
    100          
257 15           d = (U8 *)SvPVX(dsv) + off;
258              
259 52 100         for (; src < end; src++) {
260 37           const U8 c = *src;
261 37 100         if (c < 0x80)
262 23           *d++ = c;
263             else {
264 14           *d++ = (U8)(0xC0 | ((c >> 6) & 0x1F));
265 14           *d++ = (U8)(0x80 | ((c ) & 0x3F));
266             }
267             }
268 15           *d = 0;
269 15           SvCUR_set(dsv, d - (U8 *)SvPVX(dsv));
270 15           SvPOK_only(dsv);
271 15           }
272              
273             static void
274 0           xs_utf8_encode_native_inplace(pTHX_ SV *sv, const U8 *s, STRLEN len) {
275 0           const U8 *p = s;
276 0           const U8 *e = s + len;
277              
278 0 0         while (p < e && *p < 0x80)
    0          
279 0           p++;
280              
281 0 0         if (p != e) {
282             STRLEN size, off;
283             U8 *d;
284              
285 0           off = p - s;
286 0           size = len;
287 0 0         while (p < e)
288 0           size += (*p++ > 0x7F);
289              
290 0 0         if (SvLEN(sv) < size + 1) {
291 0           (void)sv_grow(sv, size + 1);
292 0           s = (const U8 *)SvPVX(sv);
293 0           e = s + len;
294             }
295 0           d = (U8 *)SvPVX(sv) + size;
296 0           *d = 0;
297 0 0         for (s += off, e--; e >= s; e--) {
298 0           const U8 c = *e;
299 0 0         if (c < 0x80)
300 0           *--d = c;
301             else {
302 0           *--d = (U8)(0x80 | ((c ) & 0x3F));
303 0           *--d = (U8)(0xC0 | ((c >> 6) & 0x1F));
304             }
305             }
306 0           SvCUR_set(sv, size);
307             }
308 0           SvPOK_only(sv);
309 0           }
310              
311             static void
312 3           xs_utf8_downgrade(pTHX_ SV *dsv, const U8 *s, STRLEN len) {
313 3           const U8 *e = s + len - 1;
314             U8 *d, c, v;
315              
316 3 50         (void)SvUPGRADE(dsv, SVt_PV);
317 3 50         (void)SvGROW(dsv, len + 1);
    50          
318 3           d = (U8 *)SvPVX(dsv);
319              
320 28 100         while (s < e) {
321 26           c = *s++;
322 26 100         if (c < 0x80)
323 14           *d++ = c;
324             else {
325 12 100         if ((c & 0xFE) != 0xC2)
326 1           goto error;
327 11           v = (c & 0x1F) << 6;
328 11           c = *s++;
329 11 50         if ((c & 0xC0) != 0x80)
330 0           goto error;
331 11           *d++ = (U8)(v | (c & 0x3F));
332             }
333             }
334 2 100         if (s < e + 1) {
335 1 50         if (*s < 0x80)
336 1           *d++ = *s;
337             else {
338 0           error:
339 1           croak("Can't decode a wide character string");
340             }
341             }
342 2           *d = 0;
343 2           SvCUR_set(dsv, d - (U8 *)SvPVX(dsv));
344 2           SvPOK_only(dsv);
345 2           }
346              
347             static SSize_t
348 310839           PerlIO_read_utf8(pTHX_ PerlIO *fh, SV *bufsv, SSize_t length, SSize_t offset) {
349             utf8_valid_stream_t s;
350 310839           SSize_t got = 0; // code points counted so far
351 310839           STRLEN fed = 0; // bytes present in the output region
352 310839           STRLEN complete = 0; // last counted code-point boundary
353 310839           STRLEN base = 0; // byte offset this call writes at
354 310839           STRLEN pad = 0; // zero-fill gap for offset past end
355             STRLEN blen; // existing content length
356             char* buf;
357              
358 310839 100         if (length < 0)
359 5           croak("Negative length");
360              
361 310834 100         if (!SvOK(bufsv))
362 310820           SvPVCLEAR(bufsv);
363 310834           (void)SvPVutf8_force(bufsv, blen);
364              
365             // Resolve the byte offset (base) where this call starts writing
366 310834 100         if (offset != 0) {
367 14           char *pv = SvPVX(bufsv);
368 14           size_t chars = utf8_distance_unsafe(pv, blen);
369 14 100         if (offset < 0) {
370 4           size_t back = (size_t)-offset;
371 4 50         if (back > chars)
372 0           croak("Offset outside string");
373 4           base = utf8_advance_forward_unsafe(pv, blen, chars - back, NULL);
374             }
375 10 100         else if ((size_t)offset == chars) {
376 5           base = blen;
377             }
378 5 50         else if ((size_t)offset < chars) {
379 0           base = utf8_advance_forward_unsafe(pv, blen, (size_t)offset, NULL);
380             }
381             else {
382 5           pad = (STRLEN)((size_t)offset - chars);
383 5           base = blen + pad;
384             }
385             }
386              
387             // Single worst-case allocation: FFFD is 3 bytes, a valid code point up
388             // to 4, so base + length*4 + 1 covers the call; buffer never regrows.
389 310834 50         buf = SvGROW(bufsv, base + (STRLEN)length * 4 + 1);
    100          
390              
391             // Zero-fill the offset-past-end gap.
392 310834 100         if (pad) {
393 5           Zero(buf + blen, pad, char);
394 5           SvCUR_set(bufsv, base);
395             }
396              
397 310834           buf += base;
398 310834           utf8_valid_stream_init(&s);
399              
400             // Fast-gets layers expose the read buffer directly: validate, copy valid
401             // runs and substitute U+FFFD in a single pass out of it. Other layers fall
402             // back to the PerlIO_read() path below.
403 310834 100         if (PerlIO_fast_gets(fh)) {
404 465344 100         while (got < length) {
405 234029           bool eof = false;
406              
407 234029 100         if (PerlIO_get_cnt(fh) <= 0 && PerlIO_fill(fh) != 0) {
    100          
408 1878 50         if (PerlIO_error(fh))
409 0           return -1;
410 1878           eof = true;
411             }
412              
413 234029           const char* ptr = PerlIO_get_ptr(fh);
414 234029           STRLEN avail = (STRLEN)PerlIO_get_cnt(fh);
415 234029           STRLEN taken = 0;
416              
417 234029 100         if (avail == 0)
418 1878           eof = true;
419              
420 234029           bool span_eof = eof; // the whole available span is the tail iff at EOF
421              
422             // Drain the span: a single fill may hold several ill-formed subparts.
423 52810           for (;;) {
424 286839 100         if (got >= length)
425 24246           break;
426              
427             // Cap the span to the output room left (length*4 - fed bytes).
428 262593           STRLEN span = avail - taken;
429 262593           STRLEN room = (STRLEN)length * 4 - fed;
430 262593           bool tail_eof = span_eof;
431 262593 100         if (span > room) {
432 249832           span = room;
433 249832           tail_eof = false; // a capped span is not the stream tail
434             }
435              
436             utf8_valid_stream_result_t r;
437 262593           r = utf8_valid_stream_check(&s, ptr + taken, span, tail_eof);
438              
439             // Copy the valid prefix, then walk it once to count code points and
440             // honor the budget. A run resuming a carried sequence spans from
441             // `complete`, so measure from there rather than from oldfed.
442 262593 100         if (r.consumed) {
443 247130           STRLEN oldfed = fed;
444              
445 247130           Copy(ptr + taken, buf + oldfed, r.consumed, char);
446              
447             size_t took;
448 247130           STRLEN keep = utf8_advance_forward_unsafe(
449 247130           buf + complete, (oldfed - complete) + r.consumed,
450 247130           (size_t)(length - got), &took);
451              
452 247130           fed = complete + keep;
453 247130           taken += fed - oldfed;
454 247130           got += (SSize_t)took;
455 247130           complete = fed;
456              
457 247130 100         if (got >= length)
458 207069           break;
459             }
460              
461 55524 100         if (r.status == UTF8_VALID_STREAM_OK)
462 2244           break;
463              
464 53280 100         if (r.status == UTF8_VALID_STREAM_PARTIAL) {
465             // Carry the incomplete trailing sequence into the output; its
466             // continuation arrives on the next fill.
467 259           STRLEN tail = span - r.consumed;
468 259           Copy(ptr + taken, buf + fed, tail, char);
469 259           fed += tail;
470 259           taken += tail;
471 259           break;
472             }
473              
474             // ILLFORMED or TRUNCATED: emit one U+FFFD for the maximal subpart.
475             {
476 53021 100         if (ckWARN_d(WARN_UTF8)) {
477 53012           STRLEN sublen = r.carried + r.advance; // <= 3
478             char subpart[4];
479 53012 100         if (r.carried)
480 211           memcpy(subpart, buf + fed - r.carried, r.carried);
481 53012 100         if (r.advance)
482 52801           memcpy(subpart + r.carried, ptr + taken, r.advance);
483 53012           xs_report_illformed_read(aTHX_ subpart, sublen, span_eof);
484             }
485              
486 53021           fed -= r.carried; // drop the carried lead already sitting in buf
487 53021           memcpy(buf + fed, "\xEF\xBF\xBD", 3);
488 53021           fed += 3;
489 53021           got += 1;
490 53021           taken += r.advance; // skip this fill's portion of the subpart
491 53021           complete = fed;
492             }
493              
494 53021 100         if (r.status == UTF8_VALID_STREAM_TRUNCATED)
495 211           break;
496             }
497              
498 234029 100         if (avail)
499 232151           PerlIO_set_ptrcnt(fh, (char*)ptr + taken, (SSize_t)(avail - taken));
500              
501 234029 100         if (eof)
502 1878           break;
503             }
504             }
505             else {
506 219086 100         while (got < (size_t)length) {
507 142058           STRLEN req = (STRLEN)(length - got);
508 142058           SSize_t count = PerlIO_read(fh, buf + fed, req);
509              
510 142058 50         if (count < 0 || (count == 0 && PerlIO_error(fh)))
    100          
    50          
511 0           return -1;
512              
513 142058           bool eof = (count == 0);
514 142058           STRLEN scan = fed;
515 142058           fed += (STRLEN)count;
516              
517             // Drain the newly read bytes; a single read may contain several
518             // ill-formed subparts, each replaced in place with U+FFFD.
519 17602           for (;;) {
520             utf8_valid_stream_result_t r;
521 159660           r = utf8_valid_stream_check(&s, buf + scan, fed - scan, eof);
522              
523 159660 100         if (r.status == UTF8_VALID_STREAM_ILLFORMED ||
524 142058 100         r.status == UTF8_VALID_STREAM_TRUNCATED) {
525 17672           STRLEN sub_start = scan + r.consumed - r.carried; // region-rel
526 17672           STRLEN sublen = r.carried + r.advance; // <= 3
527 17672           STRLEN delta = 3 - sublen; // >= 0
528              
529 17672 100         if (ckWARN_d(WARN_UTF8))
530 17670           xs_report_illformed_read(aTHX_ buf + sub_start, sublen, eof);
531              
532 17672 100         if (delta) { // make room, then write FFFD
533 17662           Move(buf + sub_start + sublen,
534             buf + sub_start + sublen + delta,
535             fed - (sub_start + sublen), char);
536 17662           fed += delta;
537             }
538              
539 17672           memcpy(buf + sub_start, "\xEF\xBF\xBD", 3);
540 17672           scan = sub_start + 3; // resume just past the FFFD
541              
542 17672 100         if (r.status == UTF8_VALID_STREAM_TRUNCATED)
543 70           break; // truncated is the final tail
544 17602           continue; // ill-formed: keep draining
545             }
546 141988           break; // OK / PARTIAL: chunk drained
547             }
548              
549             // Count completed code points (FFFD included).
550             {
551 142058           STRLEN boundary = fed - s.pending;
552 142058           got += utf8_distance_unsafe(buf + complete, boundary - complete);
553 142058           complete = boundary;
554             }
555              
556 142058 100         if (eof)
557 613           break;
558             }
559              
560             // A trailing incomplete sequence was read but not emitted; push it back so
561             // the next call re-reads it. At EOF the drain already resolved any tail to
562             // U+FFFD, so s.pending is 0 there.
563 77641 100         if (s.pending) {
564 53           PerlIO_unread(fh, buf + fed - s.pending, s.pending);
565 53           fed -= s.pending;
566             }
567             }
568              
569 310834           SvCUR_set(bufsv, base + fed);
570 310834           *SvEND(bufsv) = '\0';
571 310834           SvUTF8_on(bufsv);
572 310834 50         SvSETMAGIC(bufsv);
573 310834           return got;
574             }
575              
576             /* SVt_PV, SVt_PVIV, SVt_PVNV, SVt_PVMG */
577             #define SvPV_stealable(sv) \
578             ((SvFLAGS(sv) & ~(SVTYPEMASK|SVf_UTF8)) == (SVs_TEMP|SVf_POK|SVp_POK) && \
579             (SvTYPE(sv) >= SVt_PV && SvTYPE(sv) <= SVt_PVMG) && SvREFCNT(sv) == 1)
580              
581             MODULE = Unicode::UTF8 PACKAGE = Unicode::UTF8
582              
583             PROTOTYPES: DISABLE
584              
585             void
586             decode_utf8(octets, fallback=NULL)
587             SV *octets
588             CV *fallback
589             PREINIT:
590             const U8 *src;
591             STRLEN len, off;
592             bool reuse_sv;
593             PPCODE:
594 4522           src = (const U8 *)SvPV_const(octets, len);
595 4522 50         reuse_sv = SvPV_stealable(octets);
    0          
    0          
    0          
596 4522 100         if (SvUTF8(octets)) {
597 3 50         if (!reuse_sv) {
598 3           octets = sv_newmortal();
599 3           reuse_sv = TRUE;
600             }
601 3           xs_utf8_downgrade(aTHX_ octets, src, len);
602 2 50         if (SvCUR(octets) == len) {
603 0           ST(0) = octets;
604 0           SvUTF8_on(octets);
605 0           XSRETURN(1);
606             }
607 2           src = (const U8 *)SvPV_const(octets, len);
608             }
609 4521 100         if (utf8_check_ascii((const char *)src, len, &off)) {
610 379 100         if (reuse_sv) {
611 2           ST(0) = octets;
612 2           SvUTF8_on(octets);
613 2           XSRETURN(1);
614             }
615             else {
616 377 50         dXSTARG;
617 377           sv_setpvn(TARG, (const char *)src, len);
618 377           SvUTF8_on(TARG);
619 377 50         PUSHTARG;
620             }
621             }
622             else {
623 4142 50         dXSTARG;
624 4142           xs_utf8_decode_replace(aTHX_ TARG, src, len, off, fallback);
625 1807           SvUTF8_on(TARG);
626 1807 50         PUSHTARG;
627             }
628              
629             void
630             encode_utf8(string, fallback=NULL)
631             SV *string
632             CV *fallback
633             PREINIT:
634             const U8 *src;
635             STRLEN len;
636             bool reuse_sv;
637             PPCODE:
638 3708           src = (const U8 *)SvPV_const(string, len);
639 3708 50         reuse_sv = SvPV_stealable(string);
    0          
    0          
    0          
640 3708 100         if (!SvUTF8(string)) {
641 3 50         if (reuse_sv) {
642 0           xs_utf8_encode_native_inplace(aTHX_ string, src, len);
643 0           ST(0) = string;
644 0           XSRETURN(1);
645             }
646             else {
647 3 50         dXSTARG;
648 3           xs_utf8_encode_native(aTHX_ TARG, src, len, FALSE);
649 3 50         SvTAINT(TARG);
    0          
650 3 50         PUSHTARG;
651             }
652             }
653             else {
654             STRLEN off;
655 3705 100         if (utf8_check_ascii((const char *)src, len, &off)) {
656 342 50         if (reuse_sv) {
657 0           ST(0) = string;
658 0           SvUTF8_off(string);
659 0           XSRETURN(1);
660             }
661             else {
662 342 50         dXSTARG;
663 342           sv_setpvn(TARG, (const char *)src, len);
664 342           SvUTF8_off(TARG);
665 342 50         PUSHTARG;
666             }
667             }
668             else {
669 3363 50         dXSTARG;
670 3363           xs_utf8_encode_replace(aTHX_ TARG, src, len, off, fallback);
671 4 50         PUSHTARG;
672             }
673             }
674              
675             IV
676             read_utf8(fh, bufsv, length, offset = 0)
677             PerlIO *fh
678             SV *bufsv
679             IV length
680             IV offset
681             PROTOTYPE:
682             *$$;$
683             CODE:
684             {
685 310839           SSize_t got = PerlIO_read_utf8(aTHX_ fh, bufsv, length, offset);
686 310834 50         if (got < 0)
687 0           XSRETURN_UNDEF;
688 310834 100         RETVAL = (IV)got;
689             }
690             OUTPUT:
691             RETVAL
692              
693             void
694             valid_utf8(octets)
695             SV *octets
696             PREINIT:
697             const char *src;
698             STRLEN len;
699             PPCODE:
700 2676           src = SvPV_const(octets, len);
701 2676 100         if (SvUTF8(octets)) {
702 2           octets = sv_mortalcopy(octets);
703 2 100         if (!sv_utf8_downgrade(octets, TRUE))
704 1           croak("Can't validate a wide character string");
705 1           src = SvPV_const(octets, len);
706             }
707 2675 100         ST(0) = boolSV(utf8_valid_ascii(src, len));
708 2675           XSRETURN(1);
709