Feature #8707 ยป patch.diff
| hash.c | ||
|---|---|---|
|
return Qnil;
|
||
|
}
|
||
|
void
|
||
|
rb_hash_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg)
|
||
|
static VALUE
|
||
|
hash_reverse_foreach_call(VALUE arg)
|
||
|
{
|
||
|
VALUE hash = ((struct hash_foreach_arg *)arg)->hash;
|
||
|
if (st_reverse_foreach_check(RHASH(hash)->ntbl, hash_foreach_iter, (st_data_t)arg, (st_data_t)Qundef)) {
|
||
|
rb_raise(rb_eRuntimeError, "hash modified during iteration");
|
||
|
}
|
||
|
return Qnil;
|
||
|
}
|
||
|
static void
|
||
|
do_hash_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg, int reverse)
|
||
|
{
|
||
|
struct hash_foreach_arg arg;
|
||
| ... | ... | |
|
arg.hash = hash;
|
||
|
arg.func = (rb_foreach_func *)func;
|
||
|
arg.arg = farg;
|
||
|
rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash);
|
||
|
if (reverse)
|
||
|
rb_ensure(hash_reverse_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash);
|
||
|
else
|
||
|
rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash);
|
||
|
}
|
||
|
void
|
||
|
rb_hash_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg)
|
||
|
{
|
||
|
do_hash_foreach(hash, func, farg, 0);
|
||
|
}
|
||
|
static void
|
||
|
rb_hash_reverse_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg)
|
||
|
{
|
||
|
do_hash_foreach(hash, func, farg, 1);
|
||
|
}
|
||
|
static VALUE
|
||
| ... | ... | |
|
return hash;
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* hsh.reverse_each {| key, value | block } -> hsh
|
||
|
* hsh.reverse_each -> an_enumerator
|
||
|
*
|
||
|
* Calls <i>block</i> once for each key in <i>hsh</i>, passing the key-value
|
||
|
* pair as parameters.
|
||
|
*
|
||
|
* If no block is given, an enumerator is returned instead.
|
||
|
*
|
||
|
* h = { "a" => 100, "b" => 200 }
|
||
|
* h.reverse_each {|key, value| puts "#{key} is #{value}" }
|
||
|
*
|
||
|
* <em>produces:</em>
|
||
|
*
|
||
|
* b is 200
|
||
|
* a is 100
|
||
|
*
|
||
|
*/
|
||
|
static VALUE
|
||
|
rb_hash_reverse_each_pair(VALUE hash)
|
||
|
{
|
||
|
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
|
||
|
if (rb_block_arity() > 1)
|
||
|
rb_hash_reverse_foreach(hash, each_pair_i_fast, 0);
|
||
|
else
|
||
|
rb_hash_reverse_foreach(hash, each_pair_i, 0);
|
||
|
return hash;
|
||
|
}
|
||
|
static int
|
||
|
to_a_i(VALUE key, VALUE value, VALUE ary)
|
||
|
{
|
||
| ... | ... | |
|
rb_define_method(rb_cHash,"each_key", rb_hash_each_key, 0);
|
||
|
rb_define_method(rb_cHash,"each_pair", rb_hash_each_pair, 0);
|
||
|
rb_define_method(rb_cHash,"each", rb_hash_each_pair, 0);
|
||
|
rb_define_method(rb_cHash,"reverse_each", rb_hash_reverse_each_pair, 0);
|
||
|
rb_define_method(rb_cHash,"keys", rb_hash_keys, 0);
|
||
|
rb_define_method(rb_cHash,"values", rb_hash_values, 0);
|
||
| include/ruby/st.h | ||
|---|---|---|
|
int st_update(st_table *table, st_data_t key, st_update_callback_func *func, 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);
|
||
|
int st_reverse_foreach_check(st_table *, int (*)(ANYARGS), st_data_t, st_data_t);
|
||
|
void st_add_direct(st_table *, st_data_t, st_data_t);
|
||
|
void st_free_table(st_table *);
|
||
|
void st_cleanup_safe(st_table *, st_data_t);
|
||
| st.c | ||
|---|---|---|
|
return 0;
|
||
|
}
|
||
|
#if 0 /* unused right now */
|
||
|
int
|
||
|
st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
|
||
|
st_reverse_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;
|
||
|
int i;
|
||
|
if (table->entries_packed) {
|
||
|
for (i = table->num_entries-1; 0 <= i; i--) {
|
||
|
int j;
|
||
|
st_data_t key, val;
|
||
|
key = PKEY(table, i);
|
||
|
val = PVAL(table, i);
|
||
|
retval = (*func)(key, val, arg);
|
||
|
switch (retval) {
|
||
|
for (i = table->num_entries-1; i >= 0; i--) {
|
||
|
st_data_t key, val;
|
||
|
st_index_t hash;
|
||
|
key = PKEY(table, i);
|
||
|
val = PVAL(table, i);
|
||
|
hash = PHASH(table, i);
|
||
|
if (key == never) continue;
|
||
|
retval = (*func)(key, val, arg);
|
||
|
if (!table->entries_packed) {
|
||
|
FIND_ENTRY(table, ptr, hash, i);
|
||
|
if (retval == ST_CHECK) {
|
||
|
if (!ptr) goto deleted;
|
||
|
goto unpacked_continue;
|
||
|
}
|
||
|
goto unpacked;
|
||
|
}
|
||
|
switch (retval) {
|
||
|
case ST_CHECK: /* check if hash is modified during iteration */
|
||
|
for (j = 0; j < table->num_entries; j++) {
|
||
|
if (PKEY(table, j) == key)
|
||
|
break;
|
||
|
}
|
||
|
if (j == table->num_entries) {
|
||
|
/* call func with error notice */
|
||
|
retval = (*func)(0, 0, arg, 1);
|
||
|
return 1;
|
||
|
}
|
||
|
if (PHASH(table, i) == 0 && PKEY(table, i) == never) {
|
||
|
break;
|
||
|
}
|
||
|
i = find_packed_index(table, hash, key);
|
||
|
if (i == table->real_entries) {
|
||
|
goto deleted;
|
||
|
}
|
||
|
/* fall through */
|
||
|
case ST_CONTINUE:
|
||
|
break;
|
||
|
case ST_STOP:
|
||
|
return 0;
|
||
|
case ST_DELETE:
|
||
|
remove_packed_entry(table, i);
|
||
|
break;
|
||
|
}
|
||
|
remove_safe_packed_entry(table, i, never);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
else {
|
||
|
ptr = table->tail;
|
||
|
}
|
||
|
if ((ptr = table->head) != 0) {
|
||
|
ptr = ptr->back;
|
||
|
if (ptr != 0) {
|
||
|
do {
|
||
|
retval = (*func)(ptr->key, ptr->record, arg, 0);
|
||
|
if (ptr->key == never)
|
||
|
goto unpacked_continue;
|
||
|
i = ptr->hash % table->num_bins;
|
||
|
retval = (*func)(ptr->key, ptr->record, arg);
|
||
|
unpacked:
|
||
|
switch (retval) {
|
||
|
case ST_CHECK: /* check if hash is modified during iteration */
|
||
|
i = ptr->hash % table->num_bins;
|
||
|
for (tmp = table->bins[i]; tmp != ptr; tmp = tmp->next) {
|
||
|
if (!tmp) {
|
||
|
deleted:
|
||
|
/* call func with error notice */
|
||
|
retval = (*func)(0, 0, arg, 1);
|
||
|
return 1;
|
||
| ... | ... | |
|
}
|
||
|
/* fall through */
|
||
|
case ST_CONTINUE:
|
||
|
unpacked_continue:
|
||
|
ptr = ptr->back;
|
||
|
break;
|
||
|
case ST_STOP:
|
||
| ... | ... | |
|
for (; (tmp = *last) != 0; last = &tmp->next) {
|
||
|
if (ptr == tmp) {
|
||
|
tmp = ptr->back;
|
||
|
*last = ptr->next;
|
||
|
remove_entry(table, ptr);
|
||
|
st_free_entry(ptr);
|
||
|
ptr->key = ptr->record = never;
|
||
|
ptr->hash = 0;
|
||
|
ptr = tmp;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
ptr = ptr->next;
|
||
|
free(tmp);
|
||
|
table->num_entries--;
|
||
|
}
|
||
|
} while (ptr && table->head);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
/*
|
||
|
* hash_32 - 32 bit Fowler/Noll/Vo FNV-1a hash code
|
||
| test/ruby/test_hash.rb | ||
|---|---|---|
|
assert_equal([], res - expected)
|
||
|
end
|
||
|
def test_reverse_each
|
||
|
count = 0
|
||
|
@cls[].reverse_each { |k, v| count + 1 }
|
||
|
assert_equal(0, count)
|
||
|
h = @h
|
||
|
h.reverse_each do |k, v|
|
||
|
assert_equal(v, h.delete(k))
|
||
|
end
|
||
|
assert_equal(@cls[], h)
|
||
|
h = @cls[]
|
||
|
h[1] = 1
|
||
|
h[2] = 2
|
||
|
assert_equal([[2,2],[1,1]], h.reverse_each.to_a)
|
||
|
end
|
||
|
def test_empty?
|
||
|
assert_empty(@cls[])
|
||
|
assert_not_empty(@h)
|
||