From: Nobuyoshi Nakada Date: 2007-06-29T13:55:08+09:00 Subject: Re: [BUG] delete_if with destructive methods in block Hi, At Fri, 29 Jun 2007 11:10:25 +0900, Florian Gross wrote in [ruby-core:11556]: > It outputs the following: > > [5, 6] > {1=>2, 3=>4} > > [5] > {3=>4} The second is outputed from the block called by Hash#delete used internally. It seems wrong indeed. And rb_hash_shift() should consider iter_lev too. Index: hash.c =================================================================== --- hash.c (revision 12654) +++ hash.c (working copy) @@ -591,4 +591,21 @@ index_i(key, value, args) } +static VALUE +rb_hash_delete_key(hash, key) + VALUE hash, 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: @@ -670,12 +687,6 @@ rb_hash_delete(hash, key) 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); @@ -685,5 +696,4 @@ rb_hash_delete(hash, key) struct shift_var { - int stop; VALUE key; VALUE val; @@ -696,6 +706,5 @@ shift_i(key, value, 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; @@ -703,4 +712,15 @@ shift_i(key, value, var) } +static int +shift_i_safe(key, value, var) + VALUE key, value; + struct shift_var *var; +{ + if (key == Qundef) return ST_CONTINUE; + var->key = key; + var->val = value; + return ST_STOP; +} + /* * call-seq: @@ -723,8 +743,19 @@ rb_hash_shift(hash) rb_hash_modify(hash); - var.stop = 0; - rb_hash_foreach(hash, shift_i, (st_data_t)&var); + var.key = Qundef; + if (RHASH(hash)->iter_lev > 0) { + rb_hash_foreach(hash, shift_i_safe, (st_data_t)&var); + if (var.key != Qundef) { + st_data_t key = var.key; + if (st_delete_safe(RHASH(hash)->tbl, &key, 0, Qundef)) { + FL_SET(hash, HASH_DELETED); + } + } + } + else { + rb_hash_foreach(hash, shift_i, (st_data_t)&var); + } - if (var.stop) { + if (var.key != Qundef) { return rb_assoc_new(var.key, var.val); } @@ -743,5 +774,5 @@ delete_if_i(key, 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; -- Nobu Nakada