Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

implement a better RubyHash hashing function for arrays, as the CF on…

…e isn't good enough

git-svn-id: http://svn.macosforge.org/repository/ruby/MacRuby/trunk@5042 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information...
commit 01ec3c8bd2d1ce2b12b04d1f2c46791dd2b14623 1 parent a4c25b8
Laurent Sansonetti authored
Showing with 63 additions and 32 deletions.
  1. +31 −0 NSArray.m
  2. +2 −0  array.h
  3. +28 −32 hash.c
  4. +2 −0  hash.h
View
31 NSArray.m
@@ -13,6 +13,7 @@
#include "objc.h"
#include "vm.h"
#include "array.h"
+#include "hash.h"
VALUE rb_cArray;
VALUE rb_cNSArray;
@@ -1459,3 +1460,33 @@
return values;
}
+
+// A very naive hashing function for arrays, which hashes the array's length,
+// then first and last elements (in case the array has more than 4 elements).
+// We cannot rely on the CoreFoundation hashing function for arrays as it's
+// simply returning the number of elements, and can trigger huge performance
+// problems when using same-sized arrays as keys in Hash objects.
+unsigned long
+rb_ary_hash(VALUE ary)
+{
+ const long len = RARRAY_LEN(ary);
+ unsigned long hash = 0;
+ if (len > 0) {
+ VALUE elem = RARRAY_AT(ary, 0);
+ if (elem == ary) {
+ // recursive array
+ return (unsigned long)rb_cRubyArray;
+ }
+ hash += rb_hash_code(elem);
+ if (len > 4) {
+ elem = RARRAY_AT(ary, len - 1);
+ if (elem == ary) {
+ // recursive array
+ return (unsigned long)rb_cRubyArray;
+ }
+ hash = (hash >> 3) ^ rb_hash_code(elem);
+ }
+ hash += len;
+ }
+ return hash;
+}
View
2  array.h
@@ -178,6 +178,8 @@ VALUE rary_diff(VALUE ary1, SEL sel, VALUE ary2);
VALUE rary_and(VALUE ary1, SEL sel, VALUE ary2);
VALUE rary_or(VALUE ary1, SEL sel, VALUE ary2);
+unsigned long rb_ary_hash(VALUE ary);
+
#if defined(__cplusplus)
} // extern "C"
#endif
View
60 hash.c
@@ -18,6 +18,7 @@
#include "objc.h"
#include "vm.h"
#include "hash.h"
+#include "array.h"
#include "class.h"
static VALUE rhash_try_convert(VALUE, SEL, VALUE);
@@ -36,22 +37,44 @@ static SEL selFlattenBang = 0;
static SEL selDefault = 0;
static SEL selHash = 0;
-VALUE
-rb_hash(VALUE obj)
+unsigned long
+rb_hash_code(VALUE obj)
{
+ switch (TYPE(obj)) {
+ case T_FIXNUM:
+ case T_FLOAT:
+ case T_SYMBOL:
+ case T_NIL:
+ case T_FALSE:
+ case T_TRUE:
+ return (unsigned long)obj;
+
+ case T_STRING:
+ return rb_str_hash(obj);
+
+ case T_ARRAY:
+ return rb_ary_hash(obj);
+ }
+
VALUE v = rb_vm_call(obj, selHash, 0, NULL);
retry:
switch (TYPE(v)) {
case T_FIXNUM:
- return v;
+ return FIX2LONG(v);
case T_BIGNUM:
- return LONG2FIX(((long *)(RBIGNUM_DIGITS(v)))[0]);
+ return ((unsigned long *)(RBIGNUM_DIGITS(v)))[0];
default:
v = rb_to_int(v);
goto retry;
}
}
+VALUE
+rb_hash(VALUE obj)
+{
+ return LONG2NUM(rb_hash_code(obj));
+}
+
typedef int st_foreach_func(st_data_t, st_data_t, st_data_t);
struct foreach_safe_arg {
@@ -90,40 +113,13 @@ rb_any_cmp(VALUE a, VALUE b)
if (a == b) {
return 0;
}
- const int type = TYPE(a);
- switch (type) {
- case T_FIXNUM:
- case T_FLOAT:
- case T_SYMBOL:
- if (type == TYPE(b)) {
- return a != b;
- }
- break;
-
- case T_STRING:
- return rb_str_cmp(a, b);
- }
-
return !rb_eql(a, b);
}
static int
rb_any_hash(VALUE a)
{
- switch (TYPE(a)) {
- case T_FIXNUM:
- case T_FLOAT:
- case T_SYMBOL:
- case T_NIL:
- case T_FALSE:
- case T_TRUE:
- return (int)a;
-
- case T_STRING:
- return (int)rb_str_hash(a);
- }
-
- return (int)FIX2LONG(rb_hash(a));
+ return (int)rb_hash_code(a);
}
static const struct st_hash_type objhash = {
View
2  hash.h
@@ -103,6 +103,8 @@ VALUE rhash_keys(VALUE hash, SEL sel);
VALUE rhash_has_key(VALUE hash, SEL sel, VALUE key);
VALUE rhash_set_default(VALUE hash, SEL sel, VALUE ifnone);
+unsigned long rb_hash_code(VALUE obj);
+
#if defined(__cplusplus)
} // extern "C"
#endif
Please sign in to comment.
Something went wrong with that request. Please try again.