diff options
-rw-r--r-- | ChangeLog | 17 | ||||
-rw-r--r-- | include/ruby/ruby.h | 1 | ||||
-rw-r--r-- | internal.h | 1 | ||||
-rw-r--r-- | parse.y | 2 | ||||
-rw-r--r-- | string.c | 25 | ||||
-rw-r--r-- | test/ruby/test_string.rb | 6 |
6 files changed, 51 insertions, 1 deletions
@@ -1,3 +1,20 @@ +Thu Sep 5 13:49:00 2013 Charlie Somerville <charliesome@ruby-lang.org> + + * include/ruby/ruby.h: add RSTRING_FSTR flag + + * internal.h: add rb_fstring() prototype + + * parse.y (str_suffix_gen): deduplicate frozen string literals + + * string.c (rb_fstring): deduplicate frozen string literals + + * string.c (rb_str_free): delete fstrings from frozen_strings table when + they are GC'd + + * string.c (Init_String): initialize frozen_strings table + + * test/ruby/test_string.rb: test frozen strings are deduplicated + Thu Sep 5 12:48:00 2013 Kenta Murata <mrkn@cookpad.com> * configure.in (with_gmp): set with_gmp no if it is empty. diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index ad0c30af41..30033023bb 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -905,6 +905,7 @@ struct RString { } as; }; #define RSTRING_NOEMBED FL_USER1 +#define RSTRING_FSTR FL_USER17 #define RSTRING_EMBED_LEN_MASK (FL_USER2|FL_USER3|FL_USER4|FL_USER5|FL_USER6) #define RSTRING_EMBED_LEN_SHIFT (FL_USHIFT+2) #define RSTRING_EMBED_LEN(str) \ diff --git a/internal.h b/internal.h index e2c90e79b6..e10bf64b46 100644 --- a/internal.h +++ b/internal.h @@ -600,6 +600,7 @@ size_t rb_strftime(char *s, size_t maxsize, const char *format, rb_encoding *enc #endif /* string.c */ +VALUE rb_fstring(VALUE); int rb_str_buf_cat_escaped_char(VALUE result, unsigned int c, int unicode_p); int rb_str_symname_p(VALUE); VALUE rb_str_quote_unprintable(VALUE); @@ -8561,7 +8561,7 @@ str_suffix_gen(struct parser_params *parser, NODE *node, long opt) #endif #if STR_OPTION_FROZEN if (opt & STR_OPTION_FROZEN) { - OBJ_FREEZE(node->nd_lit); + node->nd_lit = rb_fstring(node->nd_lit); nd_set_type(node, NODE_LIT); } #endif @@ -131,6 +131,26 @@ VALUE rb_cSymbol; #define STR_ENC_GET(str) rb_enc_from_index(ENCODING_GET(str)) +static st_table* frozen_strings; + +static const struct st_hash_type fstring_hash_type = { + rb_str_cmp, + rb_str_hash +}; + +VALUE +rb_fstring(VALUE str) +{ + VALUE fstr; + if (!st_lookup(frozen_strings, (st_data_t)str, (st_data_t*)&fstr)) { + fstr = rb_str_dup(str); + OBJ_FREEZE(fstr); + RBASIC(fstr)->flags |= RSTRING_FSTR; + st_insert(frozen_strings, fstr, fstr); + } + return fstr; +} + static inline int single_byte_optimizable(VALUE str) { @@ -838,6 +858,9 @@ rb_free_tmp_buffer(volatile VALUE *store) void rb_str_free(VALUE str) { + if (FL_TEST(str, RSTRING_FSTR)) { + st_delete(frozen_strings, (st_data_t*)&str, NULL); + } if (!STR_EMBED_P(str) && !STR_SHARED_P(str)) { xfree(RSTRING(str)->as.heap.ptr); } @@ -8672,6 +8695,8 @@ Init_String(void) #undef rb_intern #define rb_intern(str) rb_intern_const(str) + frozen_strings = st_init_table(&fstring_hash_type); + rb_cString = rb_define_class("String", rb_cObject); rb_include_module(rb_cString, rb_mComparable); rb_define_alloc_func(rb_cString, empty_str_alloc); diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 06a92c5957..1387feccf5 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -2243,6 +2243,12 @@ class TestString < Test::Unit::TestCase }) end end + + def test_frozen_strings_are_deduplicated + a = "hello"f + b = "hello"f + assert_equal a.object_id, b.object_id + end end class TestString2 < TestString |