File Coverage

MCDB_File.xs
Criterion Covered Total %
statement 83 96 86.4
branch 72 132 54.5
condition n/a
subroutine n/a
pod n/a
total 155 228 67.9


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) 2011, Glue Logic LLC. All rights reserved. code()gluelogic.com
3             *
4             * This library is free software; you can redistribute it and/or modify
5             * it under the same terms as Perl itself, either Perl version 5.12.4 or,
6             * at your option, any later version of Perl 5 you may have available.
7             */
8              
9             #include "EXTERN.h"
10             #include "perl.h"
11             #include "XSUB.h"
12              
13             #include
14             #include
15             #include
16             #include
17             #include
18              
19             #include
20             #include
21             #include
22              
23             struct mcdbxs_read {
24             struct mcdb m;
25             struct mcdb_iter iter;
26             bool values; /* flag for values() processing */
27             };
28              
29             inline
30             static SV *
31 122           mcdbxs_svcp (const unsigned char * const restrict p, const STRLEN len)
32             {
33             /* newSVpvn() allocates len+1 and '\0'-terminates string in returned SV */
34 122           return newSVpvn((const char *)p, len);
35             }
36              
37             static bool
38 52           mcdbxs_is_iterkey (struct mcdb_iter * const restrict iter,
39             const char * const restrict kp, const STRLEN klen)
40             {
41 52           return (iter->ptr
42 43 50         && klen == mcdb_iter_keylen(iter)
43 43 50         && 0 == memcmp(mcdb_iter_keyptr(iter), kp, klen))
44 95 100         || ((iter->ptr = NULL), false);
45             }
46              
47             static SV *
48 61           mcdbxs_nextkey (struct mcdbxs_read * const restrict this)
49             {
50 61           struct mcdb_iter * const restrict iter = &this->iter;
51             /* ? sometimes NEXTKEY gets called before FIRSTKEY if hash gets re-tied ? */
52 61 100         if (!iter->ptr)
53 10           mcdb_iter_init(iter, &this->m);
54 122           return mcdb_iter(iter)
55 53           ? mcdbxs_svcp(mcdb_iter_keyptr(iter), mcdb_iter_keylen(iter))
56 61 100         : (SV *)(iter->ptr = NULL);
57             }
58              
59             static void *
60 26           mcdbxs_malloc (size_t sz)
61             {
62             void * restrict x;
63 26           return Newx(x, sz, char);
64             }
65              
66             static void
67 26           mcdbxs_free (void *p)
68             {
69 26           Safefree(p);
70 26           }
71              
72             static void
73 9           mcdbxs_make_destroy (struct mcdb_make * const restrict mk)
74             {
75 9           mcdb_make_destroy(mk);
76 9           mcdb_makefn_cleanup(mk);
77 9           Safefree(mk);
78 9           }
79              
80             #define mcdbxs_madvise(this,advice) mcdb_mmap_madvise((this)->m.map,(advice))
81              
82              
83             MODULE = MCDB_File PACKAGE = MCDB_File PREFIX = mcdbxs_
84              
85             PROTOTYPES: DISABLED
86              
87             struct mcdbxs_read *
88             mcdbxs_TIEHASH(CLASS, filename)
89             char * CLASS;
90             char * filename;
91             CODE:
92 10           Newx(RETVAL, 1, struct mcdbxs_read);
93 10           RETVAL->m.map = RETVAL->iter.map =
94 10           mcdb_mmap_create(NULL, NULL, filename, mcdbxs_malloc, mcdbxs_free);
95 10 100         if (RETVAL->iter.map) {
96 9           RETVAL->iter.ptr = NULL;
97 9           RETVAL->iter.eod = NULL;
98 9           RETVAL->values = false;
99             }
100             else {
101 1           Safefree(RETVAL);
102 1           XSRETURN_NO;
103             }
104             OUTPUT:
105             RETVAL
106              
107             U32
108             mcdbxs_SCALAR(this)
109             struct mcdbxs_read * this;
110             CODE:
111 0           RETVAL = mcdb_numrecs(&this->m);
112             OUTPUT:
113             RETVAL
114              
115             int
116             mcdbxs_EXISTS(this, k)
117             struct mcdbxs_read * this;
118             SV * k;
119             PREINIT:
120             STRLEN klen;
121             char *kp;
122             INIT:
123 6 100         if (!SvOK(k)) XSRETURN_NO;
    50          
    50          
124             CODE:
125 5 50         kp = SvPV(k, klen);
126 5 100         RETVAL = mcdb_find(&this->m, kp, klen);
    50          
127             OUTPUT:
128             RETVAL
129              
130             SV *
131             mcdbxs_find(this, k)
132             struct mcdbxs_read * this;
133             SV * k;
134             PREINIT:
135             STRLEN klen;
136             char *kp;
137             INIT:
138 0 0         if (!SvOK(k)) XSRETURN_UNDEF;
    0          
    0          
139             CODE:
140 0 0         kp = SvPV(k, klen);
141 0 0         if (mcdb_find(&this->m, kp, klen))
    0          
142 0           RETVAL = mcdbxs_svcp(mcdb_dataptr(&this->m), mcdb_datalen(&this->m));
143             else
144 0           XSRETURN_UNDEF;
145             OUTPUT:
146             RETVAL
147              
148             SV *
149             mcdbxs_FETCH(this, k)
150             struct mcdbxs_read * this;
151             SV * k;
152             PREINIT:
153             STRLEN klen;
154             char *kp;
155             INIT:
156 53 100         if (!SvOK(k)) XSRETURN_UNDEF;
    50          
    50          
157             CODE:
158 52 50         kp = SvPV(k, klen);
159 52 100         if (mcdbxs_is_iterkey(&this->iter, kp, klen)) {
160 43           RETVAL = mcdbxs_svcp(mcdb_iter_dataptr(&this->iter),
161 43           mcdb_iter_datalen(&this->iter));
162 43 100         if (this->values && !mcdb_iter(&this->iter)) {
    100          
163 43           this->values = false; this->iter.ptr = this->iter.eod = NULL;
164             }
165             }
166 9 100         else if (mcdb_find(&this->m, kp, klen)) {
    50          
167 8           RETVAL = mcdbxs_svcp(mcdb_dataptr(&this->m), mcdb_datalen(&this->m));
168             /* messiness required to detect Perl calling FETCH for values()
169             * after having obtained all FIRSTKEY/NEXTKEY; needed to support
170             * (duplicated) keys with multiple values, permitted in mcdb */
171 10 100         if (this->iter.eod && MCDB_HEADER_SZ == mcdb_datapos(&this->m)-klen-8) {
    100          
172 2           this->values = true;
173 2           mcdb_iter_init(&this->iter, &this->m);
174 2 50         if (!mcdb_iter(&this->iter) || !mcdb_iter(&this->iter)) {
    50          
175 0           this->values = false; this->iter.ptr = this->iter.eod = NULL;
176             }
177             }
178             }
179             else
180 1           XSRETURN_UNDEF;
181             OUTPUT:
182             RETVAL
183              
184             SV *
185             mcdbxs_FIRSTKEY(this)
186             struct mcdbxs_read * this;
187             CODE:
188 9           this->iter.ptr = NULL; /* should already be NULL */
189 9           RETVAL = mcdbxs_nextkey(this);
190 9 100         if (!RETVAL) XSRETURN_UNDEF;/* empty database */
191             OUTPUT:
192             RETVAL
193              
194             SV *
195             mcdbxs_NEXTKEY(this, k)
196             struct mcdbxs_read * this;
197             SV * k;
198             INIT:
199 52 50         if (!SvOK(k)) XSRETURN_UNDEF;
    0          
    0          
200             CODE:
201 52           RETVAL = mcdbxs_nextkey(this);
202 52 100         if (!RETVAL) XSRETURN_UNDEF;
203             OUTPUT:
204             RETVAL
205              
206             void
207             mcdbxs_DESTROY(sv)
208             SV * sv;
209             PREINIT:
210             struct mcdbxs_read *this;
211             CODE:
212 9 50         if (sv_isobject(sv) && SvTYPE(SvRV(sv)) == SVt_PVMG) {
    50          
213 9 50         this = INT2PTR(struct mcdbxs_read *, SvIV((SV *)SvRV(sv)));
214 9           mcdb_mmap_destroy(this->m.map);
215 9           Safefree(this);
216             }
217              
218             AV *
219             mcdbxs_multi_get(this, k)
220             struct mcdbxs_read * this;
221             SV * k;
222             PREINIT:
223             STRLEN klen;
224             char *kp;
225             INIT:
226 12 50         if (!SvOK(k)) XSRETURN_UNDEF;
    0          
    0          
227             CODE:
228 12 100         kp = SvPV(k, klen);
229 12           RETVAL = newAV(); /* might be redundant (see .c generated from .xs) */
230 12           sv_2mortal((SV *)RETVAL);
231 12 100         if (mcdb_findstart(&this->m, kp, klen))
232 29 100         while (mcdb_findnext(&this->m, kp, klen))
233 18           av_push(RETVAL,
234             mcdbxs_svcp(mcdb_dataptr(&this->m),mcdb_datalen(&this->m)));
235             OUTPUT:
236             RETVAL
237              
238             void
239             mcdbxs_madvise(this, advice)
240             struct mcdbxs_read * this;
241             int advice;
242              
243              
244             MODULE = MCDB_File PACKAGE = MCDB_File::Make PREFIX = mcdbxs_make_
245              
246             # /*(MCDB_FILE::Make::insert instead of STORE to support multi-insert)*/
247             # /*(mcdb supports multiple records with same key, so a STORE method might be
248             # * misleading if caller incorrectly assumes value replaced for repeated key)*/
249             void
250             mcdbxs_make_insert(mk, ...)
251             struct mcdb_make * mk;
252             PREINIT:
253             SV *k, *v;
254             char *kp, *vp;
255             STRLEN klen, vlen;
256             int x;
257             PPCODE:
258 62 100         for (x = 1; x+1 < items; x += 2) {
259 31           k = ST(x); v = ST(x+1);
260 31 50         if (SvOK(k) && SvOK(v)) {
    0          
    0          
    50          
    0          
    0          
261 31 100         kp = SvPV(k, klen); vp = SvPV(v, vlen);
    100          
262 31 50         if (mcdb_make_add(mk, kp, klen, vp, vlen) != 0)
263 0           croak("MCDB_File::Make::insert: %s", Strerror(errno));
264             }
265             else
266 0           croak("MCDB_File::Make::insert: invalid argument");
267             }
268              
269             struct mcdb_make *
270             mcdbxs_make_new(CLASS, fname, ...)
271             char * CLASS;
272             char * fname;
273             PREINIT:
274             struct mcdb_make *mk;
275             CODE:
276 9           RETVAL = Newx(mk, 1, struct mcdb_make);
277 9 100         if (mcdb_makefn_start(mk, fname, mcdbxs_malloc, mcdbxs_free) == 0
278 8 50         && mcdb_make_start(mk, mk->fd, mcdbxs_malloc, mcdbxs_free) == 0) {
279 8 50         if (items >= 3)
280 0 0         mk->st_mode = SvIV(ST(2)); /* optional mcdb perm mode */
281             }
282             else {
283 1           mcdbxs_make_destroy(mk);
284 1           XSRETURN_UNDEF;
285             }
286             OUTPUT:
287             RETVAL
288              
289             void
290             mcdbxs_make_DESTROY(sv)
291             SV * sv;
292             CODE:
293 8 50         if (sv_isobject(sv) && SvTYPE(SvRV(sv)) == SVt_PVMG)
    50          
294 8 50         mcdbxs_make_destroy(INT2PTR(struct mcdb_make *,SvIV((SV *)SvRV(sv))));
295              
296             void
297             mcdbxs_make_finish(mk, ...)
298             struct mcdb_make * mk;
299             PREINIT:
300 8           bool do_fsync = true;
301             CODE:
302 8 50         if (items >= 2)
303 0 0         do_fsync = SvIV(ST(1)) != 0; /* optional control fsync */
304 8 50         if (mcdb_make_finish(mk) != 0 || mcdb_makefn_finish(mk, do_fsync) != 0) {
    50          
305             /*mcdb_make_destroy(mk);*//* already called in mcdb_make_finish() */
306 0           mcdb_makefn_cleanup(mk);
307 0           croak("MCDB_File::Make::finish: %s", Strerror(errno));
308             }