diff --git a/ChangeLog b/ChangeLog index a67f5c298a..6bf1feb964 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Wed Aug 15 13:50:10 2007 Nobuyoshi Nakada + + * hash.c (rb_hash_delete_key): delete the entry without calling block. + + * hash.c (rb_hash_shift): should consider iter_lev too. + + * hash.c (delete_if_i): use rb_hash_delete_key() so that the block + isn't called twice. [ruby-core:11556] + Wed Aug 15 13:39:25 2007 Koichi Sasada * process.c (proc_geteuid): fix strange cast. [ruby-dev:31417] diff --git a/hash.c b/hash.c index 194e4e538a..20bc33b878 100644 --- a/hash.c +++ b/hash.c @@ -594,6 +594,22 @@ rb_hash_index(VALUE hash, VALUE value) return rb_hash_key(hash, value); } +static VALUE +rb_hash_delete_key(VALUE hash, VALUE key) +{ + st_data_t ktmp = (st_data_t)key, val; + + if (RHASH(hash)->iter_lev > 0) { + if (st_delete_safe(RHASH(hash)->tbl, &ktmp, &val, Qundef)) { + FL_SET(hash, HASH_DELETED); + return (VALUE)val; + } + } + else if (st_delete(RHASH(hash)->tbl, &ktmp, &val)) + return (VALUE)val; + return Qundef; +} + /* * call-seq: * hsh.delete(key) => value @@ -618,14 +634,8 @@ rb_hash_delete(VALUE hash, VALUE key) VALUE val; rb_hash_modify(hash); - if (RHASH(hash)->iter_lev > 0) { - if (st_delete_safe(RHASH(hash)->tbl, (st_data_t*)&key, &val, Qundef)) { - FL_SET(hash, HASH_DELETED); - return val; - } - } - else if (st_delete(RHASH(hash)->tbl, (st_data_t*)&key, &val)) - return val; + val = rb_hash_delete_key(hash, key); + if (val != Qundef) return val; if (rb_block_given_p()) { return rb_yield(key); } @@ -633,7 +643,6 @@ rb_hash_delete(VALUE hash, VALUE key) } struct shift_var { - int stop; VALUE key; VALUE val; }; @@ -642,13 +651,21 @@ static int shift_i(VALUE key, VALUE value, struct shift_var *var) { if (key == Qundef) return ST_CONTINUE; - if (var->stop) return ST_STOP; - var->stop = 1; + if (var->key != Qundef) return ST_STOP; var->key = key; var->val = value; return ST_DELETE; } +static int +shift_i_safe(VALUE key, VALUE value, struct shift_var *var) +{ + if (key == Qundef) return ST_CONTINUE; + var->key = key; + var->val = value; + return ST_STOP; +} + /* * call-seq: * hsh.shift -> anArray or obj @@ -668,10 +685,14 @@ rb_hash_shift(VALUE hash) struct shift_var var; rb_hash_modify(hash); - var.stop = 0; - rb_hash_foreach(hash, shift_i, (st_data_t)&var); + var.key = Qundef; + rb_hash_foreach(hash, RHASH(hash)->iter_lev > 0 ? shift_i_safe : shift_i, + (st_data_t)&var); - if (var.stop) { + if (var.key != Qundef) { + if (RHASH(hash)->iter_lev > 0) { + rb_hash_delete_key(hash, var.key); + } return rb_assoc_new(var.key, var.val); } else if (FL_TEST(hash, HASH_PROC_DEFAULT)) { @@ -687,7 +708,7 @@ delete_if_i(VALUE key, VALUE value, VALUE hash) { if (key == Qundef) return ST_CONTINUE; if (RTEST(rb_yield_values(2, key, value))) { - rb_hash_delete(hash, key); + rb_hash_delete_key(hash, key); } return ST_CONTINUE; } @@ -1618,7 +1639,7 @@ rb_hash_rassoc(VALUE hash, VALUE obj) static VALUE rb_hash_flatten(int argc, VALUE *argv, VALUE hash) { - VALUE ary, lv; + VALUE ary; ary = rb_hash_to_a(hash); rb_funcall2(ary, rb_intern("flatten!"), argc, argv); diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index b23552aeae..440736765c 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -266,6 +266,17 @@ class TestHash < Test::Unit::TestCase h = base.dup assert_equal(h3, h.delete_if {|k,v| v }) assert_equal(h3, h) + + h = base.dup + n = 0 + h.delete_if {|*a| + n += 1 + assert_equal(2, a.size) + assert_equal(base[a[0]], a[1]) + h.shift + true + } + assert_equal(base.size, n) end def test_dup