← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
hash_table.c
Go to the documentation of this file.
1/*
2 * Hash Table with AVX-512 Optimized String Comparison
3 *
4 * Direct copy from HPC_Embeddings with SIMD optimizations.
5 */
6
7#include <stdint.h>
8#include <stdlib.h>
9#include <string.h>
10#if defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) || defined(__SSSE3__) || \
11 defined(__SSE4_1__) || defined(__SSE4_2__) || defined(__AVX__) || defined(__AVX2__) || \
12 defined(__AVX512F__)
13#include <immintrin.h>
14#endif
15#include <stdio.h>
18
19/* Hash seed constant from HPC_Embeddings */
20#define CK_TOKENIZER_HASH_SEED 0x9747b28c
21
22uint32_t ck_tokenizer_hash(const char *key, size_t len) {
23 return ck_murmurhash3(key, (uint32_t)len, CK_TOKENIZER_HASH_SEED);
24}
25
26uint32_t ck_tokenizer_hash_str(const char *key) {
28}
29
30/* SIMD-optimized string comparison using AVX-512 */
31/* Falls back to regular strcmp if AVX-512 is not available at compile time */
32static inline int simd_strcmp(const char *s1, const char *s2) {
33 size_t len1 = strlen(s1);
34 size_t len2 = strlen(s2);
35
36 /* Fallback to regular strcmp for short strings */
37 if (len1 < 64 || len2 < 64) {
38 return strcmp(s1, s2);
39 }
40
41#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512DQ__)
42 /* AVX-512 path */
43 while (1) {
44 /* Load 64 bytes from each string into AVX-512 registers */
45 __m512i chunk1 = _mm512_loadu_si512((const __m512i *)s1);
46 __m512i chunk2 = _mm512_loadu_si512((const __m512i *)s2);
47
48 /* Compare the chunks byte by byte */
49 __mmask64 cmp_mask = _mm512_cmpeq_epu8_mask(chunk1, chunk2);
50
51 /* Check if all bytes are equal */
52 if (cmp_mask != 0xFFFFFFFFFFFFFFFF) {
53 /* Find the first differing byte */
54 int first_diff = __builtin_ctzll(~cmp_mask);
55 return (unsigned char)s1[first_diff] - (unsigned char)s2[first_diff];
56 }
57
58 /* Check if we hit a null character in s1 or s2 */
59 __mmask64 null_mask1 = _mm512_test_epi8_mask(chunk1, _mm512_set1_epi8('\0'));
60 __mmask64 null_mask2 = _mm512_test_epi8_mask(chunk2, _mm512_set1_epi8('\0'));
61
62 if (null_mask1 || null_mask2) {
63 if (len1 == len2) {
64 return 0;
65 } else {
66 return (len1 < len2) ? -1 : 1;
67 }
68 }
69
70 /* Advance the pointers by 64 bytes for the next iteration */
71 s1 += 64;
72 s2 += 64;
73 }
74#else
75 /* SSE/AVX fallback - just use regular strcmp for simplicity */
76 (void)s1;
77 (void)s2;
78 (void)len1;
79 (void)len2;
80 return strcmp(s1, s2);
81#endif
82}
83
85 if (bucket_count == 0) {
86 bucket_count = CK_TOKENIZER_HT_BUCKETS_SMALL;
87 }
88
90 if (!table) {
91 return NULL;
92 }
93
94 table->entries = (CKTokenizerHashEntry **)calloc(bucket_count, sizeof(CKTokenizerHashEntry *));
95 if (!table->entries) {
96 free(table);
97 return NULL;
98 }
99
100 table->size = bucket_count;
101 table->count = 0;
102 table->load_factor = 0.75f;
103
104 return table;
105}
106
107static CKTokenizerHashEntry *create_entry(const char *key, const void *value, size_t value_size) {
109 if (!entry) {
110 return NULL;
111 }
112
113 entry->key = strdup(key);
114 if (!entry->key) {
115 free(entry);
116 return NULL;
117 }
118
119 if (value) {
120 entry->value = malloc(value_size);
121 if (!entry->value) {
122 free(entry->key);
123 free(entry);
124 return NULL;
125 }
126 memcpy(entry->value, value, value_size);
127 } else {
128 entry->value = NULL;
129 }
130
131 entry->next = NULL;
132 return entry;
133}
134
135static void free_entry(CKTokenizerHashEntry *entry, bool free_value) {
136 if (!entry) return;
137 free(entry->key);
138 if (free_value && entry->value) {
139 free(entry->value);
140 }
141 free(entry);
142}
143
145 if (!table) {
146 return;
147 }
148
149 for (size_t i = 0; i < table->size; i++) {
150 CKTokenizerHashEntry *entry = table->entries[i];
151 while (entry) {
152 CKTokenizerHashEntry *next = entry->next;
153 free_entry(entry, free_values);
154 entry = next;
155 }
156 }
157
158 free(table->entries);
159 free(table);
160}
161
163 const char *key,
164 void *value) {
165 if (!table || !key) {
166 return -1;
167 }
168
169 uint32_t bucket = ck_tokenizer_hash_str(key) % table->size;
170 CKTokenizerHashEntry *entry = table->entries[bucket];
171
172 /* Check if key already exists */
173 while (entry) {
174 if (strcmp(entry->key, key) == 0) {
175 /* Update existing entry - just replace value pointer */
176 entry->value = value;
177 return 0;
178 }
179 entry = entry->next;
180 }
181
182 /* Create new entry with NULL value (caller manages memory) */
183 CKTokenizerHashEntry *new_entry = (CKTokenizerHashEntry *)malloc(sizeof(CKTokenizerHashEntry));
184 if (!new_entry) {
185 return -1;
186 }
187
188 new_entry->key = strdup(key);
189 if (!new_entry->key) {
190 free(new_entry);
191 return -1;
192 }
193
194 new_entry->value = value;
195 new_entry->next = table->entries[bucket];
196 table->entries[bucket] = new_entry;
197 table->count++;
198
199 return 0;
200}
201
203 if (!table || !key) {
204 return NULL;
205 }
206
207 uint32_t bucket = ck_tokenizer_hash_str(key) % table->size;
208 CKTokenizerHashEntry *entry = table->entries[bucket];
209
210 while (entry) {
211 if (strcmp(entry->key, key) == 0) {
212 return entry->value;
213 }
214 entry = entry->next;
215 }
216
217 return NULL;
218}
219
220/* AVX-512 optimized lookup */
222 if (!table || !key) {
223 return NULL;
224 }
225
226 uint32_t bucket = ck_tokenizer_hash_str(key) % table->size;
227 CKTokenizerHashEntry *entry = table->entries[bucket];
228
229 while (entry) {
230 if (simd_strcmp(entry->key, key) == 0) {
231 return entry->value;
232 }
233 entry = entry->next;
234 }
235
236 return NULL;
237}
238
240 const char *key,
241 bool free_value) {
242 if (!table || !key) {
243 return -1;
244 }
245
246 uint32_t bucket = ck_tokenizer_hash_str(key) % table->size;
247 CKTokenizerHashEntry *entry = table->entries[bucket];
248 CKTokenizerHashEntry *prev = NULL;
249
250 while (entry) {
251 if (strcmp(entry->key, key) == 0) {
252 if (prev) {
253 prev->next = entry->next;
254 } else {
255 table->entries[bucket] = entry->next;
256 }
257 free_entry(entry, free_value);
258 table->count--;
259 return 0;
260 }
261 prev = entry;
262 entry = entry->next;
263 }
264
265 return -1;
266}
267
269 return table ? table->count : 0;
270}
271
273 return ck_tokenizer_hash_table_lookup(table, key) != NULL;
274}
275
278 void *user_data) {
279 if (!table || !callback) {
280 return -1;
281 }
282
283 for (size_t i = 0; i < table->size; i++) {
284 CKTokenizerHashEntry *entry = table->entries[i];
285 while (entry) {
286 int ret = callback(entry->key, entry->value, user_data);
287 if (ret != 0) {
288 return ret;
289 }
290 entry = entry->next;
291 }
292 }
293
294 return 0;
295}
296
298 const char **out_keys,
299 size_t max_keys) {
300 if (!table || !out_keys) {
301 return 0;
302 }
303
304 size_t written = 0;
305 for (size_t i = 0; i < table->size && written < max_keys; i++) {
306 CKTokenizerHashEntry *entry = table->entries[i];
307 while (entry && written < max_keys) {
308 out_keys[written++] = entry->key;
309 entry = entry->next;
310 }
311 }
312
313 return written;
314}
315
317 if (!table) {
318 return;
319 }
320
321 for (size_t i = 0; i < table->size; i++) {
322 CKTokenizerHashEntry *entry = table->entries[i];
323 while (entry) {
324 CKTokenizerHashEntry *next = entry->next;
325 free_entry(entry, free_values);
326 entry = next;
327 }
328 table->entries[i] = NULL;
329 }
330
331 table->count = 0;
332}
bool ck_tokenizer_hash_table_contains(CKTokenizerHashTable *table, const char *key)
Definition hash_table.c:272
#define CK_TOKENIZER_HASH_SEED
Definition hash_table.c:20
size_t ck_tokenizer_hash_table_count(CKTokenizerHashTable *table)
Definition hash_table.c:268
size_t ck_tokenizer_hash_table_keys(CKTokenizerHashTable *table, const char **out_keys, size_t max_keys)
Definition hash_table.c:297
void ck_tokenizer_hash_table_free(CKTokenizerHashTable *table, bool free_values)
Definition hash_table.c:144
static CKTokenizerHashEntry * create_entry(const char *key, const void *value, size_t value_size)
Definition hash_table.c:107
static void free_entry(CKTokenizerHashEntry *entry, bool free_value)
Definition hash_table.c:135
int ck_tokenizer_hash_table_iterate(CKTokenizerHashTable *table, CKTokenizerHashCallback callback, void *user_data)
Definition hash_table.c:276
int ck_tokenizer_hash_table_insert(CKTokenizerHashTable *table, const char *key, void *value)
Definition hash_table.c:162
void * ck_tokenizer_hash_table_lookup_avx(CKTokenizerHashTable *table, const char *key)
Definition hash_table.c:221
uint32_t ck_tokenizer_hash(const char *key, size_t len)
Definition hash_table.c:22
uint32_t ck_tokenizer_hash_str(const char *key)
Definition hash_table.c:26
CKTokenizerHashTable * ck_tokenizer_hash_table_create(size_t bucket_count)
Definition hash_table.c:84
static int simd_strcmp(const char *s1, const char *s2)
Definition hash_table.c:32
void * ck_tokenizer_hash_table_lookup(CKTokenizerHashTable *table, const char *key)
Definition hash_table.c:202
int ck_tokenizer_hash_table_delete(CKTokenizerHashTable *table, const char *key, bool free_value)
Definition hash_table.c:239
void ck_tokenizer_hash_table_clear(CKTokenizerHashTable *table, bool free_values)
Definition hash_table.c:316
int(* CKTokenizerHashCallback)(const char *key, void *value, void *user_data)
Definition hash_table.h:112
#define CK_TOKENIZER_HT_BUCKETS_SMALL
Definition hash_table.h:140
uint32_t ck_murmurhash3(const char *key, uint32_t len, uint32_t seed)
Definition murmurhash3.c:11
static uint32_t ck_murmurhash3_str(const char *str, uint32_t seed)
Definition murmurhash3.h:55
struct CKTokenizerHashEntry * next
Definition hash_table.h:25
CKTokenizerHashEntry ** entries
Definition hash_table.h:30