summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog20
-rw-r--r--benchmark/bm_hash_aref_flo.rb4
-rw-r--r--benchmark/bm_hash_ident_flo.rb4
-rw-r--r--benchmark/bm_marshal_dump_flo.rb2
-rw-r--r--hash.c20
-rw-r--r--internal.h2
-rw-r--r--marshal.c8
-rw-r--r--st.c9
-rw-r--r--version.h6
9 files changed, 67 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index d492336cef..6dcc09818f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+Fri Feb 6 01:03:38 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * marshal.c (w_object, marshal_dump): use indetity tables for
+ arbitrary VALUE keys, because of performance of FLONUM.
+ [Bug #10761]
+
+ * marshal.c (obj_alloc_by_klass, marshal_load): ditto.
+
+Fri Feb 6 01:03:38 2015 Eric Wong <e@80x24.org>
+
+ * benchmark/bm_marshal_dump_flo.rb: new benchmark for [Bug #10761]
+
+Fri Feb 6 01:03:38 2015 Eric Wong <e@80x24.org>
+
+ * st.c (st_numhash): mix float value for flonum
+ * hash.c (rb_any_hash): ditto
+ * benchmark/bm_hash_aref_flo.rb: new benchmark
+ * benchmark/bm_hash_ident_flo.rb: ditto
+ [Bug #10761]
+
Thu Feb 5 16:30:09 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* parse.y (gettable_gen): disable warnings of possible reference
diff --git a/benchmark/bm_hash_aref_flo.rb b/benchmark/bm_hash_aref_flo.rb
new file mode 100644
index 0000000000..a097bb5915
--- /dev/null
+++ b/benchmark/bm_hash_aref_flo.rb
@@ -0,0 +1,4 @@
+h = {}
+strs = (1..10000).to_a.map!(&:to_f)
+strs.each { |s| h[s] = s }
+50.times { strs.each { |s| h[s] } }
diff --git a/benchmark/bm_hash_ident_flo.rb b/benchmark/bm_hash_ident_flo.rb
new file mode 100644
index 0000000000..0c7edfed3e
--- /dev/null
+++ b/benchmark/bm_hash_ident_flo.rb
@@ -0,0 +1,4 @@
+h = {}.compare_by_identity
+strs = (1..10000).to_a.map!(&:to_f)
+strs.each { |s| h[s] = s }
+50.times { strs.each { |s| h[s] } }
diff --git a/benchmark/bm_marshal_dump_flo.rb b/benchmark/bm_marshal_dump_flo.rb
new file mode 100644
index 0000000000..9b8d0c6afb
--- /dev/null
+++ b/benchmark/bm_marshal_dump_flo.rb
@@ -0,0 +1,2 @@
+bug10761 = 10000.times.map { |x| x.to_f }
+100.times { Marshal.dump(bug10761) }
diff --git a/hash.c b/hash.c
index bd49a1dd92..e9772bc283 100644
--- a/hash.c
+++ b/hash.c
@@ -137,7 +137,13 @@ rb_any_hash(VALUE a)
if (SPECIAL_CONST_P(a)) {
if (a == Qundef) return 0;
- if (STATIC_SYM_P(a)) a >>= (RUBY_SPECIAL_SHIFT + ID_SCOPE_SHIFT);
+ if (STATIC_SYM_P(a)) {
+ a >>= (RUBY_SPECIAL_SHIFT + ID_SCOPE_SHIFT);
+ }
+ else if (FLONUM_P(a)) {
+ /* prevent pathological behavior: [Bug #10761] */
+ a = (st_index_t)rb_float_value(a);
+ }
hnum = rb_objid_hash((st_index_t)a);
}
else if (BUILTIN_TYPE(a) == T_STRING) {
@@ -2501,6 +2507,18 @@ rb_hash_compare_by_id_p(VALUE hash)
return Qfalse;
}
+st_table *
+rb_init_identtable(void)
+{
+ return st_init_table(&identhash);
+}
+
+st_table *
+rb_init_identtable_with_size(st_index_t size)
+{
+ return st_init_table_with_size(&identhash, size);
+}
+
static int
any_p_i(VALUE key, VALUE value, VALUE arg)
{
diff --git a/internal.h b/internal.h
index 10e85c77b8..9ed083fff1 100644
--- a/internal.h
+++ b/internal.h
@@ -701,6 +701,8 @@ struct st_table *rb_hash_tbl_raw(VALUE hash);
VALUE rb_hash_has_key(VALUE hash, VALUE key);
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
long rb_objid_hash(st_index_t index);
+st_table *rb_init_identtable(void);
+st_table *rb_init_identtable_with_size(st_index_t size);
#define RHASH_TBL_RAW(h) rb_hash_tbl_raw(h)
VALUE rb_hash_keys(VALUE hash);
diff --git a/marshal.c b/marshal.c
index 6a61ae53b4..bd57b1f6db 100644
--- a/marshal.c
+++ b/marshal.c
@@ -751,7 +751,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
VALUE real_obj = obj;
obj = compat->dumper(real_obj);
if (!arg->compat_tbl) {
- arg->compat_tbl = st_init_numtable();
+ arg->compat_tbl = rb_init_identtable();
}
st_insert(arg->compat_tbl, (st_data_t)obj, (st_data_t)real_obj);
if (obj != real_obj && !ivtbl) hasiv = 0;
@@ -1000,7 +1000,7 @@ marshal_dump(int argc, VALUE *argv)
wrapper = TypedData_Make_Struct(rb_cData, struct dump_arg, &dump_arg_data, arg);
arg->dest = 0;
arg->symbols = st_init_numtable();
- arg->data = st_init_numtable();
+ arg->data = rb_init_identtable();
arg->infection = 0;
arg->compat_tbl = 0;
arg->encodings = 0;
@@ -1510,7 +1510,7 @@ obj_alloc_by_klass(VALUE klass, struct load_arg *arg, VALUE *oldclass)
if (oldclass) *oldclass = compat->oldclass;
if (!arg->compat_tbl) {
- arg->compat_tbl = st_init_numtable();
+ arg->compat_tbl = rb_init_identtable();
}
st_insert(arg->compat_tbl, (st_data_t)obj, (st_data_t)real_obj);
return obj;
@@ -2019,7 +2019,7 @@ marshal_load(int argc, VALUE *argv)
arg->src = port;
arg->offset = 0;
arg->symbols = st_init_numtable();
- arg->data = st_init_numtable();
+ arg->data = rb_init_identtable();
arg->compat_tbl = 0;
arg->proc = 0;
arg->readable = 0;
diff --git a/st.c b/st.c
index b7f66d08b4..e630c1ec48 100644
--- a/st.c
+++ b/st.c
@@ -1761,6 +1761,15 @@ st_numhash(st_data_t n)
* - (n << 3) was finally added to avoid losing bits for fixnums
* - avoid expensive modulo instructions, it is currently only
* shifts and bitmask operations.
+ * - flonum (on 64-bit) is pathologically bad, mix the actual
+ * float value in, but do not use the float value as-is since
+ * many integers get interpreted as 2.0 or -2.0 [Bug #10761]
*/
+#ifdef USE_FLONUM /* RUBY */
+ if (FLONUM_P(n)) {
+ n ^= (st_data_t)rb_float_value(n);
+ }
+#endif
+
return (st_index_t)((n>>(RUBY_SPECIAL_SHIFT+3)|(n<<3)) ^ (n>>3));
}
diff --git a/version.h b/version.h
index 03f76f28e3..1e9378d959 100644
--- a/version.h
+++ b/version.h
@@ -1,10 +1,10 @@
#define RUBY_VERSION "2.2.0"
-#define RUBY_RELEASE_DATE "2015-02-05"
-#define RUBY_PATCHLEVEL 38
+#define RUBY_RELEASE_DATE "2015-02-06"
+#define RUBY_PATCHLEVEL 39
#define RUBY_RELEASE_YEAR 2015
#define RUBY_RELEASE_MONTH 2
-#define RUBY_RELEASE_DAY 5
+#define RUBY_RELEASE_DAY 6
#include "ruby/version.h"