diff options
author | Koichi Sasada <ko1@cookpad.com> | 2019-01-16 10:48:30 +0000 |
---|---|---|
committer | Koichi Sasada <ko1@atdot.net> | 2019-07-31 09:44:23 +0900 |
commit | ebd398ac5a4147a1e652d6943c39a29a62f12e66 (patch) | |
tree | 79dba6622c591db987f0a9c065fe37cd09a58e98 /hash.c | |
parent | 4afd8975242917d319cfb20c7ed635b979ad48d5 (diff) | |
download | ruby-ebd398ac5a4147a1e652d6943c39a29a62f12e66.tar.gz |
remove RHash::iter_lev.
iter_lev is used to detect the hash is iterating or not.
Usually, iter_lev should be very small number (1 or 2) so
`int` is overkill.
This patch introduce iter_lev in flags (7 bits, FL13 to FL19)
and if iter_lev exceeds this range, save it in hidden attribute.
We can get 1 word in RHash.
We can't modify frozen objects. Therefore I added new internal API
`rb_ivar_set_internal()` which allows us to set an attribute
even if the target object is frozen
if the name is hidden ivar (the name without `@` prefix).
Diffstat (limited to 'hash.c')
-rw-r--r-- | hash.c | 60 |
1 files changed, 58 insertions, 2 deletions
@@ -1252,16 +1252,72 @@ hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error) return ST_CHECK; } +static int +iter_lev_in_ivar(VALUE hash) +{ + VALUE levval = rb_ivar_get(hash, rb_intern("hash_iter_lev")); + HASH_ASSERT(FIXNUM_P(levval)); + return FIX2INT(levval); +} + +void rb_ivar_set_internal(VALUE obj, ID id, VALUE val); + +static void +iter_lev_in_ivar_set(VALUE hash, int lev) +{ + rb_ivar_set_internal(hash, rb_intern("hash_iter_lev"), INT2FIX(lev)); +} + +static int +iter_lev_in_flags(VALUE hash) +{ + unsigned int u = (unsigned int)(RBASIC(hash)->flags & (unsigned int)RHASH_LEV_MASK) >> RHASH_LEV_SHIFT; + return (int)u; +} + +static int +RHASH_ITER_LEV(VALUE hash) +{ + int lev = iter_lev_in_flags(hash); + + if (lev == RHASH_LEV_MAX) { + return iter_lev_in_ivar(hash); + } + else { + return lev; + } +} + static void hash_iter_lev_inc(VALUE hash) { - *((int *)&RHASH(hash)->iter_lev) = RHASH_ITER_LEV(hash) + 1; + int lev = iter_lev_in_flags(hash); + if (lev == RHASH_LEV_MAX) { + lev = iter_lev_in_ivar(hash); + iter_lev_in_ivar_set(hash, lev+1); + } + else { + lev += 1; + RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | (lev << RHASH_LEV_SHIFT)); + if (lev == RHASH_LEV_MAX) { + iter_lev_in_ivar_set(hash, lev); + } + } } static void hash_iter_lev_dec(VALUE hash) { - *((int *)&RHASH(hash)->iter_lev) = RHASH_ITER_LEV(hash) - 1; + int lev = iter_lev_in_flags(hash); + if (lev == RHASH_LEV_MAX) { + lev = iter_lev_in_ivar(hash); + HASH_ASSERT(lev > 0); + iter_lev_in_ivar_set(hash, lev-1); + } + else { + HASH_ASSERT(lev > 0); + RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((lev-1) << RHASH_LEV_SHIFT)); + } } static VALUE |