summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog17
-rw-r--r--include/ruby/ruby.h1
-rw-r--r--internal.h1
-rw-r--r--parse.y2
-rw-r--r--string.c25
-rw-r--r--test/ruby/test_string.rb6
6 files changed, 51 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 876b2954e5..eca114bd62 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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);
diff --git a/parse.y b/parse.y
index 064e977e9a..d17591253c 100644
--- a/parse.y
+++ b/parse.y
@@ -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
diff --git a/string.c b/string.c
index 4bc26df81e..7ee4730930 100644
--- a/string.c
+++ b/string.c
@@ -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