pp_formline: simplify growing of PL_formtarget
authorDavid Mitchell <davem@iabyn.com>
Sat, 4 Feb 2017 15:54:09 +0000 (15:54 +0000)
committerDavid Mitchell <davem@iabyn.com>
Sat, 4 Feb 2017 16:03:17 +0000 (16:03 +0000)
There's some reasonably complex logic to try and second guess how much
space to allocate or reallocate for the output buffer (some of which is
my doing from 2011, 26e935cfa6e7).

This commit removes most of this and now just does:

initially, grow the buffer by the size of the format. If any further
growing is needed later on (e.g. after a utf8 upgrade or due to @*) then
just grow as needed. This may give less optimal growing in edge cases
( i.e. repeated smaller grows rather than one big grow), but the old code
was often guessing wrong anyway.

This commit also makes it *always* check whether PL_formtarget needs growing
when about to append data to it, which is safer.

pp_ctl.c

index 799baa4..0b75903 100644 (file)
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -482,7 +482,6 @@ PP(pp_formline)
     NV value;
     bool gotsome = FALSE;   /* seen at least one non-blank item on this line */
     STRLEN len;             /* length of current sv */
-    STRLEN linemax;        /* estimate of output size in bytes */
     bool item_is_utf8 = FALSE;
     bool targ_is_utf8 = FALSE;
     const char *fmt;
@@ -505,10 +504,11 @@ PP(pp_formline)
        SvTAINTED_on(PL_formtarget);
     if (DO_UTF8(PL_formtarget))
        targ_is_utf8 = TRUE;
-    /* this is an initial estimate of how much output buffer space
-     * to allocate. It may be exceeded later */
-    linemax = (SvCUR(formsv) * (IN_BYTES ? 1 : 3) + 1);
-    t = SvGROW(PL_formtarget, len + linemax + 1);
+    /* Usually the output data will be the same size as the format,
+     * so this is a good first guess. Later on, @* or utf8 upgrades
+     * may trigger further growing.
+     */
+    t = SvGROW(PL_formtarget, len + SvCUR(formsv) + 1);
     /* XXX from now onwards, SvCUR(PL_formtarget) is invalid */
     t += len;
     f = SvPV_const(formsv, len);
@@ -761,14 +761,12 @@ PP(pp_formline)
             * if trans, translate certain characters during the copy */
            {
                U8 *tmp = NULL;
-               STRLEN grow = 0;
+                STRLEN cur = t - SvPVX_const(PL_formtarget);
 
-               SvCUR_set(PL_formtarget,
-                         t - SvPVX_const(PL_formtarget));
+               SvCUR_set(PL_formtarget, cur);
 
                if (targ_is_utf8 && !item_is_utf8) {
                    source = tmp = bytes_to_utf8(source, &to_copy);
-                    grow = to_copy;
                } else {
                    if (item_is_utf8 && !targ_is_utf8) {
                        U8 *s;
@@ -776,14 +774,10 @@ PP(pp_formline)
                           a problem we have a simple solution for.
                           Don't need get magic.  */
                        sv_utf8_upgrade_nomg(PL_formtarget);
+                        cur = SvCUR(PL_formtarget); /* may have changed */
                        targ_is_utf8 = TRUE;
                        /* re-calculate linemark */
                        s = (U8*)SvPVX(PL_formtarget);
-                       /* the bytes we initially allocated to append the
-                        * whole line may have been gobbled up during the
-                        * upgrade, so allocate a whole new line's worth
-                        * for safety */
-                       grow = linemax;
                        while (linemark--)
                            s += UTF8SKIP(s);
                        linemark = s - (U8*)SvPVX(PL_formtarget);
@@ -791,17 +785,10 @@ PP(pp_formline)
                    /* Easy. They agree.  */
                    assert (item_is_utf8 == targ_is_utf8);
                }
-               if (!trans)
-                   /* @* and ^* are the only things that can exceed
-                    * the linemax, so grow by the output size, plus
-                    * a whole new form's worth in case of any further
-                    * output */
-                   grow = linemax + to_copy;
-               if (grow)
-                   SvGROW(PL_formtarget, SvCUR(PL_formtarget) + grow + 1);
-               t = SvPVX(PL_formtarget) + SvCUR(PL_formtarget);
 
+                t = SvGROW(PL_formtarget, cur + to_copy + 1) + cur;
                Copy(source, t, to_copy, char);
+
                if (trans) {
                    /* blank out ~ or control chars, depending on trans.
                     * works on bytes not chars, so relies on not
@@ -817,7 +804,7 @@ PP(pp_formline)
                }
 
                t += to_copy;
-               SvCUR_set(PL_formtarget, SvCUR(PL_formtarget) + to_copy);
+               SvCUR_set(PL_formtarget, cur + to_copy);
                if (tmp)
                    Safefree(tmp);
                break;