Feature #6219 ยป 0001-Hash-store-return-old-value.patch
hash.c | ||
---|---|---|
}
|
||
static void
|
||
hash_update_fail(void)
|
||
{
|
||
rb_raise(rb_eRuntimeError, "can't add a new key into hash during iteration");
|
||
}
|
||
static void
|
||
hash_update(VALUE hash, VALUE key)
|
||
{
|
||
if (RHASH(hash)->iter_lev > 0 && !st_lookup(RHASH(hash)->ntbl, key, 0)) {
|
||
rb_raise(rb_eRuntimeError, "can't add a new key into hash during iteration");
|
||
hash_update_fail();
|
||
}
|
||
}
|
||
... | ... | |
return hash;
|
||
}
|
||
static VALUE
|
||
hash_default_value(VALUE hash, VALUE key)
|
||
{
|
||
if (!FL_TEST(hash, HASH_PROC_DEFAULT) &&
|
||
rb_method_basic_definition_p(CLASS_OF(hash), id_default)) {
|
||
return RHASH_IFNONE(hash);
|
||
}
|
||
else {
|
||
return rb_funcall(hash, id_default, 1, key);
|
||
}
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* hsh[key] -> value
|
||
... | ... | |
st_data_t val;
|
||
if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
|
||
if (!FL_TEST(hash, HASH_PROC_DEFAULT) &&
|
||
rb_method_basic_definition_p(CLASS_OF(hash), id_default)) {
|
||
return RHASH_IFNONE(hash);
|
||
}
|
||
else {
|
||
return rb_funcall(hash, id_default, 1, key);
|
||
}
|
||
return hash_default_value(hash, key);
|
||
}
|
||
return (VALUE)val;
|
||
}
|
||
... | ... | |
return hash;
|
||
}
|
||
static st_data_t
|
||
copy_str_key(st_data_t str)
|
||
struct hash_aset_tuple {
|
||
st_data_t old;
|
||
st_data_t new;
|
||
int iter_lev;
|
||
};
|
||
static int
|
||
hash_aset_i(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
|
||
{
|
||
struct hash_aset_tuple *ptr = (void *)arg;
|
||
if (existing) {
|
||
ptr->old = *value;
|
||
}
|
||
else {
|
||
if (ptr->iter_lev > 0) hash_update_fail();
|
||
}
|
||
*value = ptr->new;
|
||
return ST_CONTINUE;
|
||
}
|
||
static int
|
||
hash_aset_copy_i(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
|
||
{
|
||
struct hash_aset_tuple *ptr = (void *)arg;
|
||
if (existing) {
|
||
ptr->old = *value;
|
||
}
|
||
else {
|
||
if (ptr->iter_lev > 0) hash_update_fail();
|
||
*key = (st_data_t)rb_str_new4((VALUE)*key);
|
||
}
|
||
*value = ptr->new;
|
||
return ST_CONTINUE;
|
||
}
|
||
static VALUE
|
||
hash_store(VALUE hash, VALUE key, VALUE val)
|
||
{
|
||
return (st_data_t)rb_str_new4((VALUE)str);
|
||
st_update_callback_func *func;
|
||
struct hash_aset_tuple t;
|
||
rb_hash_modify(hash);
|
||
t.new = val;
|
||
t.old = Qundef;
|
||
t.iter_lev = RHASH(hash)->iter_lev;
|
||
if (RHASH(hash)->ntbl->type == &identhash || rb_obj_class(key) != rb_cString) {
|
||
func = hash_aset_i;
|
||
}
|
||
else {
|
||
func = hash_aset_copy_i;
|
||
}
|
||
st_update(RHASH(hash)->ntbl, key, func, (st_data_t)&t);
|
||
return t.old;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* hsh[key] = value -> value
|
||
* hsh.store(key, value) -> value
|
||
*
|
||
* Element Assignment---Associates the value given by
|
||
* <i>value</i> with the key given by <i>key</i>.
|
||
... | ... | |
VALUE
|
||
rb_hash_aset(VALUE hash, VALUE key, VALUE val)
|
||
{
|
||
rb_hash_modify(hash);
|
||
hash_update(hash, key);
|
||
if (RHASH(hash)->ntbl->type == &identhash || rb_obj_class(key) != rb_cString) {
|
||
st_insert(RHASH(hash)->ntbl, key, val);
|
||
}
|
||
else {
|
||
st_insert2(RHASH(hash)->ntbl, key, val, copy_str_key);
|
||
}
|
||
hash_store(hash, key, val);
|
||
return val;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* hsh.store(key, value) -> old_value
|
||
*
|
||
* Associates the value given by <i>value</i> with the key given by
|
||
* <i>key</i>. Almostly equivalent to Hash#[]=, but returns old
|
||
* value which was associated with <i>key</i>.
|
||
*
|
||
* h = { "a" => 100, "b" => 200 }
|
||
* h.store("a", 9) #=> 100
|
||
* h.store("c", 4) #=> nil
|
||
* h #=> {"a"=>9, "b"=>200, "c"=>4}
|
||
*
|
||
*/
|
||
VALUE
|
||
rb_hash_store(VALUE hash, VALUE key, VALUE val)
|
||
{
|
||
val = hash_store(hash, key, val);
|
||
if (val == Qundef) val = hash_default_value(hash, key);
|
||
return val;
|
||
}
|
||
... | ... | |
rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
|
||
{
|
||
if (key == Qundef) return ST_CONTINUE;
|
||
hash_update(hash, key);
|
||
st_insert(RHASH(hash)->ntbl, key, value);
|
||
return ST_CONTINUE;
|
||
}
|
||
... | ... | |
rb_define_method(rb_cHash,"eql?", rb_hash_eql, 1);
|
||
rb_define_method(rb_cHash,"fetch", rb_hash_fetch_m, -1);
|
||
rb_define_method(rb_cHash,"[]=", rb_hash_aset, 2);
|
||
rb_define_method(rb_cHash,"store", rb_hash_aset, 2);
|
||
rb_define_method(rb_cHash,"store", rb_hash_store, 2);
|
||
rb_define_method(rb_cHash,"default", rb_hash_default, -1);
|
||
rb_define_method(rb_cHash,"default=", rb_hash_set_default, 1);
|
||
rb_define_method(rb_cHash,"default_proc", rb_hash_default_proc, 0);
|
test/ruby/test_hash.rb | ||
---|---|---|
def test_store
|
||
t = Time.now
|
||
h = @cls.new
|
||
h.store(1, 'one')
|
||
h.store(2, 'two')
|
||
h.store(3, 'three')
|
||
h.store(self, 'self')
|
||
h.store(t, 'time')
|
||
h.store(nil, 'nil')
|
||
h.store('nil', nil)
|
||
assert_nil(h.store(1, 'one'))
|
||
assert_nil(h.store(2, 'two'))
|
||
assert_nil(h.store(3, 'three'))
|
||
assert_nil(h.store(self, 'self'))
|
||
assert_nil(h.store(t, 'time'))
|
||
assert_nil(h.store(nil, 'nil'))
|
||
assert_nil(h.store('nil', nil))
|
||
assert_equal('one', h[1])
|
||
assert_equal('two', h[2])
|
||
assert_equal('three', h[3])
|
||
... | ... | |
assert_equal(nil, h['nil'])
|
||
assert_equal(nil, h['koala'])
|
||
h.store(1, 1)
|
||
h.store(nil, 99)
|
||
h.store('nil', nil)
|
||
assert_equal('one', h.store(1, 1))
|
||
assert_equal('nil', h.store(nil, 99))
|
||
assert_equal(nil, h.store('nil', nil))
|
||
assert_equal(1, h[1])
|
||
assert_equal('two', h[2])
|
||
assert_equal('three', h[3])
|
||
... | ... | |
assert_equal(99, h[nil])
|
||
assert_equal(nil, h['nil'])
|
||
assert_equal(nil, h['koala'])
|
||
h.default = 42
|
||
assert_equal(42, h.store(99, 'wombat'))
|
||
assert_equal('wombat', h[99])
|
||
key = nil
|
||
h.default_proc = proc {|_, k| key = k; 'foo'}
|
||
assert_equal('foo', h.store(23, 'bar'))
|
||
assert_equal('bar', h[23])
|
||
assert_equal(23, key)
|
||
end
|
||
def test_to_a
|