diff options
-rw-r--r-- | string.c | 143 |
1 files changed, 76 insertions, 67 deletions
@@ -1893,35 +1893,74 @@ str_independent(VALUE str) return !str_dependent_p(str); } +static inline size_t +aligned_newsize(long len, int termlen) +{ + size_t total = 128; + + if (len >= LONG_MAX / 2 - termlen) { + if (LONG_MAX - len - termlen >= 4095) + return ((size_t)len + termlen + 4095) / 4096 * 4096; + return (size_t)len + termlen; + } + + while (total < (size_t)len + termlen) + total *= 2; + + return total; +} + static void str_make_independent_expand(VALUE str, long len, long expand, const int termlen) { - char *ptr; - const char *oldptr; - long capa = len + expand; + long newlen; + size_t total; + int independent = !str_dependent_p(str); + char *ptr, *oldptr; - if (len > capa) len = capa; + newlen = len + expand; + if (expand < 0) { + /* avoid out-of-bound access */ + len = newlen; + } - if (!STR_EMBED_P(str) && STR_EMBEDDABLE_P(capa, termlen)) { - ptr = RSTRING(str)->as.heap.ptr; - STR_SET_EMBED(str); - memcpy(RSTRING(str)->as.ary, ptr, len); - TERM_FILL(RSTRING(str)->as.ary + len, termlen); - STR_SET_EMBED_LEN(str, len); + if (STR_EMBEDDABLE_P(newlen, termlen)) { + if (!STR_EMBED_P(str)) { + oldptr = RSTRING_PTR(str); + STR_SET_EMBED(str); + if (len) memcpy(RSTRING(str)->as.ary, oldptr, len); + if (independent) ruby_xfree(oldptr); + } + TERM_FILL(RSTRING(str)->as.ary + newlen, termlen); + STR_SET_EMBED_LEN(str, newlen); return; } - ptr = ALLOC_N(char, (size_t)capa + termlen); - oldptr = RSTRING_PTR(str); - if (oldptr) { - memcpy(ptr, oldptr, len); + total = aligned_newsize(newlen, termlen); + if (independent && !STR_EMBED_P(str)) { + REALLOC_N(RSTRING(str)->as.heap.ptr, char, total); + } + else { + oldptr = RSTRING_PTR(str); + ptr = ALLOC_N(char, total); + if (len) memcpy(ptr, oldptr, len); + STR_SET_NOEMBED(str); + FL_UNSET(str, STR_SHARED|STR_NOFREE); + RSTRING(str)->as.heap.ptr = ptr; + } + RSTRING(str)->as.heap.len = newlen; + RSTRING(str)->as.heap.aux.capa = (long)(total - termlen); + TERM_FILL(RSTRING(str)->as.heap.ptr + newlen, termlen); +} + +static inline void +str_expand_if_necessary(VALUE str, long expand, int termlen) +{ + long len = RSTRING_LEN(str); + + if (str_capacity(str, termlen) < (size_t)len + expand) { + str_make_independent_expand(str, len, expand, termlen); } - STR_SET_NOEMBED(str); - FL_UNSET(str, STR_SHARED|STR_NOFREE); - TERM_FILL(ptr + len, termlen); - RSTRING(str)->as.heap.ptr = ptr; - RSTRING(str)->as.heap.len = len; - RSTRING(str)->as.heap.aux.capa = capa; } void @@ -1945,12 +1984,9 @@ rb_str_modify_expand(VALUE str, long expand) rb_raise(rb_eArgError, "string size too big"); } - if (!str_independent(str)) { + if (!str_independent(str) || expand > 0) { str_make_independent_expand(str, len, expand, termlen); } - else if (expand > 0) { - RESIZE_CAPA_TERM(str, len + expand, termlen); - } ENC_CODERANGE_CLEAR(str); } @@ -2026,17 +2062,11 @@ 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 = str_capacity(str, termlen); - /* This function assumes that (capa + termlen) bytes of memory - * is allocated, like many other functions in this file. + * is allocated, like all other functions in this file. */ - if (capa < len) { - rb_check_lockedtmp(str); - str_make_independent_expand(str, len, 0L, termlen); - } - else if (str_dependent_p(str)) { + if (str_dependent_p(str)) { if (!zero_filled(s + len, termlen)) str_make_independent_expand(str, len, 0L, termlen); } @@ -2510,51 +2540,30 @@ VALUE rb_str_resize(VALUE str, long len) { long slen; - int independent; + int termlen; if (len < 0) { rb_raise(rb_eArgError, "negative string size (or size too big)"); } - independent = str_independent(str); + str_modifiable(str); ENC_CODERANGE_CLEAR(str); slen = RSTRING_LEN(str); + termlen = TERM_LEN(str); - { - long capa; - const int termlen = TERM_LEN(str); + if (str_dependent_p(str) || str_capacity(str, termlen) < (size_t)len) { + str_make_independent_expand(str, slen, len - slen, termlen); + } + else if (len != slen) { if (STR_EMBED_P(str)) { - if (len == slen) return str; - if (STR_EMBEDDABLE_P(len, termlen)) { - STR_SET_EMBED_LEN(str, len); - TERM_FILL(RSTRING(str)->as.ary + len, termlen); - return str; - } - str_make_independent_expand(str, slen, len - slen, termlen); - } - else if (STR_EMBEDDABLE_P(len, termlen)) { - char *ptr = STR_HEAP_PTR(str); - STR_SET_EMBED(str); - if (slen > len) slen = len; - if (slen > 0) MEMCPY(RSTRING(str)->as.ary, ptr, char, slen); - TERM_FILL(RSTRING(str)->as.ary + len, termlen); STR_SET_EMBED_LEN(str, len); - if (independent) ruby_xfree(ptr); - return str; } - else if (!independent) { - if (len == slen) return str; - str_make_independent_expand(str, slen, len - slen, termlen); - } - else if ((capa = RSTRING(str)->as.heap.aux.capa) < len || - (capa - len) > (len < 1024 ? len : 1024)) { - REALLOC_N(RSTRING(str)->as.heap.ptr, char, (size_t)len + termlen); - RSTRING(str)->as.heap.aux.capa = len; + else { + STR_SET_LEN(str, len); } - else if (len == slen) return str; - RSTRING(str)->as.heap.len = len; - TERM_FILL(RSTRING(str)->as.heap.ptr + len, termlen); /* sentinel */ + TERM_FILL(RSTRING_PTR(str) + len, termlen); } + return str; } @@ -3858,7 +3867,7 @@ str_succ(VALUE str) carry_pos = s - sbeg; } } - RESIZE_CAPA(str, slen + carry_len); + str_expand_if_necessary(str, carry_len, TERM_LEN(str)); sbeg = RSTRING_PTR(str); s = sbeg + carry_pos; memmove(s + carry_len, s, slen - carry_pos); @@ -4259,7 +4268,7 @@ rb_str_splice_0(VALUE str, long beg, long len, VALUE val) RSTRING_GETMEM(str, sptr, slen); if (len < vlen) { /* expand string */ - RESIZE_CAPA(str, slen + vlen - len); + str_expand_if_necessary(str, vlen - len, TERM_LEN(str)); sptr = RSTRING_PTR(str); } @@ -4699,7 +4708,7 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) rp = RSTRING_PTR(repl); rlen = RSTRING_LEN(repl); len = RSTRING_LEN(str); if (rlen > plen) { - RESIZE_CAPA(str, len + rlen - plen); + str_expand_if_necessary(str, rlen - plen, TERM_LEN(str)); } p = RSTRING_PTR(str); if (rlen != plen) { |