ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/TDB_FileX/TDB_FileX.xs
Revision: 1.2
Committed: Tue Apr 29 21:27:54 2025 UTC (16 months, 2 weeks ago) by root
Branch: MAIN
Changes since 1.1: +58 -50 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     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 root 1.2 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 root 1.1 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 root 1.2 NO_OUTPUT int
231 root 1.1 tdb_delete (TDB_CONTEXT *tdb, TDB_DATA key)
232     ALIAS:
233     DELETE = 0
234 root 1.2 POSTCALL:
235     croak_on_error (RETVAL);
236 root 1.1
237 root 1.2 NO_OUTPUT int
238 root 1.1 tdb_wipe_all (TDB_CONTEXT *tdb)
239     ALIAS:
240     CLEAR = 0
241     CODE:
242     tdb_wipe_all (tdb);
243 root 1.2 POSTCALL:
244     croak_on_error (RETVAL);
245 root 1.1
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 root 1.2 SV *
256 root 1.1 tdb_exists (TDB_CONTEXT *tdb, TDB_DATA key)
257     ALIAS:
258     EXISTS = 0
259 root 1.2 CODE:
260     RETVAL = tdb_exists (tdb, key) ? &PL_sv_yes : &PL_sv_no;
261     OUTPUT: RETVAL
262 root 1.1
263     TDB_DATA
264     tdb_fetch (TDB_CONTEXT *tdb, TDB_DATA key)
265     ALIAS:
266     FETCH = 0
267    
268 root 1.2 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 root 1.1 tdb_append (TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
277 root 1.2 POSTCALL:
278     croak_on_error (RETVAL);
279 root 1.1
280     TDB_DATA
281     tdb_firstkey (TDB_CONTEXT *tdb)
282     ALIAS:
283     FIRSTKEY = 0
284    
285 root 1.2 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 root 1.1
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 root 1.2 void
415 root 1.1 tdb_reopen (TDB_CONTEXT *tdb)
416     PROTOTYPE:
417     CODE:
418     void *logcb = tdb_get_logging_private (tdb);
419    
420 root 1.2 int res = tdb_reopen (tdb);
421 root 1.1
422 root 1.2 if (res < 0)
423 root 1.1 {
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 root 1.2 croak ("tdb_reopen failed");
429 root 1.1 }
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 root 1.2 NO_OUTPUT int
435 root 1.1 tdb_reopen_all (int parent_longlived = 0)
436 root 1.2 POSTCALL:
437     if (RETVAL < 0)
438     croak ("tdb_reopen_all failed");
439 root 1.1
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 root 1.2 RETVAL = (ix ? tdb_traverse_read : tdb_traverse) (tdb, SvOK (fn) ? traverse_cb: 0, fn);
452     croak_on_error (RETVAL);
453 root 1.1 OUTPUT: RETVAL
454