khash: rename internal helpers to kh__ prefix and organize API boundary

Rename internal helper functions from kh_ to kh__ prefix while correctly
organizing the API boundary:

KHASH_DECLARE (public interface):
- kh_keys_##name, kh_vals_##name, kh_flags_##name (used by kh_exist macro)

KHASH_DEFINE (internal helpers with kh__ prefix):
- kh__kv_size_##name, kh__htable_size_##name
- kh__mark_occupied_##name, kh__mark_deleted_##name
- kh__key_idx_##name, kh__next_probe_##name
- kh__insert_key_##name, kh__clear_flags_##name
- kh__is_small_##name, kh__get_small_##name
- kh__rebuild_##name, kh__put_small_##name

This clearly separates public API functions from internal implementation
helpers while ensuring kh_flags_##name remains accessible to the public
kh_exist macro.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-08-03 16:33:21 +09:00
parent f4e9bf603c
commit 0be8fdc030
+41 -40
View File
@@ -86,6 +86,10 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
return kh_is_map ? \
(khval_t*)((uint8_t*)(h)->data + sizeof(khkey_t) * (h)->n_buckets) : NULL; \
} \
static inline uint8_t* kh_flags_##name(const kh_##name##_t *h) { \
return (uint8_t*)(h)->data + sizeof(khkey_t) * (h)->n_buckets + \
(kh_is_map ? sizeof(khval_t) * (h)->n_buckets : 0); \
} \
void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h); \
kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size); \
kh_##name##_t *kh_init_##name(mrb_state *mrb); \
@@ -112,57 +116,54 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
#define KHASH_DEFINE(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb); \
/* Internal helper functions */ \
static inline size_t kh_kv_size_##name(khint_t count) { \
static inline size_t kh__kv_size_##name(khint_t count) { \
return sizeof(khkey_t) * count + \
(kh_is_map ? sizeof(khval_t) * count : 0); \
} \
static inline size_t kh_htable_size_##name(khint_t n_buckets) { \
return kh_kv_size_##name(n_buckets) + n_buckets / 4; \
static inline size_t kh__htable_size_##name(khint_t n_buckets) { \
return kh__kv_size_##name(n_buckets) + n_buckets / 4; \
} \
static inline uint8_t* kh_flags_##name(const kh_##name##_t *h) { \
return (uint8_t*)(h)->data + kh_kv_size_##name((h)->n_buckets); \
} \
static inline void kh_mark_occupied_##name(kh_##name##_t *h, khint_t i) { \
static inline void kh__mark_occupied_##name(kh_##name##_t *h, khint_t i) { \
uint8_t *flags = kh_flags_##name(h); \
flags[i/4] &= ~__m_either[i%4]; /* Clear both empty and deleted bits */ \
} \
static inline void kh_mark_deleted_##name(kh_##name##_t *h, khint_t i) { \
static inline void kh__mark_deleted_##name(kh_##name##_t *h, khint_t i) { \
uint8_t *flags = kh_flags_##name(h); \
flags[i/4] |= __m_del[i%4]; /* Set deleted bit */ \
} \
static inline khint_t kh_key_idx_##name(mrb_state *mrb, khkey_t key, kh_##name##_t *h) { \
static inline khint_t kh__key_idx_##name(mrb_state *mrb, khkey_t key, kh_##name##_t *h) { \
return __hash_func(mrb, key) & khash_mask(h); \
} \
static inline khint_t kh_next_probe_##name(khint_t k, khint_t *step, kh_##name##_t *h) { \
static inline khint_t kh__next_probe_##name(khint_t k, khint_t *step, kh_##name##_t *h) { \
return (k+(++(*step))) & khash_mask(h); \
} \
static inline khint_t kh_insert_key_##name(kh_##name##_t *h, khint_t index, khkey_t key) { \
static inline khint_t kh__insert_key_##name(kh_##name##_t *h, khint_t index, khkey_t key) { \
khkey_t *keys = kh_keys_##name(h); \
keys[index] = key; \
kh_mark_occupied_##name(h, index); \
kh__mark_occupied_##name(h, index); \
h->size++; \
return index; \
} \
static inline void kh_clear_flags_##name(kh_##name##_t *h, khint_t n_buckets) { \
static inline void kh__clear_flags_##name(kh_##name##_t *h, khint_t n_buckets) { \
memset(kh_flags_##name(h), 0xaa, n_buckets/4); \
} \
/* Small table optimization functions */ \
static inline int kh_is_small_##name(const kh_##name##_t *h) { \
static inline int kh__is_small_##name(const kh_##name##_t *h) { \
return h->n_buckets == 0; /* Small table marker */ \
} \
static inline khint_t kh_get_small_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) { \
static inline khint_t kh__get_small_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) { \
khkey_t *keys = kh_keys_##name(h); \
for (khint_t i = 0; i < h->size; i++) { \
if (__hash_equal(mrb, keys[i], key)) return i; \
} \
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) { \
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; \
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); \
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 */ \
@@ -182,9 +183,9 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
/* Cleanup */ \
mrb_free(mrb, old_data); \
} \
static inline khint_t kh_put_small_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret) { \
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 */ \
khint_t pos = kh_get_small_##name(mrb, h, key); \
khint_t pos = kh__get_small_##name(mrb, h, key); \
if (pos < h->size) { \
if (ret) *ret = 0; /* Key exists */ \
return pos; \
@@ -192,7 +193,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
/* Check if we need to convert to hash table */ \
if (h->size >= KHASH_SMALL_THRESHOLD) { \
/* Convert from small table to hash table */ \
kh_rebuild_##name(mrb, h, KHASH_MIN_SIZE); \
kh__rebuild_##name(mrb, h, KHASH_MIN_SIZE); \
/* Now add the new key using regular hash table */ \
return kh_put_##name(mrb, h, key, ret); \
} \
@@ -206,10 +207,10 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
khint_t sz = h->n_buckets; \
uint8_t *p = (uint8_t*)mrb_malloc(mrb, kh_htable_size_##name(sz)); \
uint8_t *p = (uint8_t*)mrb_malloc(mrb, kh__htable_size_##name(sz)); \
h->size = 0; \
h->data = p; /* Single data pointer for optimized layout */ \
kh_clear_flags_##name(h, sz); \
kh__clear_flags_##name(h, sz); \
} \
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)); \
@@ -228,25 +229,25 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
{ \
(void)mrb; \
if (h && h->data) { \
kh_clear_flags_##name(h, h->n_buckets); \
kh__clear_flags_##name(h, h->n_buckets); \
h->size = 0; \
} \
} \
khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) \
{ \
if (kh_is_small_##name(h)) { \
return kh_get_small_##name(mrb, h, key); \
if (kh__is_small_##name(h)) { \
return kh__get_small_##name(mrb, h, key); \
} \
/* Cache calculated pointers for performance */ \
khkey_t *keys = kh_keys_##name(h); \
uint8_t *ed_flags = kh_flags_##name(h); \
khint_t k = kh_key_idx_##name(mrb, key, h), step = 0; \
khint_t k = kh__key_idx_##name(mrb, key, h), step = 0; \
(void)mrb; \
while (!__ac_isempty(ed_flags, k)) { \
if (!__ac_isdel(ed_flags, k)) { \
if (__hash_equal(mrb, keys[k], key)) return k; \
} \
k = kh_next_probe_##name(k, &step, h); \
k = kh__next_probe_##name(k, &step, h); \
} \
return kh_end(h); \
} \
@@ -255,12 +256,12 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
if (new_n_buckets < KHASH_MIN_SIZE) \
new_n_buckets = KHASH_MIN_SIZE; \
khash_power2(new_n_buckets); \
kh_rebuild_##name(mrb, h, new_n_buckets); \
kh__rebuild_##name(mrb, h, new_n_buckets); \
} \
khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret) \
{ \
if (kh_is_small_##name(h)) { \
return kh_put_small_##name(mrb, h, key, ret); \
if (kh__is_small_##name(h)) { \
return kh__put_small_##name(mrb, h, key, ret); \
} \
khint_t k, del_k, step = 0; \
if (h->size >= khash_upper_bound(h)) { \
@@ -269,7 +270,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
/* Cache calculated pointers for performance */ \
khkey_t *keys = kh_keys_##name(h); \
uint8_t *ed_flags = kh_flags_##name(h); \
k = kh_key_idx_##name(mrb, key, h); \
k = kh__key_idx_##name(mrb, key, h); \
del_k = kh_end(h); \
while (!__ac_isempty(ed_flags, k)) { \
if (!__ac_isdel(ed_flags, k)) { \
@@ -281,17 +282,17 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
else if (del_k == kh_end(h)) { \
del_k = k; \
} \
k = kh_next_probe_##name(k, &step, h); \
k = kh__next_probe_##name(k, &step, h); \
} \
if (del_k != kh_end(h)) { \
/* put at del */ \
kh_insert_key_##name(h, del_k, key); \
kh__insert_key_##name(h, del_k, key); \
if (ret) *ret = 2; \
return del_k; \
} \
else { \
/* put at empty */ \
kh_insert_key_##name(h, k, key); \
kh__insert_key_##name(h, k, key); \
if (ret) *ret = 1; \
return k; \
} \
@@ -299,7 +300,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x) \
{ \
(void)mrb; \
if (kh_is_small_##name(h)) { \
if (kh__is_small_##name(h)) { \
/* Small table deletion: shift elements down */ \
mrb_assert(x < h->size); \
khkey_t *keys = kh_keys_##name(h); \
@@ -313,7 +314,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
else { \
/* Regular hash table deletion */ \
mrb_assert(x != h->n_buckets && !__ac_iseither(kh_flags_##name(h), x)); \
kh_mark_deleted_##name(h, x); \
kh__mark_deleted_##name(h, x); \
h->size--; \
} \
} \
@@ -327,7 +328,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
if (size <= KHASH_SMALL_THRESHOLD) { \
/* Start as small table */ \
h->n_buckets = 0; /* Small table marker */ \
h->data = mrb_malloc(mrb, kh_kv_size_##name(KHASH_SMALL_THRESHOLD)); \
h->data = mrb_malloc(mrb, kh__kv_size_##name(KHASH_SMALL_THRESHOLD)); \
h->size = 0; \
} \
else { \
@@ -357,17 +358,17 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
} \
else if (src->n_buckets == 0) { \
/* Small table case */ \
size_t data_size = kh_kv_size_##name(KHASH_SMALL_THRESHOLD); \
size_t data_size = kh__kv_size_##name(KHASH_SMALL_THRESHOLD); \
dst->data = mrb_realloc(mrb, dst->data, data_size); \
dst->size = src->size; \
dst->n_buckets = 0; \
/* Copy only the used portion of keys and values */ \
size_t copy_size = kh_kv_size_##name(src->size); \
size_t copy_size = kh__kv_size_##name(src->size); \
memcpy(dst->data, src->data, copy_size); \
} \
else { \
/* Regular hash table case */ \
size_t data_size = kh_htable_size_##name(src->n_buckets); \
size_t data_size = kh__htable_size_##name(src->n_buckets); \
dst->data = mrb_realloc(mrb, dst->data, data_size); \
dst->size = src->size; \
dst->n_buckets = src->n_buckets; \