This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Perl_sv_vcatpvfn_flags: handle mixed utf8 better
authorDavid Mitchell <davem@iabyn.com>
Fri, 2 Jun 2017 13:47:11 +0000 (14:47 +0100)
committerDavid Mitchell <davem@iabyn.com>
Wed, 7 Jun 2017 08:11:09 +0000 (09:11 +0100)
Once the output string gets upgraded to utf8 (e.g. due to a utf8 %s
argument), any remaining appending of plain (non-%) parts of the
format string becomes very inefficient. It basically creates an
SV out of the next format chunk, upgrades that SV to utf8, then
appends the upgraded buffer.

This commits makes it just append the format chunk byte by byte, upgrading
in the fly if that byte is !NATIVE_BYTE_IS_INVARIANT

sv.c

diff --git a/sv.c b/sv.c
index 9bfd28d..8817d77 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -11870,7 +11870,6 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
     SV *argsv = NULL;
     bool has_utf8 = DO_UTF8(sv);    /* has the result utf8? */
     const bool pat_utf8 = has_utf8; /* the pattern is in utf8? */
-    SV *nsv = NULL;
     /* Times 4: a decimal digit takes more than 3 binary digits.
      * NV_DIG: mantissa takes than many decimal digits.
      * Plus 32: Playing safe. */
@@ -11990,8 +11989,24 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
             {};
 
        if (q > fmtstart) {
-           if (has_utf8 && !pat_utf8)
-               sv_catpvn_nomg_utf8_upgrade(sv, fmtstart, q - fmtstart, nsv);
+           if (has_utf8 && !pat_utf8) {
+                /* upgrade and copy the bytes of fmtstart..q-1 to utf8 on
+                 * the fly */
+                const char *p;
+                char *dst;
+                STRLEN need = SvCUR(sv) + (q - fmtstart) + 1;
+
+                for (p = fmtstart; p < q; p++)
+                    if (!NATIVE_BYTE_IS_INVARIANT(*p))
+                        need++;
+                SvGROW(sv, need);
+
+                dst = SvEND(sv);
+                for (p = fmtstart; p < q; p++)
+                    append_utf8_from_native_byte((U8)*p, (U8**)&dst);
+                *dst = '\0';
+                SvCUR_set(sv, need - 1);
+            }
            else
                 S_sv_catpvn_simple(aTHX_ sv, fmtstart, q - fmtstart);
        }