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) | ||