- Make it unsigned like as in-flags bits
- Make it long since it should be fixable
- Reduce it to in-flags bits after decrement
This commit is contained in:
Nobuyoshi Nakada 2023-11-12 00:34:45 +09:00
Родитель 94f82a65f7
Коммит 9ab64b1d70
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 3582D74E1FEE4465
1 изменённых файлов: 31 добавлений и 23 удалений

54
hash.c
Просмотреть файл

@ -1274,39 +1274,43 @@ hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
return hash_iter_status_check(status); return hash_iter_status_check(status);
} }
static int static unsigned long
iter_lev_in_ivar(VALUE hash) iter_lev_in_ivar(VALUE hash)
{ {
VALUE levval = rb_ivar_get(hash, id_hash_iter_lev); VALUE levval = rb_ivar_get(hash, id_hash_iter_lev);
HASH_ASSERT(FIXNUM_P(levval)); HASH_ASSERT(FIXNUM_P(levval));
return FIX2INT(levval); long lev = FIX2LONG(levval);
HASH_ASSERT(lev >= 0);
return (unsigned long)lev;
} }
void rb_ivar_set_internal(VALUE obj, ID id, VALUE val); void rb_ivar_set_internal(VALUE obj, ID id, VALUE val);
static void static void
iter_lev_in_ivar_set(VALUE hash, int lev) iter_lev_in_ivar_set(VALUE hash, unsigned long lev)
{ {
rb_ivar_set_internal(hash, id_hash_iter_lev, INT2FIX(lev)); HASH_ASSERT(lev >= RHASH_LEV_MAX);
HASH_ASSERT(POSFIXABLE(lev)); /* POSFIXABLE means fitting to long */
rb_ivar_set_internal(hash, id_hash_iter_lev, LONG2FIX((long)lev));
} }
static inline int static inline unsigned long
iter_lev_in_flags(VALUE hash) iter_lev_in_flags(VALUE hash)
{ {
unsigned int u = (unsigned int)((RBASIC(hash)->flags >> RHASH_LEV_SHIFT) & RHASH_LEV_MAX); return (unsigned long)((RBASIC(hash)->flags >> RHASH_LEV_SHIFT) & RHASH_LEV_MAX);
return (int)u;
} }
static inline void static inline void
iter_lev_in_flags_set(VALUE hash, int lev) iter_lev_in_flags_set(VALUE hash, unsigned long lev)
{ {
HASH_ASSERT(lev <= RHASH_LEV_MAX);
RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT)); RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT));
} }
static int static unsigned long
RHASH_ITER_LEV(VALUE hash) RHASH_ITER_LEV(VALUE hash)
{ {
int lev = iter_lev_in_flags(hash); unsigned long lev = iter_lev_in_flags(hash);
if (lev == RHASH_LEV_MAX) { if (lev == RHASH_LEV_MAX) {
return iter_lev_in_ivar(hash); return iter_lev_in_ivar(hash);
@ -1319,33 +1323,37 @@ RHASH_ITER_LEV(VALUE hash)
static void static void
hash_iter_lev_inc(VALUE hash) hash_iter_lev_inc(VALUE hash)
{ {
int lev = iter_lev_in_flags(hash); unsigned long lev = iter_lev_in_flags(hash);
if (lev == RHASH_LEV_MAX) { if (lev == RHASH_LEV_MAX) {
lev = iter_lev_in_ivar(hash); lev = iter_lev_in_ivar(hash) + 1;
iter_lev_in_ivar_set(hash, lev+1); if (!POSFIXABLE(lev)) { /* paranoiac check */
rb_raise(rb_eRuntimeError, "too much nested iterations");
}
} }
else { else {
lev += 1; lev += 1;
iter_lev_in_flags_set(hash, lev); iter_lev_in_flags_set(hash, lev);
if (lev == RHASH_LEV_MAX) { if (lev < RHASH_LEV_MAX) return;
iter_lev_in_ivar_set(hash, lev);
}
} }
iter_lev_in_ivar_set(hash, lev);
} }
static void static void
hash_iter_lev_dec(VALUE hash) hash_iter_lev_dec(VALUE hash)
{ {
int lev = iter_lev_in_flags(hash); unsigned long lev = iter_lev_in_flags(hash);
if (lev == RHASH_LEV_MAX) { if (lev == RHASH_LEV_MAX) {
lev = iter_lev_in_ivar(hash); lev = iter_lev_in_ivar(hash);
HASH_ASSERT(lev > 0); if (lev > RHASH_LEV_MAX) {
iter_lev_in_ivar_set(hash, lev-1); iter_lev_in_ivar_set(hash, lev-1);
return;
}
rb_attr_delete(hash, id_hash_iter_lev);
} }
else { else if (lev == 0) {
HASH_ASSERT(lev > 0); rb_raise(rb_eRuntimeError, "iteration level underflow");
iter_lev_in_flags_set(hash, lev - 1);
} }
iter_lev_in_flags_set(hash, lev - 1);
} }
static VALUE static VALUE
@ -2901,7 +2909,7 @@ NOINSERT_UPDATE_CALLBACK(hash_aset_str)
VALUE VALUE
rb_hash_aset(VALUE hash, VALUE key, VALUE val) rb_hash_aset(VALUE hash, VALUE key, VALUE val)
{ {
int iter_lev = RHASH_ITER_LEV(hash); unsigned long iter_lev = RHASH_ITER_LEV(hash);
rb_hash_modify(hash); rb_hash_modify(hash);