ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/TDB_FileX/TDB_FileX.xs
Revision: 1.1
Committed: Fri Apr 25 16:43:02 2025 UTC (16 months, 2 weeks ago) by root
Branch: MAIN
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     static SV *custom_hash_cb[4];
129    
130     static unsigned int custom_hash_1 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[0]); }
131     static unsigned int custom_hash_2 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[1]); }
132     static unsigned int custom_hash_3 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[2]); }
133     static unsigned int custom_hash_4 (TDB_DATA *key) { return custom_hash (key, custom_hash_cb[3]); }
134    
135     typedef int mone_on_fail;
136    
137     MODULE = TDB_FileX PACKAGE = TDB_FileX PREFIX = tdb_
138    
139     PROTOTYPES: DISABLE
140    
141     BOOT:
142     {
143     HV *stash = gv_stashpv ("TDB_FileX", 1);
144    
145     static const struct {
146     const char *name;
147     IV iv;
148     } *civ, const_iv[] = {
149     # define const_iv(name) { # name, (IV) TDB_ ## name },
150     const_iv (ALLOW_NESTING)
151     const_iv (BIGENDIAN)
152     const_iv (CLEAR_IF_FIRST)
153     const_iv (CONVERT)
154     const_iv (DEFAULT)
155     const_iv (DISALLOW_NESTING)
156     const_iv (INCOMPATIBLE_HASH)
157     const_iv (INSERT)
158     const_iv (INTERNAL)
159     const_iv (MODIFY)
160     const_iv (MUTEX_LOCKING)
161     const_iv (NOLOCK)
162     const_iv (NOMMAP)
163     const_iv (NOSYNC)
164     const_iv (REPLACE)
165     const_iv (SEQNUM)
166     const_iv (VOLATILE)
167    
168     const_iv (ERR_CORRUPT)
169     const_iv (ERR_IO)
170     const_iv (ERR_LOCK)
171     const_iv (ERR_OOM)
172     const_iv (ERR_EXISTS)
173     const_iv (ERR_NOLOCK)
174     const_iv (ERR_LOCK_TIMEOUT)
175     const_iv (ERR_NOEXIST)
176     const_iv (ERR_EINVAL)
177     const_iv (ERR_RDONLY)
178     const_iv (SUCCESS)
179    
180     const_iv (DEBUG_FATAL)
181     const_iv (DEBUG_ERROR)
182     const_iv (DEBUG_WARNING)
183     const_iv (DEBUG_TRACE)
184     };
185    
186     for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); civ > const_iv; civ--)
187     newCONSTSUB (stash, (char *)civ[-1].name, newSViv (civ[-1].iv));
188    
189     tdb_runtime_check_for_robust_mutexes ();
190     }
191    
192     SV *
193     set_hash_function (int idx, SV *cb = 0)
194     PROTOTYPE: $;$
195     CODE:
196     {
197     if (idx < 1 || idx > 4)
198     croak ("hash function index must be between 1 and 4");
199    
200     SV **cbp = &custom_hash_cb[idx - 1];
201    
202     RETVAL = *cbp ? newSVsv (*cbp) : &PL_sv_undef;
203    
204     if (cb)
205     {
206     SvREFCNT_dec (*cbp);
207     *cbp = SvOK (cb) ? newSVsv (cb) : 0;
208     }
209     }
210     OUTPUT: RETVAL
211    
212     void
213     DESTROY (TDB_CONTEXT *tdb)
214     CODE:
215     if (tdb)
216     {
217     SvREFCNT_dec ((SV *)tdb_get_logging_private (tdb));
218     tdb_close (tdb);
219     }
220    
221     mone_on_fail
222     tdb_delete (TDB_CONTEXT *tdb, TDB_DATA key)
223     ALIAS:
224     DELETE = 0
225    
226     mone_on_fail
227     tdb_wipe_all (TDB_CONTEXT *tdb)
228     ALIAS:
229     CLEAR = 0
230     CODE:
231     tdb_wipe_all (tdb);
232    
233     void
234     tdb_dump_all (TDB_CONTEXT *tdb)
235    
236     enum TDB_ERROR
237     tdb_error (TDB_CONTEXT *tdb)
238    
239     const char *
240     tdb_errorstr (TDB_CONTEXT *tdb)
241    
242     int
243     tdb_exists (TDB_CONTEXT *tdb, TDB_DATA key)
244     ALIAS:
245     EXISTS = 0
246    
247     TDB_DATA
248     tdb_fetch (TDB_CONTEXT *tdb, TDB_DATA key)
249     ALIAS:
250     FETCH = 0
251    
252     mone_on_fail
253     tdb_append (TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
254    
255     TDB_DATA
256     tdb_firstkey (TDB_CONTEXT *tdb)
257     ALIAS:
258     FIRSTKEY = 0
259    
260     mone_on_fail tdb_lockall (TDB_CONTEXT *tdb)
261    
262     mone_on_fail tdb_lockall_nonblock (TDB_CONTEXT *tdb)
263    
264     mone_on_fail tdb_unlockall (TDB_CONTEXT *tdb)
265    
266     mone_on_fail tdb_lockall_read (TDB_CONTEXT *tdb)
267    
268     mone_on_fail tdb_lockall_read_nonblock (TDB_CONTEXT *tdb)
269    
270     mone_on_fail tdb_unlockall_read (TDB_CONTEXT *tdb)
271    
272     mone_on_fail tdb_lockall_mark (TDB_CONTEXT *tdb)
273    
274     mone_on_fail tdb_lockall_unmark (TDB_CONTEXT *tdb)
275    
276     mone_on_fail tdb_transaction_start (TDB_CONTEXT *tdb)
277    
278     mone_on_fail tdb_transaction_start_nonblock (TDB_CONTEXT *tdb)
279    
280     mone_on_fail tdb_transaction_cancel (TDB_CONTEXT *tdb)
281    
282     mone_on_fail tdb_transaction_commit (TDB_CONTEXT *tdb)
283    
284     mone_on_fail tdb_transaction_prepare_commit (TDB_CONTEXT *tdb)
285    
286     bool tdb_transaction_active (TDB_CONTEXT *tdb)
287    
288     void tdb_enable_seqnum (TDB_CONTEXT *tdb)
289    
290     int tdb_get_seqnum (TDB_CONTEXT *tdb)
291    
292     void tdb_increment_seqnum_nonblock (TDB_CONTEXT *tdb)
293    
294     int tdb_hash_size (TDB_CONTEXT *tdb)
295    
296     size_t tdb_map_size (TDB_CONTEXT *tdb)
297    
298     int tdb_get_flags (TDB_CONTEXT *tdb)
299    
300     void tdb_add_flags (TDB_CONTEXT *tdb, unsigned int flag)
301    
302     void tdb_remove_flags (TDB_CONTEXT *tdb, unsigned int flag)
303    
304     mone_on_fail tdb_repack (TDB_CONTEXT *tdb)
305    
306     bool tdb_runtime_check_for_robust_mutexes ()
307    
308     void
309     tdb_set_logging_function (TDB_CONTEXT *tdb, SV *cb)
310     CODE:
311     struct tdb_logging_context ctx;
312     ctx.log_fn = log_func_cb;
313     ctx.log_private = (SV *)newSVsv (cb);
314     SvREFCNT_dec ((SV *)tdb_get_logging_private (tdb));
315     tdb_set_logging_function (tdb, &ctx);
316    
317     TDB_DATA
318     tdb_nextkey (TDB_CONTEXT *tdb, TDB_DATA key)
319     ALIAS:
320     NEXTKEY = 0
321    
322     TDB_CONTEXT *
323     tdb_open (char *class, char *path, ...)
324     ALIAS:
325     TIEHASH = 0
326     CODE:
327     tdb_hash_func hash_func = 0;
328     struct tdb_logging_context ctx = { 0 };
329     int tdb_flags = TDB_DEFAULT;
330     int open_flags = O_RDWR | O_CREAT;
331     mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
332     int hash_size = 0;
333    
334     for (int i = 2; i < items - 1; i += 2)
335     {
336     const char *k = SvPVbyte_nolen (ST (i));
337     SV *v = ST (i + 1);
338    
339     if (strEQ (k, "tdb_flags" )) tdb_flags = SvIV (v);
340     else if (strEQ (k, "open_flags")) open_flags = SvIV (v);
341     else if (strEQ (k, "mode" )) mode = SvUV (v);
342     else if (strEQ (k, "hash_size" )) hash_size = SvIV (v);
343     else if (strEQ (k, "log_cb"))
344     {
345     SvREFCNT_dec (ctx.log_private);
346     ctx.log_fn = 0;
347     ctx.log_private = 0;
348    
349     if (SvOK (v))
350     {
351     ctx.log_fn = log_func_cb;
352     ctx.log_private = (void *)newSVsv (v);
353     }
354     }
355     else if (strEQ (k, "hash"))
356     {
357     const char *f = SvPVbyte_nolen (v);
358    
359     if (!SvOK (v) ) hash_func = 0;
360     else if (strEQ (f, "default")) hash_func = 0;
361     else if (strEQ (f, "jenkins")) hash_func = tdb_jenkins_hash;
362     else if (strEQ (f, "fnv1ax" )) hash_func = fnv1ax_hash;
363     else if (strEQ (f, "xxh3" )) hash_func = xxh3_hash;
364     else if (strEQ (f, "1" )) hash_func = custom_hash_1;
365     else if (strEQ (f, "2" )) hash_func = custom_hash_2;
366     else if (strEQ (f, "3" )) hash_func = custom_hash_3;
367     else if (strEQ (f, "4" )) hash_func = custom_hash_4;
368     else
369     croak ("%s: not a known hash function", f);
370     }
371     else if (strEQ (k, "mutex"))
372     {
373     if (SvTRUE (v))
374     {
375     if (tdb_runtime_check_for_robust_mutexes ())
376     tdb_flags |= TDB_MUTEX_LOCKING;
377     }
378     else
379     tdb_flags &= ~TDB_MUTEX_LOCKING;
380     }
381     else
382     croak ("%s: not a known parameter name", k);
383     }
384    
385     RETVAL = tdb_open_ex (path, hash_size, tdb_flags, open_flags, mode, ctx.log_fn ? &ctx : 0, hash_func);
386    
387     if (!RETVAL)
388     {
389     SvREFCNT_dec ((SV *)ctx.log_private);
390     XSRETURN_UNDEF;
391     }
392     OUTPUT: RETVAL
393    
394     void
395     tdb_printfreelist (TDB_CONTEXT *tdb)
396    
397     mone_on_fail
398     tdb_reopen (TDB_CONTEXT *tdb)
399     PROTOTYPE:
400     CODE:
401     void *logcb = tdb_get_logging_private (tdb);
402    
403     RETVAL = tdb_reopen (tdb);
404    
405     if (RETVAL < 0)
406     {
407     /* tdb_reopen frees the TDB_CONTEXT on failure,
408     * so set scalar value to 0 to avoid double free on DESTROY */
409     sv_setiv ((SV*)SvRV (ST (0)), 0);
410     SvREFCNT_dec ((SV *)logcb);
411     }
412     OUTPUT: RETVAL
413    
414     # FIXME: if this fails, we need to undef $tdb or something
415     # .. which we can't do - cos we don't know where it failed :(
416     # maybe reimplement this ourselves?
417     mone_on_fail
418     tdb_reopen_all (int parent_longlived = 0)
419    
420     int
421     tdb_fd (TDB_CONTEXT *tdb)
422    
423     const char *
424     tdb_name (TDB_CONTEXT *tdb)
425    
426     mone_on_fail
427     tdb_store (TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag = TDB_REPLACE)
428     ALIAS:
429     STORE = 0
430    
431     int
432     tdb_traverse (TDB_CONTEXT *tdb, SV *fn = &PL_sv_undef)
433     ALIAS:
434     tdb_traverse_read = 1
435     CODE:
436     {
437     if (SvOK (fn))
438     RETVAL = (ix ? tdb_traverse_read : tdb_traverse) (tdb, traverse_cb, fn);
439     else
440     RETVAL = (ix ? tdb_traverse_read : tdb_traverse) (tdb, NULL, NULL);
441    
442     if (RETVAL < 0)
443     XSRETURN_UNDEF;
444     }
445     OUTPUT: RETVAL
446