diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-12-03 13:32:24 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-12-03 13:32:24 +0000 |
commit | 218d1f5e1e2377b237c294e871bd3bfd6abf6633 (patch) | |
tree | 5d10d6718b479a1f3ad9eab92365881b3415a5fd /hash.c | |
parent | 6f2efe84fb6169a03ed191606935a40640d6764c (diff) |
hash.c: same hash value for similar constructs
* hash.c (rb_hash_recursive): make similar (recursive) constructs
return same hash value. execute recursively, and rewind to the
topmost frame with an object which .eql? to the recursive
object, if recursion is detected.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43981 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'hash.c')
-rw-r--r-- | hash.c | 69 |
1 files changed, 67 insertions, 2 deletions
@@ -48,7 +48,7 @@ rb_hash_freeze(VALUE hash) VALUE rb_cHash; static VALUE envtbl; -static ID id_hash, id_yield, id_default; +static ID id_hash, id_yield, id_default, id_recursive_hash; VALUE rb_hash_set_ifnone(VALUE hash, VALUE ifnone) @@ -76,6 +76,71 @@ rb_any_cmp(VALUE a, VALUE b) return !rb_eql(a, b); } +struct hash_recursive_args { + VALUE (*func)(VALUE, VALUE, int); + VALUE obj; + VALUE arg; +}; + +static VALUE +call_hash(RB_BLOCK_CALL_FUNC_ARGLIST(tag, data)) +{ + struct hash_recursive_args *p = (struct hash_recursive_args *)tag; + return p->func(p->obj, p->arg, 0); +} + +static VALUE +rb_hash_recursive(VALUE (*func)(VALUE, VALUE, int), VALUE obj, VALUE arg) +{ + VALUE hval; + VALUE thread = rb_thread_current(); + VALUE recursion_list = rb_thread_local_aref(thread, id_recursive_hash); + struct hash_recursive_args args; + long len = 0; + int state; + + if (!NIL_P(recursion_list) && (len = RARRAY_LEN(recursion_list)) > 0) { + VALUE outer; + struct hash_recursive_args *outerp; + const VALUE *ptr = RARRAY_CONST_PTR(recursion_list); + long i = 0; + do { + outerp = (struct hash_recursive_args *)(ptr[i] & ~FIXNUM_FLAG); + if (outerp->obj == obj) { + len = i; + for (i = 0; i < len; ++i) { + outer = RARRAY_AREF(recursion_list, i) & ~FIXNUM_FLAG; + outerp = (struct hash_recursive_args *)outer; + if (rb_eql(obj, outerp->obj)) { + rb_throw_obj(outer, outer); + } + } + outer = RARRAY_AREF(recursion_list, len) & ~FIXNUM_FLAG; + outerp = (struct hash_recursive_args *)outer; + rb_throw_obj(outer, outer); + } + } while (++i < len); + } + else { + recursion_list = rb_ary_new(); + rb_thread_local_aset(thread, id_recursive_hash, recursion_list); + } + args.func = func; + args.obj = obj; + args.arg = arg; + rb_ary_push(recursion_list, (VALUE)&args | FIXNUM_FLAG); + hval = rb_catch_protect((VALUE)&args, call_hash, (VALUE)&args, &state); + if (RARRAY_LEN(recursion_list) >= len) { + rb_ary_set_len(recursion_list, len); + if (len == 0) rb_thread_local_aset(thread, id_recursive_hash, Qnil); + } + if (state) rb_jump_tag(state); + if (hval == (VALUE)&args) { + hval = (*func)(obj, arg, 1); + } + return hval; +} + static VALUE hash_recursive(VALUE obj, VALUE arg, int recurse) { @@ -89,7 +154,7 @@ hash_recursive(VALUE obj, VALUE arg, int recurse) VALUE rb_hash(VALUE obj) { - VALUE hval = rb_exec_recursive_paired(hash_recursive, obj, obj, 0); + VALUE hval = rb_hash_recursive(hash_recursive, obj, 0); retry: switch (TYPE(hval)) { case T_FIXNUM: |