ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/TDB_FileX/TDB_FileX.xs
Revision: 1.2
Committed: Tue Apr 29 21:27:54 2025 UTC (17 months ago) by root
Branch: MAIN
Changes since 1.1: +58 -50 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 #include <sys/types.h>
6 #include <tdb.h>
7
8 #define XXH_STATIC_LINKING_ONLY
9 #define XXH_IMPLEMENTATION
10 #define XXH_INLINE_ALL
11 #include "xxHash/xxhash.h"
12
13 static void
14 log_func_cb (TDB_CONTEXT *tdb, enum tdb_debug_level level, const char *fmt, ...)
15 {
16 va_list ap;
17 bool xfalse = FALSE;
18 SV *cb = (SV *)tdb_get_logging_private (tdb);
19
20 if (!cb)
21 return;
22
23 dSP;
24 dXSTARG;
25
26 ENTER;
27 SAVETMPS;
28
29 va_start (ap, fmt);
30
31 SV *sv = sv_newmortal ();
32 sv_vsetpvfn (sv, fmt, strlen (fmt), &ap, NULL, 0, &xfalse);
33
34 va_end (ap);
35
36 PUSHMARK (SP);
37 XPUSHi (level);
38 XPUSHs (sv);
39 PUTBACK;
40
41 call_sv (cb, G_VOID | G_DISCARD);
42
43 FREETMPS;
44 LEAVE;
45 }
46
47 static int
48 traverse_cb (TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *status)
49 {
50 dSP;
51
52 ENTER;
53 SAVETMPS;
54
55 PUSHMARK (SP);
56 XPUSHs (sv_2mortal (newSVpv (key .dptr, key .dsize)));
57 XPUSHs (sv_2mortal (newSVpv (data.dptr, data.dsize)));
58 PUTBACK;
59
60 int count = call_sv ((SV *)status, G_SCALAR);
61
62 SPAGAIN;
63
64 if (count != 1)
65 croak ("tdb_traverse callback returned %d args\n", count);
66
67 SV *retval = POPs;
68 int ret = !SvTRUE (retval);
69
70 PUTBACK;
71 FREETMPS;
72 LEAVE;
73
74 return ret;
75 }
76
77 // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
78 // https://github.com/skeeto/hash-prospector/issues/19
79 static unsigned int
80 fnv1ax_hash (TDB_DATA *key)
81 {
82 unsigned int x = sizeof (unsigned int) < 8 ? 0x811c9dc5U : 0xcbf29ce484222325U;
83
84 for (size_t i = 0; i < key->dsize; ++i)
85 x = (x ^ key->dptr[i]) * (sizeof (unsigned int) < 8 ? 0x01000193U : 0x00000100000001b3U);
86
87 x ^= x >> 16; x *= 0x21f0aaadU;
88 x ^= x >> 15; x *= 0x735a2d97U;
89 x ^= x >> 15;
90
91 return x;
92 }
93
94 static unsigned int
95 xxh3_hash (TDB_DATA *key)
96 {
97 return XXH3_64bits (key->dptr, key->dsize);
98 }
99
100 static unsigned int
101 custom_hash (TDB_DATA *key, SV *cb)
102 {
103 dSP;
104
105 ENTER;
106 SAVETMPS;
107
108 PUSHMARK (SP);
109 XPUSHs (sv_2mortal (newSVpv (key->dptr, key->dsize)));
110 PUTBACK;
111
112 int count = call_sv (cb, G_SCALAR);
113
114 SPAGAIN;
115
116 if (count != 1)
117 croak ("hash callback returned %d vaslues, expecte4d exactly 1", count);
118
119 unsigned int hash = POPu;
120
121 PUTBACK;
122 FREETMPS;
123 LEAVE;
124
125 return hash;
126 }
127
128 static void
129 croak_on_error_slowpath (TDB_CONTEXT *tdb)
130 {
131 errno = tdb_error (tdb);
132 croak_sv (sv_2mortal (newSVpv (tdb_errorstr (tdb), 0)));
133 }
134
135 #define croak_on_error(res) do { if ((res) < 0) croak_on_error_slowpath (tdb); } while (0)
136
137 static SV *custom_hash_cb[4];
138
139 static unsigned int custom_hash_1 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[0]); }
140 static unsigned int custom_hash_2 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[1]); }
141 static unsigned int custom_hash_3 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[2]); }
142 static unsigned int custom_hash_4 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[3]); }
143
144 typedef int mone_on_fail;
145
146 MODULE = TDB_FileX PACKAGE = TDB_FileX PREFIX = tdb_
147
148 PROTOTYPES: DISABLE
149
150 BOOT:
151 {
152 HV *stash = gv_stashpv ("TDB_FileX", 1);
153
154 static const struct {
155 const char *name;
156 IV iv;
157 } *civ, const_iv[] = {
158 # define const_iv(name) { # name, (IV) TDB_ ## name },
159 const_iv (ALLOW_NESTING)
160 const_iv (BIGENDIAN)
161 const_iv (CLEAR_IF_FIRST)
162 const_iv (CONVERT)
163 const_iv (DEFAULT)
164 const_iv (DISALLOW_NESTING)
165 const_iv (INCOMPATIBLE_HASH)
166 const_iv (INSERT)
167 const_iv (INTERNAL)
168 const_iv (MODIFY)
169 const_iv (MUTEX_LOCKING)
170 const_iv (NOLOCK)
171 const_iv (NOMMAP)
172 const_iv (NOSYNC)
173 const_iv (REPLACE)
174 const_iv (SEQNUM)
175 const_iv (VOLATILE)
176
177 const_iv (ERR_CORRUPT)
178 const_iv (ERR_IO)
179 const_iv (ERR_LOCK)
180 const_iv (ERR_OOM)
181 const_iv (ERR_EXISTS)
182 const_iv (ERR_NOLOCK)
183 const_iv (ERR_LOCK_TIMEOUT)
184 const_iv (ERR_NOEXIST)
185 const_iv (ERR_EINVAL)
186 const_iv (ERR_RDONLY)
187 const_iv (SUCCESS)
188
189 const_iv (DEBUG_FATAL)
190 const_iv (DEBUG_ERROR)
191 const_iv (DEBUG_WARNING)
192 const_iv (DEBUG_TRACE)
193 };
194
195 for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); civ > const_iv; civ--)
196 newCONSTSUB (stash, (char *)civ[-1].name, newSViv (civ[-1].iv));
197
198 tdb_runtime_check_for_robust_mutexes ();
199 }
200
201 SV *
202 set_hash_function (int idx, SV *cb = 0)
203 PROTOTYPE: $;$
204 CODE:
205 {
206 if (idx < 1 || idx > 4)
207 croak ("hash function index must be between 1 and 4");
208
209 SV **cbp = &custom_hash_cb[idx - 1];
210
211 RETVAL = *cbp ? newSVsv (*cbp) : &PL_sv_undef;
212
213 if (cb)
214 {
215 SvREFCNT_dec (*cbp);
216 *cbp = SvOK (cb) ? newSVsv (cb) : 0;
217 }
218 }
219 OUTPUT: RETVAL
220
221 void
222 DESTROY (TDB_CONTEXT *tdb)
223 CODE:
224 if (tdb)
225 {
226 SvREFCNT_dec ((SV *)tdb_get_logging_private (tdb));
227 tdb_close (tdb);
228 }
229
230 NO_OUTPUT int
231 tdb_delete (TDB_CONTEXT *tdb, TDB_DATA key)
232 ALIAS:
233 DELETE = 0
234 POSTCALL:
235 croak_on_error (RETVAL);
236
237 NO_OUTPUT int
238 tdb_wipe_all (TDB_CONTEXT *tdb)
239 ALIAS:
240 CLEAR = 0
241 CODE:
242 tdb_wipe_all (tdb);
243 POSTCALL:
244 croak_on_error (RETVAL);
245
246 void
247 tdb_dump_all (TDB_CONTEXT *tdb)
248
249 enum TDB_ERROR
250 tdb_error (TDB_CONTEXT *tdb)
251
252 const char *
253 tdb_errorstr (TDB_CONTEXT *tdb)
254
255 SV *
256 tdb_exists (TDB_CONTEXT *tdb, TDB_DATA key)
257 ALIAS:
258 EXISTS = 0
259 CODE:
260 RETVAL = tdb_exists (tdb, key) ? &PL_sv_yes : &PL_sv_no;
261 OUTPUT: RETVAL
262
263 TDB_DATA
264 tdb_fetch (TDB_CONTEXT *tdb, TDB_DATA key)
265 ALIAS:
266 FETCH = 0
267
268 NO_OUTPUT int
269 tdb_store (TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag = TDB_REPLACE)
270 ALIAS:
271 STORE = 0
272 POSTCALL:
273 croak_on_error (RETVAL);
274
275 NO_OUTPUT int
276 tdb_append (TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
277 POSTCALL:
278 croak_on_error (RETVAL);
279
280 TDB_DATA
281 tdb_firstkey (TDB_CONTEXT *tdb)
282 ALIAS:
283 FIRSTKEY = 0
284
285 NO_OUTPUT int
286 tdb_context_only_and_mone_on_fail (TDB_CONTEXT *tdb)
287 INTERFACE:
288 tdb_lockall
289 tdb_lockall_nonblock
290 tdb_unlockall
291 tdb_lockall_read
292 tdb_lockall_read_nonblock
293 tdb_unlockall_read
294 tdb_lockall_mark
295 tdb_lockall_unmark
296 tdb_transaction_start
297 tdb_transaction_start_nonblock
298 tdb_transaction_cancel
299 tdb_transaction_commit
300 tdb_transaction_prepare_commit
301 tdb_repack
302 POSTCALL:
303 croak_on_error (RETVAL);
304
305 bool tdb_transaction_active (TDB_CONTEXT *tdb)
306
307 void tdb_enable_seqnum (TDB_CONTEXT *tdb)
308
309 int tdb_get_seqnum (TDB_CONTEXT *tdb)
310
311 void tdb_increment_seqnum_nonblock (TDB_CONTEXT *tdb)
312
313 int tdb_hash_size (TDB_CONTEXT *tdb)
314
315 size_t tdb_map_size (TDB_CONTEXT *tdb)
316
317 int tdb_get_flags (TDB_CONTEXT *tdb)
318
319 void tdb_add_flags (TDB_CONTEXT *tdb, unsigned int flag)
320
321 void tdb_remove_flags (TDB_CONTEXT *tdb, unsigned int flag)
322
323 bool tdb_runtime_check_for_robust_mutexes ()
324
325 void
326 tdb_set_logging_function (TDB_CONTEXT *tdb, SV *cb)
327 CODE:
328 struct tdb_logging_context ctx;
329 ctx.log_fn = log_func_cb;
330 ctx.log_private = (SV *)newSVsv (cb);
331 SvREFCNT_dec ((SV *)tdb_get_logging_private (tdb));
332 tdb_set_logging_function (tdb, &ctx);
333
334 TDB_DATA
335 tdb_nextkey (TDB_CONTEXT *tdb, TDB_DATA key)
336 ALIAS:
337 NEXTKEY = 0
338
339 TDB_CONTEXT *
340 tdb_open (char *class, char *path, ...)
341 ALIAS:
342 TIEHASH = 0
343 CODE:
344 tdb_hash_func hash_func = 0;
345 struct tdb_logging_context ctx = { 0 };
346 int tdb_flags = TDB_DEFAULT;
347 int open_flags = O_RDWR | O_CREAT;
348 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
349 int hash_size = 0;
350
351 for (int i = 2; i < items - 1; i += 2)
352 {
353 const char *k = SvPVbyte_nolen (ST (i));
354 SV *v = ST (i + 1);
355
356 if (strEQ (k, "tdb_flags" )) tdb_flags = SvIV (v);
357 else if (strEQ (k, "open_flags")) open_flags = SvIV (v);
358 else if (strEQ (k, "mode" )) mode = SvUV (v);
359 else if (strEQ (k, "hash_size" )) hash_size = SvIV (v);
360 else if (strEQ (k, "log_cb"))
361 {
362 SvREFCNT_dec (ctx.log_private);
363 ctx.log_fn = 0;
364 ctx.log_private = 0;
365
366 if (SvOK (v))
367 {
368 ctx.log_fn = log_func_cb;
369 ctx.log_private = (void *)newSVsv (v);
370 }
371 }
372 else if (strEQ (k, "hash"))
373 {
374 const char *f = SvPVbyte_nolen (v);
375
376 if (!SvOK (v) ) hash_func = 0;
377 else if (strEQ (f, "default")) hash_func = 0;
378 else if (strEQ (f, "jenkins")) hash_func = tdb_jenkins_hash;
379 else if (strEQ (f, "fnv1ax" )) hash_func = fnv1ax_hash;
380 else if (strEQ (f, "xxh3" )) hash_func = xxh3_hash;
381 else if (strEQ (f, "1" )) hash_func = custom_hash_1;
382 else if (strEQ (f, "2" )) hash_func = custom_hash_2;
383 else if (strEQ (f, "3" )) hash_func = custom_hash_3;
384 else if (strEQ (f, "4" )) hash_func = custom_hash_4;
385 else
386 croak ("%s: not a known hash function", f);
387 }
388 else if (strEQ (k, "mutex"))
389 {
390 if (SvTRUE (v))
391 {
392 if (tdb_runtime_check_for_robust_mutexes ())
393 tdb_flags |= TDB_MUTEX_LOCKING;
394 }
395 else
396 tdb_flags &= ~TDB_MUTEX_LOCKING;
397 }
398 else
399 croak ("%s: not a known parameter name", k);
400 }
401
402 RETVAL = tdb_open_ex (path, hash_size, tdb_flags, open_flags, mode, ctx.log_fn ? &ctx : 0, hash_func);
403
404 if (!RETVAL)
405 {
406 SvREFCNT_dec ((SV *)ctx.log_private);
407 XSRETURN_UNDEF;
408 }
409 OUTPUT: RETVAL
410
411 void
412 tdb_printfreelist (TDB_CONTEXT *tdb)
413
414 void
415 tdb_reopen (TDB_CONTEXT *tdb)
416 PROTOTYPE:
417 CODE:
418 void *logcb = tdb_get_logging_private (tdb);
419
420 int res = tdb_reopen (tdb);
421
422 if (res < 0)
423 {
424 /* tdb_reopen frees the TDB_CONTEXT on failure,
425 * so set scalar value to 0 to avoid double free on DESTROY */
426 sv_setiv ((SV*)SvRV (ST (0)), 0);
427 SvREFCNT_dec ((SV *)logcb);
428 croak ("tdb_reopen failed");
429 }
430
431 # FIXME: if this fails, we need to undef $tdb or something
432 # .. which we can't do - cos we don't know where it failed :(
433 # maybe reimplement this ourselves?
434 NO_OUTPUT int
435 tdb_reopen_all (int parent_longlived = 0)
436 POSTCALL:
437 if (RETVAL < 0)
438 croak ("tdb_reopen_all failed");
439
440 int
441 tdb_fd (TDB_CONTEXT *tdb)
442
443 const char *
444 tdb_name (TDB_CONTEXT *tdb)
445
446 int
447 tdb_traverse (TDB_CONTEXT *tdb, SV *fn = &PL_sv_undef)
448 ALIAS:
449 tdb_traverse_read = 1
450 CODE:
451 RETVAL = (ix ? tdb_traverse_read : tdb_traverse) (tdb, SvOK (fn) ? traverse_cb: 0, fn);
452 croak_on_error (RETVAL);
453 OUTPUT: RETVAL
454