@@ -1736,6 +1736,19 @@ rb_str_buf_new(long capa)
17361736 return str ;
17371737}
17381738
1739+ /* Allocate a String suitable for becoming the receiver of String#initialize
1740+ * with a `capacity:` of +capa+. Floors to STR_BUF_MIN_SIZE so that the buffer
1741+ * rb_str_init computes for the same capacity matches and is reused as-is
1742+ * (see the "reuse it as-is" branch in rb_str_init). Used by opt_string_new. */
1743+ VALUE
1744+ rb_str_new_capa_for_init (long capa )
1745+ {
1746+ if (capa < STR_BUF_MIN_SIZE ) {
1747+ capa = STR_BUF_MIN_SIZE ;
1748+ }
1749+ return rb_str_buf_new (capa );
1750+ }
1751+
17391752VALUE
17401753rb_str_buf_new_cstr (const char * ptr )
17411754{
@@ -2097,8 +2110,13 @@ rb_str_init(rb_execution_context_t *ec, VALUE str, VALUE orig, VALUE no_str, VAL
20972110 if (orig == str ) n = 0 ;
20982111 }
20992112 str_modifiable (str );
2100- if (STR_EMBED_P (str ) || FL_TEST (str , STR_SHARED |STR_NOFREE )) {
2101- /* make noembed always */
2113+ if (!FL_TEST (str , STR_SHARED |STR_NOFREE ) && str_capacity (str , termlen ) >= (size_t )capa ) {
2114+ /* The receiver already owns a big-enough buffer (e.g. opt_string_new
2115+ * pre-sized this object for us). Reuse it as-is, whether it is an
2116+ * embedded slot or an owned heap buffer -- do not reallocate. */
2117+ }
2118+ else if (STR_EMBED_P (str ) || FL_TEST (str , STR_SHARED |STR_NOFREE )) {
2119+ /* Move to an owned heap buffer of the requested size. */
21022120 const size_t size = (size_t )capa + termlen ;
21032121 const char * const old_ptr = RSTRING_PTR (str );
21042122 const size_t osize = RSTRING_LEN (str ) + TERM_LEN (str );
@@ -2107,19 +2125,21 @@ rb_str_init(rb_execution_context_t *ec, VALUE str, VALUE orig, VALUE no_str, VAL
21072125 memcpy (new_ptr , old_ptr , osize < size ? osize : size );
21082126 FL_UNSET_RAW (str , STR_SHARED |STR_NOFREE );
21092127 RSTRING (str )-> as .heap .ptr = new_ptr ;
2128+ FL_SET (str , STR_NOEMBED );
2129+ RSTRING (str )-> as .heap .aux .capa = capa ;
21102130 }
2111- else if (STR_HEAP_SIZE (str ) != (size_t )capa + termlen ) {
2131+ else {
2132+ /* Owned heap buffer that is too small: grow it. */
21122133 SIZED_REALLOC_N (RSTRING (str )-> as .heap .ptr , char ,
21132134 (size_t )capa + termlen , STR_HEAP_SIZE (str ));
2135+ RSTRING (str )-> as .heap .aux .capa = capa ;
21142136 }
21152137 STR_SET_LEN (str , len );
2116- TERM_FILL (& RSTRING (str )-> as . heap . ptr [ len ] , termlen );
2138+ TERM_FILL (RSTRING_PTR (str ) + len , termlen );
21172139 if (n == 1 ) {
2118- memcpy (RSTRING (str )-> as . heap . ptr , RSTRING_PTR (orig ), len );
2140+ memcpy (RSTRING_PTR (str ), RSTRING_PTR (orig ), len );
21192141 rb_enc_cr_str_exact_copy (str , orig );
21202142 }
2121- FL_SET (str , STR_NOEMBED );
2122- RSTRING (str )-> as .heap .aux .capa = capa ;
21232143 }
21242144 else if (n == 1 ) {
21252145 rb_str_replace (str , orig );
@@ -2136,7 +2156,7 @@ rb_str_init(rb_execution_context_t *ec, VALUE str, VALUE orig, VALUE no_str, VAL
21362156}
21372157
21382158/* :nodoc: */
2139- static VALUE
2159+ VALUE
21402160rb_str_s_new (int argc , VALUE * argv , VALUE klass )
21412161{
21422162 if (klass != rb_cString ) {
0 commit comments