Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 15 additions & 18 deletions string.c
Original file line number Diff line number Diff line change
Expand Up @@ -1916,7 +1916,7 @@ ec_str_alloc_heap(struct rb_execution_context_struct *ec, VALUE klass)
return (VALUE)str;
}

static inline VALUE
static inline void
str_duplicate_setup_encoding(VALUE str, VALUE dup, VALUE flags)
{
int encidx = 0;
Expand All @@ -1926,12 +1926,11 @@ str_duplicate_setup_encoding(VALUE str, VALUE dup, VALUE flags)
}
FL_SET_RAW(dup, flags & ~FL_FREEZE);
if (encidx) rb_enc_associate_index(dup, encidx);
return dup;
}

static const VALUE flag_mask = ENC_CODERANGE_MASK | ENCODING_MASK | FL_FREEZE;

static inline VALUE
static inline void
str_duplicate_setup_embed(VALUE klass, VALUE str, VALUE dup)
{
VALUE flags = FL_TEST_RAW(str, flag_mask);
Expand All @@ -1941,10 +1940,10 @@ str_duplicate_setup_embed(VALUE klass, VALUE str, VALUE dup)
RUBY_ASSERT(str_embed_capa(dup) >= len + TERM_LEN(str));
MEMCPY(RSTRING(dup)->as.embed.ary, RSTRING(str)->as.embed.ary, char, len + TERM_LEN(str));
STR_SET_LEN(dup, RSTRING_LEN(str));
return str_duplicate_setup_encoding(str, dup, flags);
str_duplicate_setup_encoding(str, dup, flags);
}

static inline VALUE
static inline void
str_duplicate_setup_heap(VALUE klass, VALUE str, VALUE dup)
{
VALUE flags = FL_TEST_RAW(str, flag_mask);
Expand All @@ -1965,32 +1964,30 @@ str_duplicate_setup_heap(VALUE klass, VALUE str, VALUE dup)
flags |= RSTRING_NOEMBED | STR_SHARED;

STR_SET_LEN(dup, RSTRING_LEN(str));
return str_duplicate_setup_encoding(str, dup, flags);
str_duplicate_setup_encoding(str, dup, flags);
}

static inline VALUE
str_duplicate_setup(VALUE klass, VALUE str, VALUE dup)
{
if (STR_EMBED_P(str)) {
return str_duplicate_setup_embed(klass, str, dup);
}
else {
return str_duplicate_setup_heap(klass, str, dup);
}
}
/* Force duplicated strings above 1024 bytes to be views rather than copies since
* copying will use memory and have significant overhead.
* Calculated as: 1024 - header size - NUL terminator size */
#define STR_DUPLICATE_MAX_EMBED_LEN ((long)(1024 - offsetof(struct RString, as.embed) - 1))

static inline VALUE
str_duplicate(VALUE klass, VALUE str)
{
VALUE dup;
if (STR_EMBED_P(str)) {
if (STR_EMBED_P(str) && RSTRING_LEN(str) <= STR_DUPLICATE_MAX_EMBED_LEN) {
dup = str_alloc_embed(klass, RSTRING_LEN(str) + TERM_LEN(str));

str_duplicate_setup_embed(klass, str, dup);
}
else {
dup = str_alloc_heap(klass);

str_duplicate_setup_heap(klass, str, dup);
}

return str_duplicate_setup(klass, str, dup);
return dup;
}

VALUE
Expand Down
6 changes: 4 additions & 2 deletions test/-ext-/string/test_rb_str_dup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
require '-test-/string'

class Test_RbStrDup < Test::Unit::TestCase
STR_DUPLICATE_MAX_EMBED_LEN = 999 # From macro defined in string.c

def test_nested_shared_non_frozen
orig_str = "a" * GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE]
orig_str = "a" * (STR_DUPLICATE_MAX_EMBED_LEN + 1)
str = Bug::String.rb_str_dup(Bug::String.rb_str_dup(orig_str))
assert_send([Bug::String, :shared_string?, str])
assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
end

def test_nested_shared_frozen
orig_str = "a" * GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE]
orig_str = "a" * (STR_DUPLICATE_MAX_EMBED_LEN + 1)
str = Bug::String.rb_str_dup(Bug::String.rb_str_dup(orig_str).freeze)
assert_send([Bug::String, :shared_string?, str])
assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
Expand Down
4 changes: 3 additions & 1 deletion test/objspace/test_objspace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ def test_memsize_of
ObjectSpace.memsize_of(//.match("")))
end

STR_DUPLICATE_MAX_EMBED_LEN = 999 # From macro defined in string.c

def test_memsize_of_root_shared_string
a = "a" * GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE]
a = "a" * (STR_DUPLICATE_MAX_EMBED_LEN + 1)
b = a.dup
c = nil
ObjectSpace.each_object(String) {|x| break c = x if a == x and x.frozen?}
Expand Down