mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* st.c: add st_foreach_check for fixing iteration over packed table
and st_delete_safe. patched by Sokolov Yura at https://github.com/ruby/ruby/pull/84 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@34963 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
efae619458
commit
a73d958c33
7 changed files with 112 additions and 23 deletions
|
@ -1,4 +1,8 @@
|
|||
Sat Mar 10 23:52:03 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
Sat Mar 10 23:52:16 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* st.c: add st_foreach_check for fixing iteration over packed table
|
||||
and st_delete_safe. patched by Sokolov Yura at
|
||||
https://github.com/ruby/ruby/pull/84
|
||||
|
||||
* st.c: fix packed num_entries on delete_safe. patched by Sokolov
|
||||
Yura at https://github.com/ruby/ruby/pull/84
|
||||
|
|
|
@ -54,7 +54,9 @@ numhash_i(st_data_t key, st_data_t value, st_data_t arg, int error)
|
|||
static VALUE
|
||||
numhash_each(VALUE self)
|
||||
{
|
||||
return st_foreach((st_table *)DATA_PTR(self), numhash_i, self) ? Qtrue : Qfalse;
|
||||
st_table *table = DATA_PTR(self);
|
||||
st_data_t data = (st_data_t)self;
|
||||
return st_foreach_check(table, numhash_i, data, data) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -266,7 +266,6 @@ to_strkey(key, value, hash)
|
|||
VALUE value;
|
||||
VALUE hash;
|
||||
{
|
||||
if (key == Qundef) return ST_CONTINUE;
|
||||
rb_hash_aset(hash, rb_funcall(key, ID_to_s, 0, 0), value);
|
||||
return ST_CHECK;
|
||||
}
|
||||
|
@ -280,7 +279,7 @@ tk_symbolkey2str(self, keys)
|
|||
|
||||
if NIL_P(keys) return new_keys;
|
||||
keys = rb_convert_type(keys, T_HASH, "Hash", "to_hash");
|
||||
st_foreach(RHASH_TBL(keys), to_strkey, new_keys);
|
||||
st_foreach_check(RHASH_TBL(keys), to_strkey, new_keys, Qundef);
|
||||
return new_keys;
|
||||
}
|
||||
|
||||
|
@ -653,7 +652,6 @@ push_kv(key, val, args)
|
|||
|
||||
ary = RARRAY_PTR(args)[0];
|
||||
|
||||
if (key == Qundef) return ST_CONTINUE;
|
||||
#if 0
|
||||
rb_ary_push(ary, key2keyname(key));
|
||||
if (val != TK_None) rb_ary_push(ary, val);
|
||||
|
@ -676,7 +674,7 @@ hash2kv(hash, ary, self)
|
|||
volatile VALUE dst = rb_ary_new2(2 * RHASH_SIZE(hash));
|
||||
volatile VALUE args = rb_ary_new3(2, dst, self);
|
||||
|
||||
st_foreach(RHASH_TBL(hash), push_kv, args);
|
||||
st_foreach_check(RHASH_TBL(hash), push_kv, args, Qundef);
|
||||
|
||||
if (NIL_P(ary)) {
|
||||
return dst;
|
||||
|
@ -695,7 +693,6 @@ push_kv_enc(key, val, args)
|
|||
|
||||
ary = RARRAY_PTR(args)[0];
|
||||
|
||||
if (key == Qundef) return ST_CONTINUE;
|
||||
#if 0
|
||||
rb_ary_push(ary, key2keyname(key));
|
||||
if (val != TK_None) {
|
||||
|
@ -721,7 +718,7 @@ hash2kv_enc(hash, ary, self)
|
|||
volatile VALUE dst = rb_ary_new2(2 * RHASH_SIZE(hash));
|
||||
volatile VALUE args = rb_ary_new3(2, dst, self);
|
||||
|
||||
st_foreach(RHASH_TBL(hash), push_kv_enc, args);
|
||||
st_foreach_check(RHASH_TBL(hash), push_kv_enc, args, Qundef);
|
||||
|
||||
if (NIL_P(ary)) {
|
||||
return dst;
|
||||
|
|
5
hash.c
5
hash.c
|
@ -116,7 +116,6 @@ foreach_safe_i(st_data_t key, st_data_t value, struct foreach_safe_arg *arg)
|
|||
{
|
||||
int status;
|
||||
|
||||
if (key == Qundef) return ST_CONTINUE;
|
||||
status = (*arg->func)(key, value, arg->arg);
|
||||
if (status == ST_CONTINUE) {
|
||||
return ST_CHECK;
|
||||
|
@ -132,7 +131,7 @@ st_foreach_safe(st_table *table, int (*func)(ANYARGS), st_data_t a)
|
|||
arg.tbl = table;
|
||||
arg.func = (st_foreach_func *)func;
|
||||
arg.arg = a;
|
||||
if (st_foreach(table, foreach_safe_i, (st_data_t)&arg)) {
|
||||
if (st_foreach_check(table, foreach_safe_i, (st_data_t)&arg, Qundef)) {
|
||||
rb_raise(rb_eRuntimeError, "hash modified during iteration");
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +186,7 @@ hash_foreach_ensure(VALUE hash)
|
|||
static VALUE
|
||||
hash_foreach_call(struct hash_foreach_arg *arg)
|
||||
{
|
||||
if (st_foreach(RHASH(arg->hash)->ntbl, hash_foreach_iter, (st_data_t)arg)) {
|
||||
if (st_foreach_check(RHASH(arg->hash)->ntbl, hash_foreach_iter, (st_data_t)arg, Qundef)) {
|
||||
rb_raise(rb_eRuntimeError, "hash modified during iteration");
|
||||
}
|
||||
return Qnil;
|
||||
|
|
|
@ -123,6 +123,7 @@ int st_lookup(st_table *, st_data_t, st_data_t *);
|
|||
int st_get_key(st_table *, st_data_t, st_data_t *);
|
||||
int st_update(st_table *table, st_data_t key, int (*func)(st_data_t key, st_data_t *value, st_data_t arg), st_data_t arg);
|
||||
int st_foreach(st_table *, int (*)(ANYARGS), st_data_t);
|
||||
int st_foreach_check(st_table *, int (*)(ANYARGS), st_data_t, st_data_t);
|
||||
int st_reverse_foreach(st_table *, int (*)(ANYARGS), st_data_t);
|
||||
void st_add_direct(st_table *, st_data_t, st_data_t);
|
||||
void st_free_table(st_table *);
|
||||
|
|
97
st.c
97
st.c
|
@ -866,18 +866,19 @@ st_update(st_table *table, st_data_t key, int (*func)(st_data_t key, st_data_t *
|
|||
}
|
||||
|
||||
int
|
||||
st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
||||
st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t never)
|
||||
{
|
||||
st_table_entry *ptr, **last, *tmp;
|
||||
enum st_retval retval;
|
||||
st_index_t i;
|
||||
|
||||
if (table->entries_packed) {
|
||||
for (i = 0; i < table->real_entries; i++) {
|
||||
st_data_t key, val;
|
||||
key = PKEY(table, i);
|
||||
val = PVAL(table, i);
|
||||
retval = (*func)(key, val, arg);
|
||||
for (i = 0; i < table->real_entries; i++) {
|
||||
st_data_t key, val;
|
||||
key = PKEY(table, i);
|
||||
val = PVAL(table, i);
|
||||
if (key == never) continue;
|
||||
retval = (*func)(key, val, arg);
|
||||
if (!table->entries_packed) {
|
||||
FIND_ENTRY(table, ptr, key, i);
|
||||
if (retval == ST_CHECK) {
|
||||
|
@ -886,8 +887,11 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
|||
}
|
||||
goto unpacked;
|
||||
}
|
||||
switch (retval) {
|
||||
switch (retval) {
|
||||
case ST_CHECK: /* check if hash is modified during iteration */
|
||||
if (PKEY(table, i) == never) {
|
||||
break;
|
||||
}
|
||||
if (i != find_packed_index(table, key)) {
|
||||
goto deleted;
|
||||
}
|
||||
|
@ -898,11 +902,11 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
|||
return 0;
|
||||
case ST_DELETE:
|
||||
remove_packed_entry(table, i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
ptr = table->head;
|
||||
|
@ -910,6 +914,8 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
|||
|
||||
if (ptr != 0) {
|
||||
do {
|
||||
if (ptr->key == never)
|
||||
goto unpacked_continue;
|
||||
i = ptr->hash % table->num_bins;
|
||||
retval = (*func)(ptr->key, ptr->record, arg);
|
||||
unpacked:
|
||||
|
@ -949,6 +955,73 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
||||
{
|
||||
st_table_entry *ptr, **last, *tmp;
|
||||
enum st_retval retval;
|
||||
st_index_t i;
|
||||
|
||||
if (table->entries_packed) {
|
||||
for (i = 0; i < table->real_entries; i++) {
|
||||
st_data_t key, val;
|
||||
key = PKEY(table, i);
|
||||
val = PVAL(table, i);
|
||||
retval = (*func)(key, val, arg);
|
||||
if (!table->entries_packed) {
|
||||
FIND_ENTRY(table, ptr, key, i);
|
||||
if (!ptr) return 0;
|
||||
goto unpacked;
|
||||
}
|
||||
switch (retval) {
|
||||
case ST_CONTINUE:
|
||||
break;
|
||||
case ST_CHECK:
|
||||
case ST_STOP:
|
||||
return 0;
|
||||
case ST_DELETE:
|
||||
remove_packed_entry(table, i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
ptr = table->head;
|
||||
}
|
||||
|
||||
if (ptr != 0) {
|
||||
do {
|
||||
i = ptr->hash % table->num_bins;
|
||||
retval = (*func)(ptr->key, ptr->record, arg);
|
||||
unpacked:
|
||||
switch (retval) {
|
||||
case ST_CONTINUE:
|
||||
ptr = ptr->fore;
|
||||
break;
|
||||
case ST_CHECK:
|
||||
case ST_STOP:
|
||||
return 0;
|
||||
case ST_DELETE:
|
||||
last = &table->bins[ptr->hash % table->num_bins];
|
||||
for (; (tmp = *last) != 0; last = &tmp->next) {
|
||||
if (ptr == tmp) {
|
||||
tmp = ptr->fore;
|
||||
*last = ptr->next;
|
||||
remove_entry(table, ptr);
|
||||
st_free_entry(ptr);
|
||||
if (ptr == tmp) return 0;
|
||||
ptr = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (ptr && table->head);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* unused right now */
|
||||
int
|
||||
st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
||||
|
|
|
@ -32,5 +32,18 @@ class Bug::StNumHash
|
|||
assert_equal(up - 1, tbl.size, "delete_safe doesn't change size from #{up} to #{up-1}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_delete_safe_on_iteration
|
||||
10.downto(1) do |up|
|
||||
tbl = Bug::StNumHash.new
|
||||
1.upto(up){|i| tbl[i] = i}
|
||||
assert_nothing_raised("delete_safe forces iteration to fail with size #{up}") do
|
||||
tbl.each do |k, v, t|
|
||||
assert_equal k, t.delete_safe(k)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue