summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2026-04-11 15:42:19 +0200
committerJean Boussier <jean.boussier@gmail.com>2026-04-12 14:59:53 +0200
commit8e96d6f792ccc840b804a4fd443d0b0064b54752 (patch)
tree3735cbc6491777f39a1a41c3e1f70c6c58f9e38d
parentd0ef5d137fa1d128a0fcb8564c6966177313ab92 (diff)
rb_gc_obj_optimal_size: don't enlarge small AR table hashes
Ref: https://github.com/ruby/ruby/pull/16653 Frozen hashes backed by AR tables can be smaller than 160B.
-rw-r--r--gc.c7
-rw-r--r--hash.c16
-rw-r--r--internal/hash.h16
3 files changed, 22 insertions, 17 deletions
diff --git a/gc.c b/gc.c
index 3cf3bdebce..8a0d051e8f 100644
--- a/gc.c
+++ b/gc.c
@@ -3601,7 +3601,12 @@ rb_gc_obj_optimal_size(VALUE obj)
}
case T_HASH:
- return sizeof(struct RHash) + (RHASH_ST_TABLE_P(obj) ? sizeof(st_table) : sizeof(ar_table));
+ {
+ if (RB_OBJ_FROZEN(obj) && RHASH_AR_TABLE_P(obj)) {
+ return sizeof(struct RHash) + offsetof(ar_table, pairs) + RHASH_AR_TABLE_BOUND(obj) * sizeof(ar_table_pair);
+ }
+ return sizeof(struct RHash) + (RHASH_ST_TABLE_P(obj) ? sizeof(st_table) : sizeof(ar_table));
+ }
default:
return 0;
diff --git a/hash.c b/hash.c
index 751d4564d5..4321f479fd 100644
--- a/hash.c
+++ b/hash.c
@@ -471,24 +471,8 @@ ar_set_entry(VALUE hash, unsigned int index, st_data_t key, st_data_t val, st_ha
#define RHASH_AR_TABLE_SIZE(h) (HASH_ASSERT(RHASH_AR_TABLE_P(h)), \
RHASH_AR_TABLE_SIZE_RAW(h))
-#define RHASH_AR_TABLE_BOUND_RAW(h) \
- ((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \
- (RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT)))
-
-#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s)
-#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type)
-
#define HASH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(HASH_DEBUG, expr, #expr)
-static inline unsigned int
-RHASH_AR_TABLE_BOUND(VALUE h)
-{
- HASH_ASSERT(RHASH_AR_TABLE_P(h));
- const unsigned int bound = RHASH_AR_TABLE_BOUND_RAW(h);
- HASH_ASSERT(bound <= RHASH_AR_TABLE_MAX_SIZE);
- return bound;
-}
-
#if HASH_DEBUG
#define hash_verify(hash) hash_verify_(hash, __FILE__, __LINE__)
diff --git a/internal/hash.h b/internal/hash.h
index 3a5be14ad7..8c5511e82b 100644
--- a/internal/hash.h
+++ b/internal/hash.h
@@ -193,4 +193,20 @@ RHASH_AR_TABLE_SIZE_RAW(VALUE h)
return (unsigned)ret;
}
+#define RHASH_AR_TABLE_BOUND_RAW(h) \
+ ((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \
+ (RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT)))
+
+#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s)
+#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type)
+
+static inline unsigned int
+RHASH_AR_TABLE_BOUND(VALUE h)
+{
+ RUBY_ASSERT(RHASH_AR_TABLE_P(h));
+ const unsigned int bound = RHASH_AR_TABLE_BOUND_RAW(h);
+ RUBY_ASSERT(bound <= RHASH_AR_TABLE_MAX_SIZE);
+ return bound;
+}
+
#endif /* INTERNAL_HASH_H */