khash: add kh_init_data and kh_destroy_data functions

Add core data initialization functions that handle only the internal data
allocation/deallocation without managing the khash struct itself.

Changes:
- Add kh_init_data_##name() for initializing khash internal data
- Add kh_destroy_data_##name() for cleaning up khash internal data
- Refactor kh_init_##name##_size() to use kh_init_data internally
- Refactor kh_destroy_##name() to use kh_destroy_data internally
- Add corresponding kh_init_data() and kh_destroy_data() macros

Benefits:
- Eliminates code duplication between init/destroy and embed functions
- Provides clear separation: data functions handle internals, regular functions handle struct lifecycle
- Enables embedding khash in other structures (e.g., mruby-set's RSet)
- Centralizes complex initialization logic in single implementation

Architecture:
- kh_init_data/kh_destroy_data: core implementation with small table optimization
- kh_init_size/kh_destroy: convenience wrappers that add struct allocation
- Same functionality preserved, all tests pass

This prepares khash for mruby-set integration while improving code organization
and maintainability.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-08-01 14:11:43 +09:00
parent 56f798fb44
commit 3cd2201b70
+33 -20
View File
@@ -99,7 +99,9 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret); \
void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets); \
void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x); \
kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h);
kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h); \
void kh_init_data_##name(mrb_state *mrb, kh_##name##_t *h, khint_t size); \
void kh_destroy_data_##name(mrb_state *mrb, kh_##name##_t *h);
/* define kh_xxx_funcs
@@ -187,24 +189,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
} \
kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size) { \
kh_##name##_t *h = (kh_##name##_t*)mrb_calloc(mrb, 1, sizeof(kh_##name##_t)); \
if (size <= KHASH_SMALL_THRESHOLD) { \
/* Start as small table */ \
h->n_buckets = 0; /* Small table marker */ \
if (kh_alloc_small_##name(mrb, h)) { \
mrb_free(mrb, h); \
mrb_raise_nomemory(mrb); \
} \
} else { \
/* Start as regular hash table */ \
if (size < KHASH_MIN_SIZE) \
size = KHASH_MIN_SIZE; \
khash_power2(size); \
h->n_buckets = size; \
if (kh_alloc_simple_##name(mrb, h)) { \
mrb_free(mrb, h); \
mrb_raise_nomemory(mrb); \
} \
} \
kh_init_data_##name(mrb, h, size); \
return h; \
} \
kh_##name##_t *kh_init_##name(mrb_state *mrb) { \
@@ -213,7 +198,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
if (h) { \
mrb_free(mrb, h->data); /* Free single data allocation */ \
kh_destroy_data_##name(mrb, h); \
mrb_free(mrb, h); \
} \
} \
@@ -345,6 +330,32 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
} \
} \
return h2; \
} \
void kh_init_data_##name(mrb_state *mrb, kh_##name##_t *h, khint_t size) { \
if (size <= KHASH_SMALL_THRESHOLD) { \
/* Start as small table */ \
h->n_buckets = 0; /* Small table marker */ \
if (kh_alloc_small_##name(mrb, h)) { \
mrb_raise_nomemory(mrb); \
} \
} \
else { \
/* Start as regular hash table */ \
if (size < KHASH_MIN_SIZE) \
size = KHASH_MIN_SIZE; \
khash_power2(size); \
h->n_buckets = size; \
if (kh_alloc_simple_##name(mrb, h)) { \
mrb_raise_nomemory(mrb); \
} \
} \
} \
void kh_destroy_data_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
if (h && h->data) { \
mrb_free(mrb, h->data); /* Free only the data allocation */ \
h->data = NULL; \
} \
}
@@ -360,6 +371,8 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
#define kh_get(name, mrb, h, k) kh_get_##name(mrb, h, k)
#define kh_del(name, mrb, h, k) kh_del_##name(mrb, h, k)
#define kh_copy(name, mrb, h) kh_copy_##name(mrb, h)
#define kh_init_data(name, mrb, h, size) kh_init_data_##name(mrb, h, size)
#define kh_destroy_data(name, mrb, h) kh_destroy_data_##name(mrb, h)
/* BREAKING CHANGE: Field access macros now require type name as first parameter
* The macros keep their familiar names but now need the hash type name.