Subject: [PATCH 4/9] hash.c: use rb_hash_* functions for objid_hash, double and ident.

From: Sokolov Yura aka funny_falcon <[email-masked]>
Date: Tue, 12 Jul 2016 15:05:45 +0300

Note, that ident becomes a bit slower, cause hash value is more "random".
New versions calculates fair hash value, and so provides statistically
fair collision rate.
Previous version relies on internal pattern of symbols and "usually" provides
lesser collisions. Though it could be compromised easely.
---
 hash.c     | 55 +++++++++++++++++++------------------------------------
 internal.h |  2 +-
 numeric.c  |  7 +------
 object.c   | 10 +---------
 4 files changed, 22 insertions(+), 52 deletions(-)

diff --git a/hash.c b/hash.c
index 8ed8c48..3b6c111 100644
--- a/hash.c
+++ b/hash.c
@@ -175,8 +175,7 @@ any_hash(VALUE a, st_index_t (*other_func)(VALUE))
     }
     else if (BUILTIN_TYPE(a) == T_FLOAT) {
       flt:
-	hval = rb_dbl_hash(rb_float_value(a));
-	hnum = FIX2LONG(hval);
+	hnum = rb_dbl_hash_st(rb_float_value(a));
     }
     else {
 	hnum = other_func(a);
@@ -199,34 +198,29 @@ rb_any_hash(VALUE a)
     return any_hash(a, obj_any_hash);
 }
 
-static st_index_t
-rb_num_hash_start(st_index_t n)
+st_index_t
+rb_dbl_hash_st(double d)
 {
-    /*
-     * This hash function is lightly-tuned for Ruby.  Further tuning
-     * should be possible.  Notes:
-     *
-     * - (n >> 3) alone is great for heap objects and OK for fixnum,
-     *   however symbols perform poorly.
-     * - (n >> (RUBY_SPECIAL_SHIFT+3)) was added to make symbols hash well,
-     *   n.b.: +3 to remove most ID scope, +1 worked well initially, too
-     *   n.b.: +1 (instead of 3) worked well initially, too
-     * - (n << 16) was finally added to avoid losing bits for fixnums
-     * - avoid expensive modulo instructions, it is currently only
-     *   shifts and bitmask operations.
-     */
-    return (n >> (RUBY_SPECIAL_SHIFT + 3) ^ (n << 16)) ^ (n >> 3);
+    st_index_t hash;
+    unsigned i;
+#define ind_in_dbl type_roomof(double, st_index_t)
+    union {
+	st_index_t i[ind_in_dbl];
+	double d;
+    } v = { {0} };
+    /* normalize -0.0 to 0.0 */
+    v.d = d == 0.0 ? 0.0 : d;
+    hash = rb_hash_start(v.i[0]);
+    for (i = 1; i < ind_in_dbl; i++) {
+	hash = rb_hash_uint(hash, v.i[i]);
+    }
+    return rb_hash_end(hash);
 }
 
 long
 rb_objid_hash(st_index_t index)
 {
-    st_index_t hnum = rb_num_hash_start(index);
-
-    hnum = rb_hash_start(hnum);
-    hnum = rb_hash_uint(hnum, (st_index_t)rb_any_hash);
-    hnum = rb_hash_end(hnum);
-    return hnum;
+    return rb_hash_end(index);
 }
 
 static st_index_t
@@ -258,18 +252,7 @@ static const struct st_hash_type objhash = {
 static st_index_t
 rb_ident_hash(st_data_t n)
 {
-#ifdef USE_FLONUM /* RUBY */
-    /*
-     * - 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]
-     */
-    if (FLONUM_P(n)) {
-	n ^= (st_data_t)rb_float_value(n);
-    }
-#endif
-
-    return (st_index_t)rb_num_hash_start((st_index_t)n);
+    return rb_hash_end((st_index_t)n);
 }
 
 static const struct st_hash_type identhash = {
diff --git a/internal.h b/internal.h
index c7e80aa..fad7753 100644
--- a/internal.h
+++ b/internal.h
@@ -1084,7 +1084,6 @@ struct st_table *rb_hash_tbl_raw(VALUE hash);
 VALUE rb_hash_has_key(VALUE hash, VALUE key);
 VALUE rb_hash_default_value(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);
 
@@ -1151,6 +1150,7 @@ VALUE rb_int_modulo(VALUE x, VALUE y);
 VALUE rb_int_round(VALUE num, int ndigits);
 VALUE rb_int2str(VALUE num, int base);
 VALUE rb_dbl_hash(double d);
+st_index_t  rb_dbl_hash_st(double d);
 VALUE rb_fix_plus(VALUE x, VALUE y);
 VALUE rb_int_ge(VALUE x, VALUE y);
 
diff --git a/numeric.c b/numeric.c
index d317d04..6d199c5 100644
--- a/numeric.c
+++ b/numeric.c
@@ -1359,12 +1359,7 @@ flo_hash(VALUE num)
 VALUE
 rb_dbl_hash(double d)
 {
-    st_index_t hash;
-
-    /* normalize -0.0 to 0.0 */
-    if (d == 0.0) d = 0.0;
-    hash = rb_memhash(&d, sizeof(d));
-    return ST2FIX(hash);
+    return ST2FIX(rb_dbl_hash_st(d));
 }
 
 VALUE
diff --git a/object.c b/object.c
index 05bef4d..932bd4b 100644
--- a/object.c
+++ b/object.c
@@ -162,15 +162,7 @@ rb_obj_equal(VALUE obj1, VALUE obj2)
 VALUE
 rb_obj_hash(VALUE obj)
 {
-    VALUE oid = rb_obj_id(obj);
-#if SIZEOF_LONG == SIZEOF_VOIDP
-    st_index_t index = NUM2LONG(oid);
-#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
-    st_index_t index = NUM2LL(oid);
-#else
-# error not supported
-#endif
-    return LONG2FIX(rb_objid_hash(index));
+    /* stub for documentation */
 }
 #else
 VALUE rb_obj_hash(VALUE obj);
-- 
2.7.4