| 96 | } |
| 97 | |
| 98 | const char *cbm_intern_n(CBMInternPool *pool, const char *s, size_t len) { |
| 99 | if (!pool || !s) { |
| 100 | return NULL; |
| 101 | } |
| 102 | |
| 103 | uint32_t h = intern_hash(s, len); |
| 104 | uint32_t idx = h & pool->mask; |
| 105 | |
| 106 | /* Probe for existing entry */ |
| 107 | for (;;) { |
| 108 | const InternEntry *e = &pool->buckets[idx]; |
| 109 | if (!e->str) { |
| 110 | break; /* empty slot — not found */ |
| 111 | } |
| 112 | if (e->hash == h && e->len == (uint32_t)len && memcmp(e->str, s, len) == 0) { |
| 113 | return e->str; /* found — return existing pointer */ |
| 114 | } |
| 115 | idx = (idx + SKIP_ONE) & pool->mask; |
| 116 | } |
| 117 | |
| 118 | /* Resize at 70% load */ |
| 119 | if (pool->count * CBM_DECIMAL_BASE >= pool->capacity * INTERN_LOAD_NUM) { |
| 120 | intern_resize(pool); |
| 121 | /* Re-probe after resize */ |
| 122 | idx = h & pool->mask; |
| 123 | while (pool->buckets[idx].str) { |
| 124 | idx = (idx + SKIP_ONE) & pool->mask; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | /* Copy string into arena */ |
| 129 | char *copy = cbm_arena_strndup(&pool->arena, s, len); |
| 130 | if (!copy) { |
| 131 | return NULL; |
| 132 | } |
| 133 | |
| 134 | pool->buckets[idx] = (InternEntry){.str = copy, .hash = h, .len = (uint32_t)len}; |
| 135 | pool->count++; |
| 136 | pool->total_bytes += len; |
| 137 | return copy; |
| 138 | } |
| 139 | |
| 140 | const char *cbm_intern(CBMInternPool *pool, const char *s) { |
| 141 | if (!s) { |
no test coverage detected