mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
khash.h: make khash rebuild GC-safe
The hash rebuild process was not GC-safe. When rebuilding the hash table, the old data was orphaned before the new table was fully populated, which could lead to a segmentation fault if a GC cycle was triggered during the process. This patch refactors the rebuild function to follow a safer pattern: - A new temporary hash table is allocated on the stack. - Elements from the original table are copied to the new one. - The original table's data is swapped with the new table's data only after the new table is complete. This ensures the original data is always reachable by the GC during the rebuild. Co-authored-by: Gemini <gemini@google.com>
This commit is contained in:
+14
-15
@@ -165,29 +165,28 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
|
||||
return h->size; /* Not found - return end position */ \
|
||||
} \
|
||||
static inline void kh__rebuild_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets) { \
|
||||
/* Save old state */ \
|
||||
void *old_data = h->data; \
|
||||
kh_##name##_t hh; \
|
||||
hh.data = NULL; \
|
||||
hh.n_buckets = new_n_buckets; \
|
||||
hh.size = 0; \
|
||||
kh__alloc_##name(mrb, &hh); \
|
||||
/* Rehash from old 'h' to 'hh' */ \
|
||||
khkey_t *old_keys = kh_keys_##name(h); \
|
||||
khval_t *old_vals = kh_vals_##name(h); \
|
||||
uint8_t *old_flags = kh__is_small_##name(h) ? NULL : kh_flags_##name(h); \
|
||||
khint_t old_n_buckets = h->n_buckets; \
|
||||
khint_t old_size = h->size; \
|
||||
/* Allocate new table */ \
|
||||
h->n_buckets = new_n_buckets; \
|
||||
h->size = 0; \
|
||||
kh__alloc_##name(mrb, h); \
|
||||
/* Rehash elements */ \
|
||||
khint_t limit = old_flags ? old_n_buckets : old_size; \
|
||||
khint_t limit = old_flags ? h->n_buckets : h->size; \
|
||||
for (khint_t i = 0; i < limit; i++) { \
|
||||
if (old_flags && __ac_iseither(old_flags, i)) continue; \
|
||||
khint_t k = kh_put_##name(mrb, h, old_keys[i], NULL); \
|
||||
khint_t k = kh_put_##name(mrb, &hh, old_keys[i], NULL); \
|
||||
if (kh_is_map) { \
|
||||
khval_t *new_vals = kh_vals_##name(h); \
|
||||
new_vals[k] = old_vals[i]; \
|
||||
kh_val(name, &hh, k) = old_vals[i]; \
|
||||
} \
|
||||
} \
|
||||
/* Cleanup */ \
|
||||
mrb_free(mrb, old_data); \
|
||||
/* Final Swap */ \
|
||||
mrb_free(mrb, h->data); \
|
||||
h->data = hh.data; \
|
||||
h->n_buckets = hh.n_buckets; \
|
||||
h->size = hh.size; \
|
||||
} \
|
||||
static inline khint_t kh__put_small_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret) { \
|
||||
/* First check if key exists */ \
|
||||
|
||||
Reference in New Issue
Block a user