From ebd398ac5a4147a1e652d6943c39a29a62f12e66 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Wed, 16 Jan 2019 10:48:30 +0000 Subject: 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). --- hash.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) (limited to 'hash.c') diff --git a/hash.c b/hash.c index f458fd71aa..fc65e4cc7d 100644 --- a/hash.c +++ b/hash.c @@ -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 -- cgit v1.2.3