diff --git a/ChangeLog b/ChangeLog index 100ba42d51..6d20e34d70 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Sat Jul 2 02:22:22 2016 Naohisa Goto + + * string.c (str_fill_term): When termlen increases, re-allocation + of memory for termlen should always be needed. + In this fix, if possible, decrease capa instead of realloc. + [Bug #12536] [ruby-dev:49699] + Fri Jul 1 20:20:20 2016 Naohisa Goto * string.c: Specify termlen as far as possible. diff --git a/string.c b/string.c index 55d87bb9a9..a755785b96 100644 --- a/string.c +++ b/string.c @@ -2029,17 +2029,36 @@ str_null_char(const char *s, long len, const int minlen, rb_encoding *enc) static char * str_fill_term(VALUE str, char *s, long len, int termlen) { - long capa = rb_str_capacity(str) + 1; + long capa = rb_str_capacity(str); + /* This function could be called during the encoding changing procedure. + * If so, the termlen may be different from current TERM_LEN(str). + */ + const int oldtermlen = TERM_LEN(str); - if (capa < len + termlen) { + if (capa < len + termlen - 1) { /* assumes oldtermlen is 1 here */ rb_check_lockedtmp(str); str_make_independent_expand(str, len, 0L, termlen); } else if (str_dependent_p(str)) { - if (!zero_filled(s + len, termlen)) + if ((termlen > oldtermlen) || !zero_filled(s + len, termlen)) str_make_independent_expand(str, len, 0L, termlen); } else { + if (termlen > oldtermlen) { + if (!STR_EMBED_P(str)) { + const int d = termlen - oldtermlen; + if (capa > len + d) { + /* decrease capa for the new termlen */ + capa -= d; + assert(capa >= 1); + assert(!FL_TEST((str), STR_SHARED)); + RSTRING(str)->as.heap.aux.capa = capa; + } else { + assert(capa >= len); + RESIZE_CAPA_TERM(str, capa, termlen); + } + } + } TERM_FILL(s + len, termlen); return s; }