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.
- cbase/__infra__.pxd +6 -0
- cbase/__init__.pxd +6 -0
- cbase/__init__.py +28 -0
- cbase/allocator_protocol/__infra__.pxd +58 -0
- cbase/allocator_protocol/__init__.pxd +58 -0
- cbase/allocator_protocol/__init__.py +45 -0
- cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
- cbase/allocator_protocol/c_allocator_protocol.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_allocator_protocol.h +352 -0
- cbase/allocator_protocol/c_allocator_protocol.pxd +64 -0
- cbase/allocator_protocol/c_allocator_protocol.pyi +144 -0
- cbase/allocator_protocol/c_allocator_protocol.pyx +209 -0
- cbase/allocator_protocol/c_heap_allocator.c +38144 -0
- cbase/allocator_protocol/c_heap_allocator.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_heap_allocator.h +444 -0
- cbase/allocator_protocol/c_heap_allocator.pxd +97 -0
- cbase/allocator_protocol/c_heap_allocator.pyi +161 -0
- cbase/allocator_protocol/c_heap_allocator.pyx +300 -0
- cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
- cbase/allocator_protocol/c_nt_shm_allocator.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
- cbase/allocator_protocol/c_nt_shm_allocator.pxd +102 -0
- cbase/allocator_protocol/c_nt_shm_allocator.pyx +223 -0
- cbase/allocator_protocol/c_shm_allocator.h +1168 -0
- cbase/allocator_protocol/c_shm_allocator.pxd +142 -0
- cbase/allocator_protocol/c_shm_allocator.pyi +299 -0
- cbase/allocator_protocol/c_shm_allocator.pyx +442 -0
- cbase/allocator_protocol/c_shm_comp.c +4643 -0
- cbase/allocator_protocol/c_shm_comp.cp313-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_shm_comp.h +49 -0
- cbase/allocator_protocol/c_shm_comp.pxd +38 -0
- cbase/allocator_protocol/c_shm_comp.pyx +1 -0
- cbase/backports/__infra__.pxd +17 -0
- cbase/backports/__init__.pxd +17 -0
- cbase/backports/__init__.py +9 -0
- cbase/backports/pydict.h +55 -0
- cbase/backports/pydict.pxd +5 -0
- cbase/backports/pylong.c +9946 -0
- cbase/backports/pylong.cp313-win_amd64.pyd +0 -0
- cbase/backports/pylong.h +79 -0
- cbase/backports/pylong.pxd +23 -0
- cbase/backports/pylong.pyx +61 -0
- cbase/bytemap/__infra__.pxd +42 -0
- cbase/bytemap/__init__.pxd +42 -0
- cbase/bytemap/__init__.py +21 -0
- cbase/bytemap/c_bytemap.c +52076 -0
- cbase/bytemap/c_bytemap.cp313-win_amd64.pyd +0 -0
- cbase/bytemap/c_bytemap.h +867 -0
- cbase/bytemap/c_bytemap.pxd +370 -0
- cbase/bytemap/c_bytemap.pyx +1758 -0
- cbase/bytemap/xxh3.h +55 -0
- cbase/bytemap/xxhash.c +42 -0
- cbase/bytemap/xxhash.h +7487 -0
- cbase/env.c +11109 -0
- cbase/env.cp313-win_amd64.pyd +0 -0
- cbase/env.pxd +8 -0
- cbase/env.pyi +48 -0
- cbase/env.pyx +46 -0
- cbase/includes/cbase/allocator_protocol/c_allocator_protocol.c +31818 -0
- cbase/includes/cbase/allocator_protocol/c_allocator_protocol.h +352 -0
- cbase/includes/cbase/allocator_protocol/c_heap_allocator.c +38144 -0
- cbase/includes/cbase/allocator_protocol/c_heap_allocator.h +444 -0
- cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.c +33399 -0
- cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.h +996 -0
- cbase/includes/cbase/allocator_protocol/c_shm_allocator.h +1168 -0
- cbase/includes/cbase/allocator_protocol/c_shm_comp.c +4643 -0
- cbase/includes/cbase/allocator_protocol/c_shm_comp.h +49 -0
- cbase/includes/cbase/backports/pydict.h +55 -0
- cbase/includes/cbase/backports/pylong.c +9946 -0
- cbase/includes/cbase/backports/pylong.h +79 -0
- cbase/includes/cbase/bytemap/c_bytemap.c +52076 -0
- cbase/includes/cbase/bytemap/c_bytemap.h +867 -0
- cbase/includes/cbase/bytemap/xxh3.h +55 -0
- cbase/includes/cbase/bytemap/xxhash.c +42 -0
- cbase/includes/cbase/bytemap/xxhash.h +7487 -0
- cbase/includes/cbase/env.c +11109 -0
- cbase/includes/cbase/int128.h +81 -0
- cbase/includes/cbase/intern_string/c_intern_string.c +28081 -0
- cbase/includes/cbase/intern_string/c_intern_string.h +614 -0
- cbase/includes/cbase/nt/pthread_nt_compat.h +102 -0
- cbase/includes/cbase/nt/regex_nt_compat.h +133 -0
- cbase/includes/cbase/nt/time_sched_nt_compat.h +32 -0
- cbase/int128.h +81 -0
- cbase/int128.pxd +15 -0
- cbase/intern_string/BENCHMARK.md +157 -0
- cbase/intern_string/__infra__.pxd +11 -0
- cbase/intern_string/__init__.pxd +11 -0
- cbase/intern_string/__init__.py +19 -0
- cbase/intern_string/c_intern_string.c +28081 -0
- cbase/intern_string/c_intern_string.cp313-win_amd64.pyd +0 -0
- cbase/intern_string/c_intern_string.h +614 -0
- cbase/intern_string/c_intern_string.pxd +118 -0
- cbase/intern_string/c_intern_string.pyi +376 -0
- cbase/intern_string/c_intern_string.pyx +765 -0
- cbase/nt/pthread_nt_compat.h +102 -0
- cbase/nt/regex_nt_compat.h +133 -0
- cbase/nt/time_sched_nt_compat.h +32 -0
- pycybase-0.1.8.post4.dist-info/METADATA +180 -0
- pycybase-0.1.8.post4.dist-info/RECORD +101 -0
- pycybase-0.1.8.post4.dist-info/WHEEL +5 -0
- 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 */
|