summaryrefslogtreecommitdiff
path: root/string.c
diff options
context:
space:
mode:
Diffstat (limited to 'string.c')
-rw-r--r--string.c25
1 files changed, 22 insertions, 3 deletions
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;
}