ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/TDB_FileX/TDB_FileX.xs
Revision: 1.3
Committed: Tue Apr 29 21:56:12 2025 UTC (16 months, 2 weeks ago) by root
Branch: MAIN
Changes since 1.2: +64 -5 lines
Log Message:
*** empty log message ***

File Contents

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