Project

General

Profile

Feature #6219 ยป 0001-Hash-store-return-old-value.patch

nobu (Nobuyoshi Nakada), 03/30/2012 01:09 AM

View differences:

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
    (1-1/1)