From 218d1f5e1e2377b237c294e871bd3bfd6abf6633 Mon Sep 17 00:00:00 2001 From: nobu Date: Tue, 3 Dec 2013 13:32:24 +0000 Subject: 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 --- hash.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) (limited to 'hash.c') diff --git a/hash.c b/hash.c index 7d27c543d7..a69d99a821 100644 --- a/hash.c +++ b/hash.c @@ -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: -- cgit v1.2.3