PyCyBase 0.1.8.post4__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. cbase/__infra__.pxd +6 -0
  2. cbase/__init__.pxd +6 -0
  3. cbase/__init__.py +28 -0
  4. cbase/allocator_protocol/__infra__.pxd +58 -0
  5. cbase/allocator_protocol/__init__.pxd +58 -0
  6. cbase/allocator_protocol/__init__.py +45 -0
  7. cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
  8. cbase/allocator_protocol/c_allocator_protocol.cp313-win_amd64.pyd +0 -0
  9. cbase/allocator_protocol/c_allocator_protocol.h +352 -0
  10. cbase/allocator_protocol/c_allocator_protocol.pxd +64 -0
  11. cbase/allocator_protocol/c_allocator_protocol.pyi +144 -0
  12. cbase/allocator_protocol/c_allocator_protocol.pyx +209 -0
  13. cbase/allocator_protocol/c_heap_allocator.c +38144 -0
  14. cbase/allocator_protocol/c_heap_allocator.cp313-win_amd64.pyd +0 -0
  15. cbase/allocator_protocol/c_heap_allocator.h +444 -0
  16. cbase/allocator_protocol/c_heap_allocator.pxd +97 -0
  17. cbase/allocator_protocol/c_heap_allocator.pyi +161 -0
  18. cbase/allocator_protocol/c_heap_allocator.pyx +300 -0
  19. cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
  20. cbase/allocator_protocol/c_nt_shm_allocator.cp313-win_amd64.pyd +0 -0
  21. cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
  22. cbase/allocator_protocol/c_nt_shm_allocator.pxd +102 -0
  23. cbase/allocator_protocol/c_nt_shm_allocator.pyx +223 -0
  24. cbase/allocator_protocol/c_shm_allocator.h +1168 -0
  25. cbase/allocator_protocol/c_shm_allocator.pxd +142 -0
  26. cbase/allocator_protocol/c_shm_allocator.pyi +299 -0
  27. cbase/allocator_protocol/c_shm_allocator.pyx +442 -0
  28. cbase/allocator_protocol/c_shm_comp.c +4643 -0
  29. cbase/allocator_protocol/c_shm_comp.cp313-win_amd64.pyd +0 -0
  30. cbase/allocator_protocol/c_shm_comp.h +49 -0
  31. cbase/allocator_protocol/c_shm_comp.pxd +38 -0
  32. cbase/allocator_protocol/c_shm_comp.pyx +1 -0
  33. cbase/backports/__infra__.pxd +17 -0
  34. cbase/backports/__init__.pxd +17 -0
  35. cbase/backports/__init__.py +9 -0
  36. cbase/backports/pydict.h +55 -0
  37. cbase/backports/pydict.pxd +5 -0
  38. cbase/backports/pylong.c +9946 -0
  39. cbase/backports/pylong.cp313-win_amd64.pyd +0 -0
  40. cbase/backports/pylong.h +79 -0
  41. cbase/backports/pylong.pxd +23 -0
  42. cbase/backports/pylong.pyx +61 -0
  43. cbase/bytemap/__infra__.pxd +42 -0
  44. cbase/bytemap/__init__.pxd +42 -0
  45. cbase/bytemap/__init__.py +21 -0
  46. cbase/bytemap/c_bytemap.c +52076 -0
  47. cbase/bytemap/c_bytemap.cp313-win_amd64.pyd +0 -0
  48. cbase/bytemap/c_bytemap.h +867 -0
  49. cbase/bytemap/c_bytemap.pxd +370 -0
  50. cbase/bytemap/c_bytemap.pyx +1758 -0
  51. cbase/bytemap/xxh3.h +55 -0
  52. cbase/bytemap/xxhash.c +42 -0
  53. cbase/bytemap/xxhash.h +7487 -0
  54. cbase/env.c +11109 -0
  55. cbase/env.cp313-win_amd64.pyd +0 -0
  56. cbase/env.pxd +8 -0
  57. cbase/env.pyi +48 -0
  58. cbase/env.pyx +46 -0
  59. cbase/includes/cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
  60. cbase/includes/cbase/allocator_protocol/c_allocator_protocol.h +352 -0
  61. cbase/includes/cbase/allocator_protocol/c_heap_allocator.c +38144 -0
  62. cbase/includes/cbase/allocator_protocol/c_heap_allocator.h +444 -0
  63. cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
  64. cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
  65. cbase/includes/cbase/allocator_protocol/c_shm_allocator.h +1168 -0
  66. cbase/includes/cbase/allocator_protocol/c_shm_comp.c +4643 -0
  67. cbase/includes/cbase/allocator_protocol/c_shm_comp.h +49 -0
  68. cbase/includes/cbase/backports/pydict.h +55 -0
  69. cbase/includes/cbase/backports/pylong.c +9946 -0
  70. cbase/includes/cbase/backports/pylong.h +79 -0
  71. cbase/includes/cbase/bytemap/c_bytemap.c +52076 -0
  72. cbase/includes/cbase/bytemap/c_bytemap.h +867 -0
  73. cbase/includes/cbase/bytemap/xxh3.h +55 -0
  74. cbase/includes/cbase/bytemap/xxhash.c +42 -0
  75. cbase/includes/cbase/bytemap/xxhash.h +7487 -0
  76. cbase/includes/cbase/env.c +11109 -0
  77. cbase/includes/cbase/int128.h +81 -0
  78. cbase/includes/cbase/intern_string/c_intern_string.c +28081 -0
  79. cbase/includes/cbase/intern_string/c_intern_string.h +614 -0
  80. cbase/includes/cbase/nt/pthread_nt_compat.h +102 -0
  81. cbase/includes/cbase/nt/regex_nt_compat.h +133 -0
  82. cbase/includes/cbase/nt/time_sched_nt_compat.h +32 -0
  83. cbase/int128.h +81 -0
  84. cbase/int128.pxd +15 -0
  85. cbase/intern_string/BENCHMARK.md +157 -0
  86. cbase/intern_string/__infra__.pxd +11 -0
  87. cbase/intern_string/__init__.pxd +11 -0
  88. cbase/intern_string/__init__.py +19 -0
  89. cbase/intern_string/c_intern_string.c +28081 -0
  90. cbase/intern_string/c_intern_string.cp313-win_amd64.pyd +0 -0
  91. cbase/intern_string/c_intern_string.h +614 -0
  92. cbase/intern_string/c_intern_string.pxd +118 -0
  93. cbase/intern_string/c_intern_string.pyi +376 -0
  94. cbase/intern_string/c_intern_string.pyx +765 -0
  95. cbase/nt/pthread_nt_compat.h +102 -0
  96. cbase/nt/regex_nt_compat.h +133 -0
  97. cbase/nt/time_sched_nt_compat.h +32 -0
  98. pycybase-0.1.8.post4.dist-info/METADATA +180 -0
  99. pycybase-0.1.8.post4.dist-info/RECORD +101 -0
  100. pycybase-0.1.8.post4.dist-info/WHEEL +5 -0
  101. pycybase-0.1.8.post4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,614 @@
1
+ #ifndef C_CBASE_INTERN_STRING_H
2
+ #define C_CBASE_INTERN_STRING_H
3
+
4
+ /**
5
+ * c_intern_string.h -- Intern string pool.
6
+ *
7
+ * Ported from PyAlgoEngine's algo_engine/base/c_intern_string.h.
8
+ *
9
+ * Two backends, controlled by the ISTR_USE_BYTEMAP_BACKEND macro:
10
+ *
11
+ * Native (ISTR_USE_BYTEMAP_BACKEND == 0, default):
12
+ * Original open-addressing hash map with linear probing,
13
+ * power-of-two capacity, and internal pthread_mutex_t.
14
+ * istr_entry and istr_map are self-contained structs.
15
+ * Faithfully ported from PyAlgoEngine -- identical algorithm.
16
+ *
17
+ * Bytemap-backed (ISTR_USE_BYTEMAP_BACKEND == 1):
18
+ * istr_entry and istr_map are direct typedefs to bytemap_entry
19
+ * and bytemap -- zero wrapper overhead, one fewer dereference.
20
+ * Thread safety is managed at the Cython layer; _synced
21
+ * variants are pass-through stubs.
22
+ *
23
+ * Each backend defines its own struct layout behind the #if;
24
+ * only forward declarations are shared. Switch the macro and
25
+ * recompile to compare performance.
26
+ */
27
+
28
+ #include <stdint.h>
29
+ #include <stdlib.h>
30
+ #include <string.h>
31
+
32
+ #ifdef _WIN32
33
+ #include "cbase/nt/pthread_nt_compat.h"
34
+ #else
35
+ #include <pthread.h>
36
+ #endif
37
+
38
+ #include <cbase/allocator_protocol/c_allocator_protocol.h>
39
+
40
+ // ========== Configurable Macros ==========
41
+
42
+ #ifndef ISTR_USE_BYTEMAP_BACKEND
43
+ #define ISTR_USE_BYTEMAP_BACKEND 0
44
+ #endif
45
+
46
+ #if ISTR_USE_BYTEMAP_BACKEND
47
+ #include <cbase/bytemap/c_bytemap.h>
48
+ #endif
49
+
50
+ #ifndef FNV_OFFSET_BASIS
51
+ #define FNV_OFFSET_BASIS 14695981039346656037ULL
52
+ #endif
53
+
54
+ #ifndef FNV_PRIME
55
+ #define FNV_PRIME 1099511628211ULL
56
+ #endif
57
+
58
+ #ifndef ISTR_INITIAL_CAPACITY
59
+ #define ISTR_INITIAL_CAPACITY 4096
60
+ #endif
61
+
62
+ // ========== Structs (per-backend definitions) ==========
63
+
64
+ #if !ISTR_USE_BYTEMAP_BACKEND
65
+
66
+ typedef struct istr_entry {
67
+ const char* key;
68
+ size_t key_length;
69
+ uint64_t hash;
70
+ struct istr_entry* next;
71
+ } istr_entry;
72
+
73
+ typedef struct istr_map {
74
+ pthread_mutex_t lock;
75
+ istr_entry* table;
76
+ size_t capacity;
77
+ size_t size;
78
+ istr_entry* first;
79
+ } istr_map;
80
+
81
+ #else // ISTR_USE_BYTEMAP_BACKEND == 1
82
+
83
+ typedef bytemap_entry istr_entry;
84
+
85
+ typedef struct istr_map {
86
+ bytemap base;
87
+ pthread_mutex_t lock;
88
+ } istr_map;
89
+
90
+ #endif // ISTR_USE_BYTEMAP_BACKEND
91
+
92
+ typedef enum istr_ret_code {
93
+ ISTR_OK = 0,
94
+ ISTR_ERR_OOM = -1,
95
+ ISTR_ERR_INVALID_BUF = -2,
96
+ ISTR_ERR_INVALID_KEY = -3,
97
+ ISTR_ERR_NOT_FOUND = -4
98
+ } istr_ret_code;
99
+
100
+ // ========== Unified Accessors (abstract field layout differences) ==========
101
+
102
+ static inline size_t istr_map_size(const istr_map* map);
103
+ static inline istr_entry* istr_map_first(const istr_map* map);
104
+ static inline size_t istr_map_capacity(const istr_map* map);
105
+
106
+ // ========== Forward Declarations ==========
107
+
108
+ /*
109
+ * @brief FNV-1a hash function for strings.
110
+ * @param key Input string.
111
+ * @param key_length Length of the input string.
112
+ * @return 64-bit hash value.
113
+ */
114
+ static inline uint64_t fnv1a_hash(const char* key, size_t key_length);
115
+
116
+ /*
117
+ * @brief Round up n to the next power of two.
118
+ * @param n Input value.
119
+ * @return Smallest power of two >= n, minimum 1.
120
+ */
121
+ static inline size_t c_next_pow2(size_t n);
122
+
123
+ /*
124
+ * @brief Create a new interned string map.
125
+ * @param capacity Initial capacity of the map (0 for default).
126
+ * @param allocator Allocator protocol instance.
127
+ * @return Pointer to the newly created istr_map, or NULL on failure.
128
+ */
129
+ static inline istr_map* c_istr_map_new(size_t capacity, allocator_protocol* allocator);
130
+
131
+ /*
132
+ * @brief Free an interned string map and all its entries.
133
+ * @param map Pointer to the istr_map to free.
134
+ */
135
+ static inline void c_istr_map_free(istr_map* map);
136
+
137
+ /*
138
+ * @brief Extend the capacity of an interned string map (unlocked).
139
+ * @param map Pointer to the istr_map to extend.
140
+ * @param new_capacity New capacity (0 to auto double).
141
+ * @return 0 on success, -1 on failure.
142
+ */
143
+ static inline int c_istr_map_extend(istr_map* map, size_t new_capacity);
144
+
145
+ /*
146
+ * @brief Extend the capacity of an interned string map (locked).
147
+ * @param map Pointer to the istr_map to extend.
148
+ * @param new_capacity New capacity (0 to auto double).
149
+ * @return 0 on success, -1 on failure.
150
+ */
151
+ static inline int c_istr_map_extend_synced(istr_map* map, size_t new_capacity);
152
+
153
+ /*
154
+ * @brief Look up an interned string in the map (unlocked).
155
+ * @param map Pointer to the istr_map.
156
+ * @param key Input string to look up.
157
+ * @param key_length Length of key (0 to auto-detect via strlen).
158
+ * @return Pointer to the istr_entry if found, or NULL if not found.
159
+ */
160
+ static inline const istr_entry* c_istr_map_lookup(const istr_map* map, const char* key, size_t key_length);
161
+
162
+ /*
163
+ * @brief Look up an interned string in the map (locked).
164
+ */
165
+ static inline const istr_entry* c_istr_map_lookup_synced(const istr_map* map, const char* key, size_t key_length);
166
+
167
+ /*
168
+ * @brief Intern a string, returning a pointer to the internalized copy (unlocked).
169
+ * @param map Pointer to the istr_map.
170
+ * @param key Input string to intern.
171
+ * @param key_length Length of key (0 to auto-detect via strlen).
172
+ * @param out_entry If non-NULL, receives pointer to the entry.
173
+ * @return Pointer to the interned (owned) string, or NULL on failure.
174
+ */
175
+ static inline const char* c_istr(istr_map* map, const char* key, size_t key_length, const istr_entry** out_entry);
176
+
177
+ /*
178
+ * @brief Intern a string, returning a pointer to the internalized copy (locked).
179
+ */
180
+ static inline const char* c_istr_synced(istr_map* map, const char* key, size_t key_length, const istr_entry** out_entry);
181
+
182
+ // ========== Common Implementations ==========
183
+
184
+ static inline uint64_t fnv1a_hash(const char* key, size_t key_length) {
185
+ uint64_t hash = FNV_OFFSET_BASIS;
186
+ for (size_t i = 0; i < key_length; ++i) {
187
+ hash ^= (uint8_t) key[i];
188
+ hash *= FNV_PRIME;
189
+ }
190
+ return hash;
191
+ }
192
+
193
+ static inline size_t c_next_pow2(size_t n) {
194
+ if (n == 0) return 1;
195
+ n--;
196
+ n |= n >> 1;
197
+ n |= n >> 2;
198
+ #if __SIZEOF_SIZE_T__ >= 2
199
+ n |= n >> 8;
200
+ #endif
201
+ #if __SIZEOF_SIZE_T__ >= 4
202
+ n |= n >> 16;
203
+ #endif
204
+ #if __SIZEOF_SIZE_T__ >= 8
205
+ n |= n >> 32;
206
+ #endif
207
+ #if __SIZEOF_SIZE_T__ >= 16
208
+ n |= n >> 64;
209
+ #endif
210
+ n++;
211
+ return n;
212
+ }
213
+
214
+ // ==================================================================
215
+ // Backend: Native (original PyAlgoEngine, faithfully ported)
216
+ // ==================================================================
217
+
218
+ #if !ISTR_USE_BYTEMAP_BACKEND
219
+
220
+ // --- unified accessors ---
221
+ static inline size_t istr_map_size(const istr_map* map) { return map->size; }
222
+ static inline istr_entry* istr_map_first(const istr_map* map) { return map->first; }
223
+ static inline size_t istr_map_capacity(const istr_map* map) { return map->capacity; }
224
+
225
+ static inline istr_map* c_istr_map_new(size_t capacity, allocator_protocol* allocator) {
226
+ capacity = (capacity == 0) ? ISTR_INITIAL_CAPACITY : c_next_pow2(capacity);
227
+
228
+ istr_map* map = c_ap_alloc(sizeof(istr_map), allocator);
229
+ if (!map) return NULL;
230
+
231
+ istr_entry* table = c_ap_alloc(capacity * sizeof(istr_entry), allocator);
232
+ if (!table) {
233
+ c_ap_free(map);
234
+ return NULL;
235
+ }
236
+
237
+ if (pthread_mutex_init(&map->lock, NULL) != 0) {
238
+ c_ap_free(table);
239
+ c_ap_free(map);
240
+ return NULL;
241
+ }
242
+
243
+ map->capacity = capacity;
244
+ map->table = table;
245
+
246
+ return map;
247
+ }
248
+
249
+ static inline void c_istr_map_free(istr_map* map) {
250
+ if (!map) return;
251
+
252
+ // Free interned strings before releasing the pool
253
+ istr_entry* it = map->first;
254
+ while (it) {
255
+ if (it->key) {
256
+ c_ap_free((void*) it->key);
257
+ }
258
+ it = it->next;
259
+ }
260
+
261
+ pthread_mutex_destroy(&map->lock);
262
+
263
+ c_ap_free((void*) map->table);
264
+ c_ap_free((void*) map);
265
+ }
266
+
267
+ static inline int c_istr_map_extend(istr_map* map, size_t new_capacity) {
268
+ if (!map) return ISTR_ERR_INVALID_BUF;
269
+
270
+ // Step 1: Determine new capacity
271
+ if (!new_capacity) {
272
+ if (map->capacity >= SIZE_MAX / 2) return ISTR_ERR_OOM;
273
+ else if (map->capacity == 0) {
274
+ new_capacity = ISTR_INITIAL_CAPACITY;
275
+ }
276
+ else {
277
+ new_capacity = map->capacity * 2;
278
+ }
279
+ }
280
+ else {
281
+ new_capacity = c_next_pow2(new_capacity);
282
+ if (new_capacity <= map->capacity) return ISTR_ERR_INVALID_BUF;
283
+ }
284
+
285
+ allocator_protocol* allocator = c_ap_protocol_from_ptr(map);
286
+ istr_entry* new_table;
287
+
288
+ // Step 2: Allocate new pool
289
+ new_table = (istr_entry*) c_ap_alloc(new_capacity * sizeof(istr_entry), allocator);
290
+ if (!new_table) return ISTR_ERR_OOM;
291
+
292
+ // Step 3: Rehash existing entries into new pool
293
+ istr_entry* current = map->first;
294
+ map->first = NULL;
295
+ size_t mask = new_capacity - 1;
296
+
297
+ while (current) {
298
+ istr_entry* next = current->next;
299
+ uint64_t idx = current->hash & mask;
300
+ istr_entry* entry = new_table + idx;
301
+
302
+ // Linear probing for collision resolution
303
+ // There always should be at least one free slot
304
+ while (entry->key) {
305
+ if (++idx == new_capacity) idx = 0;
306
+ entry = new_table + idx;
307
+ }
308
+
309
+ entry->key = current->key;
310
+ entry->key_length = current->key_length;
311
+ entry->hash = current->hash;
312
+ entry->next = map->first;
313
+ map->first = entry;
314
+
315
+ current = next;
316
+ }
317
+
318
+ // Step 4: Free old pool and update map
319
+ c_ap_free((void*) map->table);
320
+
321
+ map->table = new_table;
322
+ map->capacity = new_capacity;
323
+ return ISTR_OK;
324
+ }
325
+
326
+ static inline int c_istr_map_extend_synced(istr_map* map, size_t new_capacity) {
327
+ if (!map) return ISTR_ERR_INVALID_BUF;
328
+ pthread_mutex_t* lock = &map->lock;
329
+ pthread_mutex_lock(lock);
330
+ int result = c_istr_map_extend(map, new_capacity);
331
+ pthread_mutex_unlock(lock);
332
+ return result;
333
+ }
334
+
335
+ static inline const istr_entry* c_istr_map_lookup(const istr_map* map, const char* key, size_t key_length) {
336
+ if (!map || !map->capacity || !key) return NULL;
337
+
338
+ // Step 0: Resolve key length
339
+ if (key_length == 0) key_length = strlen(key);
340
+
341
+ // Step 1: Compute hash and index
342
+ uint64_t hash = fnv1a_hash(key, key_length);
343
+ size_t capacity = map->capacity;
344
+ uint64_t idx = hash & (capacity - 1);
345
+ istr_entry* table = map->table;
346
+ istr_entry* entry = table + idx;
347
+
348
+ // Step 2: Search -- length shortcut + memcmp (no strcmp)
349
+ while (entry->key) {
350
+ if (entry->hash == hash && entry->key_length == key_length && memcmp(entry->key, key, key_length) == 0) {
351
+ return entry;
352
+ }
353
+ if (++idx == capacity) idx = 0;
354
+ entry = table + idx;
355
+ }
356
+
357
+ return NULL;
358
+ }
359
+
360
+ static inline const istr_entry* c_istr_map_lookup_synced(const istr_map* map, const char* key, size_t key_length) {
361
+ pthread_mutex_t* lock = (pthread_mutex_t*) &map->lock;
362
+ pthread_mutex_lock(lock);
363
+ const istr_entry* out = c_istr_map_lookup(map, key, key_length);
364
+ pthread_mutex_unlock(lock);
365
+ return out;
366
+ }
367
+
368
+ static inline const char* c_istr(istr_map* map, const char* key, size_t key_length, const istr_entry** out_entry) {
369
+ if (!map || !map->capacity || !key) return NULL;
370
+
371
+ // Step 0: Resolve key length
372
+ if (key_length == 0) key_length = strlen(key);
373
+
374
+ // Step 1: Compute hash
375
+ uint64_t hash = fnv1a_hash(key, key_length);
376
+
377
+ // Step 2: Check if key already exists
378
+ size_t capacity = map->capacity;
379
+ uint64_t idx = hash & (capacity - 1);
380
+ istr_entry* table = map->table;
381
+ istr_entry* entry = table + idx;
382
+
383
+ while (entry->key) {
384
+ if (entry->hash == hash && entry->key_length == key_length && memcmp(entry->key, key, key_length) == 0) {
385
+ if (out_entry) *out_entry = entry;
386
+ return entry->key;
387
+ }
388
+ if (++idx == capacity) idx = 0;
389
+ entry = table + idx;
390
+ }
391
+
392
+ // Step 3: Check capacity and extend if necessary
393
+ if (map->size >= capacity / 2) {
394
+ if (c_istr_map_extend(map, 0) != ISTR_OK) return NULL;
395
+ return c_istr(map, key, key_length, out_entry);
396
+ }
397
+
398
+ // Step 4: Duplicate key
399
+ char* interned_copy = NULL;
400
+ size_t total_size = key_length + 1;
401
+ allocator_protocol* allocator = c_ap_protocol_from_ptr(map);
402
+
403
+ interned_copy = (char*) c_ap_alloc(total_size, allocator);
404
+ if (!interned_copy) return NULL;
405
+ memcpy(interned_copy, key, total_size);
406
+
407
+ // Step 5: Insert new entry
408
+ entry->key = interned_copy;
409
+ entry->key_length = key_length;
410
+ entry->hash = hash;
411
+ entry->next = map->first;
412
+ map->first = entry;
413
+ map->size += 1;
414
+ if (out_entry) *out_entry = entry;
415
+ return interned_copy;
416
+ }
417
+
418
+ static inline const char* c_istr_synced(istr_map* map, const char* key, size_t key_length, const istr_entry** out_entry) {
419
+ pthread_mutex_t* lock = &map->lock;
420
+ pthread_mutex_lock(lock);
421
+ const char* out = c_istr(map, key, key_length, out_entry);
422
+ pthread_mutex_unlock(lock);
423
+ return out;
424
+ }
425
+
426
+ // ==================================================================
427
+ // Backend: Bytemap-backed
428
+ // -- istr_entry = bytemap_entry (direct typedef)
429
+ // -- istr_map wraps bytemap as first field + pthread_mutex_t,
430
+ // so (bytemap*)map == &map->base and _synced variants lock
431
+ // map->lock exactly like the native backend.
432
+ // ==================================================================
433
+
434
+ #else // ISTR_USE_BYTEMAP_BACKEND == 1
435
+
436
+ // --- unified accessors ---
437
+ static inline size_t istr_map_size(const istr_map* map) { return map->base.size; }
438
+ static inline istr_entry* istr_map_first(const istr_map* map) { return (istr_entry*) map->base.first; }
439
+ static inline size_t istr_map_capacity(const istr_map* map) { return map->base.capacity; }
440
+
441
+ // Single zero byte used as a placeholder value in bytemap slots.
442
+ // Intern strings only use the key; the 1-byte value is irrelevant.
443
+ static const char _ISTR_SENTINEL = 0;
444
+
445
+ // ------------------------------------------------------------------
446
+ // Map lifecycle
447
+ // ------------------------------------------------------------------
448
+
449
+ static inline istr_map* c_istr_map_new(size_t capacity, allocator_protocol* allocator) {
450
+ if (capacity == 0) capacity = ISTR_INITIAL_CAPACITY;
451
+
452
+ // Allocate the wrapper struct
453
+ istr_map* map = c_ap_alloc(sizeof(istr_map), allocator);
454
+ if (!map) return NULL;
455
+
456
+ // Initialize the embedded bytemap in-place
457
+ int ret = c_bytemap_ex_init(&map->base, capacity, 1, allocator);
458
+ if (ret != BYTEMAP_OK) {
459
+ c_ap_free(map);
460
+ return NULL;
461
+ }
462
+
463
+ if (pthread_mutex_init(&map->lock, NULL) != 0) {
464
+ c_bytemap_ex_dealloc(&map->base);
465
+ c_ap_free(map);
466
+ return NULL;
467
+ }
468
+
469
+ return map;
470
+ }
471
+
472
+ static inline void c_istr_map_free(istr_map* map) {
473
+ if (!map) return;
474
+
475
+ c_bytemap_ex_dealloc(&map->base);
476
+ pthread_mutex_destroy(&map->lock);
477
+ c_ap_free(map);
478
+ }
479
+
480
+ // ------------------------------------------------------------------
481
+ // Extend (mirrors native: determine capacity -> rehash, with lock)
482
+ // ------------------------------------------------------------------
483
+
484
+ static inline int c_istr_map_extend(istr_map* map, size_t new_capacity) {
485
+ if (!map) return ISTR_ERR_INVALID_BUF;
486
+
487
+ // Step 1: Determine new capacity (identical logic to native backend)
488
+ if (!new_capacity) {
489
+ if (map->base.capacity >= SIZE_MAX / 2) return ISTR_ERR_OOM;
490
+ else if (map->base.capacity == 0) {
491
+ new_capacity = ISTR_INITIAL_CAPACITY;
492
+ }
493
+ else {
494
+ new_capacity = map->base.capacity * 2;
495
+ }
496
+ }
497
+ else {
498
+ new_capacity = c_next_pow2(new_capacity);
499
+ if (new_capacity <= map->base.capacity) return ISTR_ERR_INVALID_BUF;
500
+ }
501
+
502
+ // Step 2: Rehash the underlying bytemap
503
+ int ret = c_bytemap_ex_rehash(&map->base, new_capacity, 0);
504
+ if (ret == BYTEMAP_ERR_OOM) return ISTR_ERR_OOM;
505
+ if (ret != BYTEMAP_OK) return ISTR_ERR_INVALID_BUF;
506
+ return ISTR_OK;
507
+ }
508
+
509
+ static inline int c_istr_map_extend_synced(istr_map* map, size_t new_capacity) {
510
+ if (!map) return ISTR_ERR_INVALID_BUF;
511
+
512
+ // Temporarily enable allocator-level locking so that c_ap_alloc /
513
+ // c_ap_free calls inside c_bytemap_ex_rehash are also threadsafe.
514
+ allocator_protocol* ap = c_ap_protocol_from_ptr(map);
515
+ bool saved_lock = ap->with_lock;
516
+ ap->with_lock = true;
517
+
518
+ pthread_mutex_lock(&map->lock);
519
+ int result = c_istr_map_extend(map, new_capacity);
520
+ pthread_mutex_unlock(&map->lock);
521
+
522
+ ap->with_lock = saved_lock;
523
+ return result;
524
+ }
525
+
526
+ // ------------------------------------------------------------------
527
+ // Lookup -- single linear probe, same structure as native.
528
+ // Uses bytemap's table layout and XXH3 hash (with per-map salt).
529
+ // ------------------------------------------------------------------
530
+
531
+ static inline const istr_entry* c_istr_map_lookup(const istr_map* map, const char* key, size_t key_length) {
532
+ if (!map || !map->base.capacity || !key) return NULL;
533
+
534
+ // Step 0: Resolve key length
535
+ if (key_length == 0) key_length = strlen(key);
536
+
537
+ // Step 1: Compute hash (bytemap uses XXH3 with per-map salt)
538
+ uint64_t hash;
539
+ c_bytemap_hash(&map->base, key, key_length, &hash);
540
+
541
+ // Step 2: Index into the open-addressing table
542
+ size_t capacity = map->base.capacity;
543
+ size_t idx = hash % capacity;
544
+ size_t start = idx;
545
+ istr_entry* entry = c_bytemap_entry_at(&map->base, idx);
546
+
547
+ // Step 3: Linear probe -- length shortcut + memcmp
548
+ while (entry->occupied || entry->removed) {
549
+ if (entry->occupied && entry->key_length == key_length && memcmp(entry->key, key, key_length) == 0) {
550
+ return entry;
551
+ }
552
+ idx = (idx + 1) % capacity;
553
+ if (idx == start) break;
554
+ entry = c_bytemap_entry_next(&map->base, entry);
555
+ }
556
+
557
+ return NULL;
558
+ }
559
+
560
+ static inline const istr_entry* c_istr_map_lookup_synced(const istr_map* map, const char* key, size_t key_length) {
561
+ if (!map) return NULL;
562
+ pthread_mutex_lock((pthread_mutex_t*) &map->lock);
563
+ const istr_entry* out = c_istr_map_lookup(map, key, key_length);
564
+ pthread_mutex_unlock((pthread_mutex_t*) &map->lock);
565
+ return out;
566
+ }
567
+
568
+ // ------------------------------------------------------------------
569
+ // Intern -- delegates to c_bytemap_ex_set which handles hash,
570
+ // linear probe, auto-rehash, key cloning, and insertion order.
571
+ // ------------------------------------------------------------------
572
+
573
+ static inline const char* c_istr(istr_map* map, const char* key, size_t key_length, const istr_entry** out_entry) {
574
+ if (!map || !map->base.capacity || !key) return NULL;
575
+
576
+ // Step 0: Resolve key length
577
+ if (key_length == 0) key_length = strlen(key);
578
+ if (key_length == 0) return NULL;
579
+
580
+ // Step 1: Insert or retrieve via bytemap.
581
+ istr_entry* entry = NULL;
582
+ int ret = c_bytemap_ex_set(
583
+ &map->base, key, key_length,
584
+ &_ISTR_SENTINEL, 1, // 1-byte sentinel value
585
+ 0, // seq_id (no callbacks)
586
+ &entry
587
+ );
588
+ if (ret != BYTEMAP_OK) return NULL;
589
+
590
+ // Step 2: Return results
591
+ if (out_entry) *out_entry = entry;
592
+ return entry->key;
593
+ }
594
+
595
+ static inline const char* c_istr_synced(istr_map* map, const char* key, size_t key_length, const istr_entry** out_entry) {
596
+ if (!map) return NULL;
597
+
598
+ // Temporarily enable allocator-level locking so that any c_ap_alloc
599
+ // calls inside c_bytemap_ex_set (key cloning, rehash) are also threadsafe.
600
+ allocator_protocol* ap = c_ap_protocol_from_ptr(map);
601
+ bool saved_lock = ap->with_lock;
602
+ ap->with_lock = true;
603
+
604
+ pthread_mutex_lock(&map->lock);
605
+ const char* out = c_istr(map, key, key_length, out_entry);
606
+ pthread_mutex_unlock(&map->lock);
607
+
608
+ ap->with_lock = saved_lock;
609
+ return out;
610
+ }
611
+
612
+ #endif // ISTR_USE_BYTEMAP_BACKEND
613
+
614
+ #endif // C_CBASE_INTERN_STRING_H
@@ -0,0 +1,102 @@
1
+ #ifndef PTHREAD_NT_COMPAT_H
2
+ #define PTHREAD_NT_COMPAT_H
3
+
4
+ #ifndef _WIN32
5
+ #error "pthread_nt_compat.h is intended for Windows builds only"
6
+ #endif
7
+
8
+ #include <errno.h>
9
+ #include <limits.h>
10
+ #include <time.h>
11
+
12
+ #ifndef WIN32_LEAN_AND_MEAN
13
+ #define WIN32_LEAN_AND_MEAN
14
+ #endif
15
+ #include <windows.h>
16
+
17
+ typedef CRITICAL_SECTION pthread_mutex_t;
18
+ typedef void pthread_mutexattr_t;
19
+ typedef CONDITION_VARIABLE pthread_cond_t;
20
+ typedef void pthread_condattr_t;
21
+
22
+ static inline DWORD pthread_nt_timeout_ms(const struct timespec* abstime) {
23
+ if (!abstime) return INFINITE;
24
+
25
+ struct timespec now;
26
+ if (timespec_get(&now, TIME_UTC) != TIME_UTC) return 0;
27
+
28
+ time_t sec = abstime->tv_sec - now.tv_sec;
29
+ long nsec = abstime->tv_nsec - now.tv_nsec;
30
+ if (nsec < 0) { sec -= 1; nsec += 1000000000L; }
31
+ if (sec < 0 || (sec == 0 && nsec <= 0)) return 0;
32
+
33
+ unsigned long long ms = (unsigned long long) sec * 1000ULL;
34
+ ms += (unsigned long long) ((nsec + 999999L) / 1000000L);
35
+ if (ms > (unsigned long long) INFINITE - 1ULL) return INFINITE - 1;
36
+ return (DWORD) ms;
37
+ }
38
+
39
+ static inline int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) {
40
+ (void) attr;
41
+ if (!mutex) return EINVAL;
42
+ InitializeCriticalSection(mutex);
43
+ return 0;
44
+ }
45
+
46
+ static inline int pthread_mutex_lock(pthread_mutex_t* mutex) {
47
+ if (!mutex) return EINVAL;
48
+ EnterCriticalSection(mutex);
49
+ return 0;
50
+ }
51
+
52
+ static inline int pthread_mutex_unlock(pthread_mutex_t* mutex) {
53
+ if (!mutex) return EINVAL;
54
+ LeaveCriticalSection(mutex);
55
+ return 0;
56
+ }
57
+
58
+ static inline int pthread_mutex_destroy(pthread_mutex_t* mutex) {
59
+ if (!mutex) return EINVAL;
60
+ DeleteCriticalSection(mutex);
61
+ return 0;
62
+ }
63
+
64
+ static inline int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr) {
65
+ (void) attr;
66
+ if (!cond) return EINVAL;
67
+ InitializeConditionVariable(cond);
68
+ return 0;
69
+ }
70
+
71
+ static inline int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
72
+ if (!cond || !mutex) return EINVAL;
73
+ if (SleepConditionVariableCS(cond, mutex, INFINITE)) return 0;
74
+ return EINVAL;
75
+ }
76
+
77
+ static inline int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime) {
78
+ if (!cond || !mutex) return EINVAL;
79
+ DWORD timeout_ms = pthread_nt_timeout_ms(abstime);
80
+ if (SleepConditionVariableCS(cond, mutex, timeout_ms)) return 0;
81
+ if (GetLastError() == ERROR_TIMEOUT) return ETIMEDOUT;
82
+ return EINVAL;
83
+ }
84
+
85
+ static inline int pthread_cond_signal(pthread_cond_t* cond) {
86
+ if (!cond) return EINVAL;
87
+ WakeConditionVariable(cond);
88
+ return 0;
89
+ }
90
+
91
+ static inline int pthread_cond_broadcast(pthread_cond_t* cond) {
92
+ if (!cond) return EINVAL;
93
+ WakeAllConditionVariable(cond);
94
+ return 0;
95
+ }
96
+
97
+ static inline int pthread_cond_destroy(pthread_cond_t* cond) {
98
+ (void) cond;
99
+ return 0;
100
+ }
101
+
102
+ #endif /* PTHREAD_NT_COMPAT_H */