summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorglass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-07-29 12:39:21 +0000
committerglass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-07-29 12:39:21 +0000
commiteb8f91866e0bd08a6eef9c9a2bc1318e06b4ee4d (patch)
treefde6bac860df6f22615a68c9835153eefb4d6c62
parent952e444877f53492ba0c8ddae7a1fe413b536ec1 (diff)
* hash.c (rb_hash_assoc): performance improvement by replacing
compare function in RHASH(hash)->ntbl->type temporarily like r42224. it falls back to rb_hash_foreach() if st_lookup() doesn't find the key. * test/ruby/test_hash.rb: add a test for above. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42237 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog8
-rw-r--r--hash.c52
-rw-r--r--test/ruby/test_hash.rb8
3 files changed, 66 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index c1a819475e..5ab9d557fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Mon Jul 29 21:29:48 2013 Masaki Matsushita <glass.saga@gmail.com>
+
+ * hash.c (rb_hash_assoc): performance improvement by replacing
+ compare function in RHASH(hash)->ntbl->type temporarily like r42224.
+ it falls back to rb_hash_foreach() if st_lookup() doesn't find the key.
+
+ * test/ruby/test_hash.rb: add a test for above.
+
Mon Jul 29 21:15:30 2013 Akinori MUSHA <knu@iDaemons.org>
* test/ruby/test_lazy_enumerator.rb
diff --git a/hash.c b/hash.c
index b9a8cb1738..91a28ee569 100644
--- a/hash.c
+++ b/hash.c
@@ -2123,6 +2123,32 @@ rb_hash_merge(VALUE hash1, VALUE hash2)
}
static int
+assoc_cmp(VALUE a, VALUE b)
+{
+ return !RTEST(rb_equal(a, b));
+}
+
+static VALUE
+lookup2_call(VALUE arg)
+{
+ VALUE *args = (VALUE *)arg;
+ return rb_hash_lookup2(args[0], args[1], Qundef);
+}
+
+struct reset_hash_type_arg {
+ VALUE hash;
+ const struct st_hash_type *orighash;
+};
+
+static VALUE
+reset_hash_type(VALUE arg)
+{
+ struct reset_hash_type_arg *p = (struct reset_hash_type_arg *)arg;
+ RHASH(p->hash)->ntbl->type = p->orighash;
+ return Qundef;
+}
+
+static int
assoc_i(VALUE key, VALUE val, VALUE arg)
{
VALUE *args = (VALUE *)arg;
@@ -2149,11 +2175,33 @@ assoc_i(VALUE key, VALUE val, VALUE arg)
*/
VALUE
-rb_hash_assoc(VALUE hash, VALUE obj)
+rb_hash_assoc(VALUE hash, VALUE key)
{
+ st_table *table;
+ const struct st_hash_type *orighash;
VALUE args[2];
- args[0] = obj;
+ if (RHASH_EMPTY_P(hash)) return Qnil;
+ table = RHASH(hash)->ntbl;
+ orighash = table->type;
+
+ if (orighash != &identhash) {
+ VALUE value;
+ struct reset_hash_type_arg ensure_arg;
+ struct st_hash_type assochash;
+
+ assochash.compare = assoc_cmp;
+ assochash.hash = orighash->hash;
+ table->type = &assochash;
+ args[0] = hash;
+ args[1] = key;
+ ensure_arg.hash = hash;
+ ensure_arg.orighash = orighash;
+ value = rb_ensure(lookup2_call, (VALUE)&args, reset_hash_type, (VALUE)&ensure_arg);
+ if (value != Qundef) return rb_assoc_new(key, value);
+ }
+
+ args[0] = key;
args[1] = Qnil;
rb_hash_foreach(hash, assoc_i, (VALUE)args);
return args[1];
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index 5694d2f01e..82eba0b352 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -879,6 +879,14 @@ class TestHash < Test::Unit::TestCase
assert_equal([1.0,1], {1.0=>1}.assoc(1))
end
+ def test_assoc_compare_by_identity
+ h = {}
+ h.compare_by_identity
+ h["a"] = 1
+ h["a"] = 2
+ assert_equal(["a",1], h.assoc("a"))
+ end
+
def test_rassoc
assert_equal([3,4], {1=>2, 3=>4, 5=>6}.rassoc(4))
assert_nil({1=>2, 3=>4, 5=>6}.rassoc(3))