File Coverage

MCDB_File.xs
Criterion Covered Total %
statement 86 99 86.8
branch 72 132 54.5
condition n/a
subroutine n/a
pod n/a
total 158 231 68.4


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             static SV *
30 122           mcdbxs_svcp (const unsigned char * const restrict p, const STRLEN len)
31             {
32             /* copy and append '\0'-termination */
33 122           SV * const restrict sv = newSVpvn((const char *)p, len+1);
34 122           SvCUR_set(sv, len);
35 122           (SvPVX(sv))[len] = '\0';
36 122           return sv;
37             }
38              
39             static bool
40 52           mcdbxs_is_iterkey (struct mcdb_iter * const restrict iter,
41             const char * const restrict kp, const STRLEN klen)
42             {
43 52           return (iter->ptr
44 43 50         && klen == mcdb_iter_keylen(iter)
45 43 50         && 0 == memcmp(mcdb_iter_keyptr(iter), kp, klen))
46 95 100         || ((iter->ptr = NULL), false);
47             }
48              
49             static SV *
50 61           mcdbxs_nextkey (struct mcdbxs_read * const restrict this)
51             {
52 61           struct mcdb_iter * const restrict iter = &this->iter;
53             /* ? sometimes NEXTKEY gets called before FIRSTKEY if hash gets re-tied ? */
54 61 100         if (!iter->ptr)
55 10           mcdb_iter_init(iter, &this->m);
56 122           return mcdb_iter(iter)
57 53           ? mcdbxs_svcp(mcdb_iter_keyptr(iter), mcdb_iter_keylen(iter))
58 61 100         : (SV *)(iter->ptr = NULL);
59             }
60              
61             static void *
62 26           mcdbxs_malloc (size_t sz)
63             {
64             void * restrict x;
65 26           return Newx(x, sz, char);
66             }
67              
68             static void
69 26           mcdbxs_free (void *p)
70             {
71 26           Safefree(p);
72 26           }
73              
74             static void
75 9           mcdbxs_make_destroy (struct mcdb_make * const restrict mk)
76             {
77 9           mcdb_make_destroy(mk);
78 9           mcdb_makefn_cleanup(mk);
79 9           Safefree(mk);
80 9           }
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              
239             MODULE = MCDB_File PACKAGE = MCDB_File::Make PREFIX = mcdbxs_make_
240              
241             # /*(MCDB_FILE::Make::insert instead of STORE to support multi-insert)*/
242             # /*(mcdb supports multiple records with same key, so a STORE method might be
243             # * misleading if caller incorrectly assumes value replaced for repeated key)*/
244             void
245             mcdbxs_make_insert(mk, ...)
246             struct mcdb_make * mk;
247             PREINIT:
248             SV *k, *v;
249             char *kp, *vp;
250             STRLEN klen, vlen;
251             int x;
252             PPCODE:
253 62 100         for (x = 1; x+1 < items; x += 2) {
254 31           k = ST(x); v = ST(x+1);
255 31 50         if (SvOK(k) && SvOK(v)) {
    0          
    0          
    50          
    0          
    0          
256 31 100         kp = SvPV(k, klen); vp = SvPV(v, vlen);
    100          
257 31 50         if (mcdb_make_add(mk, kp, klen, vp, vlen) != 0)
258 0           croak("MCDB_File::Make::insert: %s", Strerror(errno));
259             }
260             else
261 0           croak("MCDB_File::Make::insert: invalid argument");
262             }
263              
264             struct mcdb_make *
265             mcdbxs_make_new(CLASS, fname, ...)
266             char * CLASS;
267             char * fname;
268             PREINIT:
269             struct mcdb_make *mk;
270             CODE:
271 9           RETVAL = Newx(mk, 1, struct mcdb_make);
272 9 100         if (mcdb_makefn_start(mk, fname, mcdbxs_malloc, mcdbxs_free) == 0
273 8 50         && mcdb_make_start(mk, mk->fd, mcdbxs_malloc, mcdbxs_free) == 0) {
274 8 50         if (items >= 3)
275 0 0         mk->st_mode = SvIV(ST(2)); /* optional mcdb perm mode */
276             }
277             else {
278 1           mcdbxs_make_destroy(mk);
279 1           XSRETURN_UNDEF;
280             }
281             OUTPUT:
282             RETVAL
283              
284             void
285             mcdbxs_make_DESTROY(sv)
286             SV * sv;
287             CODE:
288 8 50         if (sv_isobject(sv) && SvTYPE(SvRV(sv)) == SVt_PVMG)
    50          
289 8 50         mcdbxs_make_destroy(INT2PTR(struct mcdb_make *,SvIV((SV *)SvRV(sv))));
290              
291             void
292             mcdbxs_make_finish(mk, ...)
293             struct mcdb_make * mk;
294             PREINIT:
295 8           bool do_fsync = true;
296             CODE:
297 8 50         if (items >= 2)
298 0 0         do_fsync = SvIV(ST(1)) != 0; /* optional control fsync */
299 8 50         if (mcdb_make_finish(mk) != 0 || mcdb_makefn_finish(mk, do_fsync) != 0) {
    50          
300             /*mcdb_make_destroy(mk);*//* already called in mcdb_make_finish() */
301 0           mcdb_makefn_cleanup(mk);
302 0           croak("MCDB_File::Make::finish: %s", Strerror(errno));
303             }