summaryrefslogtreecommitdiff
path: root/hash.c
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@cookpad.com>2019-01-16 10:48:30 +0000
committerKoichi Sasada <ko1@atdot.net>2019-07-31 09:44:23 +0900
commitebd398ac5a4147a1e652d6943c39a29a62f12e66 (patch)
tree79dba6622c591db987f0a9c065fe37cd09a58e98 /hash.c
parent4afd8975242917d319cfb20c7ed635b979ad48d5 (diff)
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.c60
1 files changed, 58 insertions, 2 deletions
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