File Coverage

CDB_File.xs
Criterion Covered Total %
statement 366 424 86.3
branch 191 318 60.0
condition n/a
subroutine n/a
pod n/a
total 557 742 75.0


line stmt bran cond sub pod time code
1             /*
2              
3             Most of this is reasonably straightforward. The complications arise
4             when we are "iterating" over the CDB file, that is to say, using `keys'
5             or `values' or `each' to retrieve all the data in the file in order.
6             This interface stores extra data to allow us to track iterations: end
7             is a pointer to the end of data in the CDB file, and also a flag which
8             indicates whether we are iterating or not (note that the end of data
9             occurs at a position >= 2048); curkey is a copy of the current key;
10             curpos is the file offset of curkey; and fetch_advance is 0 for
11              
12             FIRSTKEY, fetch, NEXTKEY, fetch, NEXTKEY, fetch, ...
13              
14             but 1 for
15              
16             FIRSTKEY, NEXTKEY, NEXTKEY, ..., fetch, fetch, fetch, ...
17              
18             Don't tell the OO Police, but there are actually two different objects
19             called CDB_File. One is created by TIEHASH, and accessed by the usual
20             tied hash methods (FETCH, FIRSTKEY, etc.). The other is created by new,
21             and accessed by insert and finish.
22              
23             In both cases, the object is a blessed reference to a scalar. The
24             scalar contains either a struct cdbobj or a struct cdbmakeobj.
25              
26             It gets a little messy in DESTROY: since this method will automatically
27             be called for both sorts of object, it distinguishes them by their
28             different sizes.
29              
30             */
31              
32             #ifdef __cplusplus
33             extern "C" {
34             #endif
35              
36             #include "EXTERN.h"
37             #include "perl.h"
38             #include "XSUB.h"
39             #include "ppport.h"
40              
41             #include
42             #include
43             #include
44             #include
45             #include
46             #include
47              
48             #ifdef WIN32
49             #define fsync _commit
50             #endif
51              
52             #ifdef HASMMAP
53             #include
54             #endif
55              
56             /* We need to whistle up an error number for a file that is not a CDB
57             file. The BSDish EFTYPE probably gives the most useful error message;
58             failing that we'll settle for the Single Unix Specification v2 EPROTO;
59             and finally the rather inappropriate, but universally(?) implemented,
60             EINVAL. */
61             #ifdef EFTYPE
62             #else
63             #ifdef EPROTO
64             #define EFTYPE EPROTO
65             #else
66             #define EFTYPE EINVAL
67             #endif
68             #endif
69              
70             #ifdef __cplusplus
71             }
72             #endif
73              
74             #if defined(SV_COW_REFCNT_MAX)
75             # define CDB_CAN_COW 1
76             #else
77             # define CDB_CAN_COW 0
78             #endif
79              
80             #if CDB_CAN_COW
81             # define CDB_DO_COW(sv) STMT_START { SvIsCOW_on(sv); CowREFCNT(sv) = 1; } STMT_END
82             #else
83             # define CDB_DO_COW(sv)
84             #endif
85              
86             #define cdb_datapos(c) ((c)->dpos)
87             #define cdb_datalen(c) ((c)->dlen)
88              
89             #define SET_FINDER_LEN(s, l) STMT_START { s.len = l; s.hash = 0; } STMT_END
90              
91             struct t_string_finder {
92             char *pv;
93             STRLEN len;
94             bool is_utf8;
95             bool pv_needs_free;
96             U32 hash;
97             };
98             typedef struct t_string_finder string_finder;
99              
100             struct t_cdb {
101             PerlIO *fh; /* */
102              
103             #ifdef HASMMAP
104             char *map;
105             #endif
106              
107             U32 end; /* If non zero, the file offset of the first byte of hash tables. */
108             bool is_utf8; /* will we be reading in utf8 encoded data? If so we'll set SvUTF8 = true; */
109             string_finder curkey; /* While iterating: the current key; */
110             STRLEN curkey_allocated;
111             U32 curpos; /* the file offset of the current record. */
112             int fetch_advance; /* the kludge */
113             U32 size; /* initialized if map is nonzero */
114             U32 loop; /* number of hash slots searched under this key */
115             U32 khash; /* initialized if loop is nonzero */
116             U32 kpos; /* initialized if loop is nonzero */
117             U32 hpos; /* initialized if loop is nonzero */
118             U32 hslots; /* initialized if loop is nonzero */
119             U32 dpos; /* initialized if cdb_findnext() returns 1 */
120             U32 dlen; /* initialized if cdb_findnext() returns 1 */
121             };
122              
123             typedef struct t_cdb cdb;
124              
125             #define CDB_HPLIST 1000
126              
127             struct cdb_hp { U32 h; U32 p; };
128              
129             struct cdb_hplist {
130             struct cdb_hp hp[CDB_HPLIST];
131             struct cdb_hplist *next;
132             int num;
133             };
134              
135             struct t_cdb_make {
136             PerlIO *f; /* Handle of file being created. */
137             bool is_utf8; /* Coerce the PV to utf8 before writing out the data? */
138             char *fn; /* Final name of file. */
139             char *fntemp; /* Temporary name of file. */
140             char final[2048];
141             char bspace[1024];
142             U32 count[256];
143             U32 start[256];
144             struct cdb_hplist *head;
145             struct cdb_hp *split; /* includes space for hash */
146             struct cdb_hp *hash;
147             U32 numentries;
148             U32 pos;
149             int fd;
150             };
151              
152             typedef struct t_cdb_make cdb_make;
153              
154             static int cdb_read(cdb *c, char *buf, unsigned int len, U32 pos);
155              
156 0           static void writeerror() { croak("Write to CDB_File failed: %s", Strerror(errno)); }
157              
158 2           static void readerror() { croak("Read of CDB_File failed: %s", Strerror(errno)); }
159              
160 0           static void nomem() { croak("Out of memory!"); }
161              
162 292           static inline SV * sv_from_datapos(cdb *c, STRLEN len) {
163             SV *sv;
164             char *buf;
165              
166 292           sv = newSV(len + 1 + CDB_CAN_COW);
167 292           SvPOK_on(sv);
168 292           CDB_DO_COW(sv);
169 292 50         if(c->is_utf8)
170 0           SvUTF8_on(sv);
171 292           buf = SvPVX(sv);
172 292 50         if (cdb_read(c, buf, len, cdb_datapos(c)) == -1)
173 0           readerror();
174 292           buf[len] = '\0';
175 292           SvCUR_set(sv, len);
176              
177 292           return sv;
178             }
179              
180 277           static inline SV * sv_from_curkey (cdb *c) {
181             SV* sv;
182 277           sv = newSV(c->curkey.len + 1 + CDB_CAN_COW);
183 277           sv_setpvn(sv, c->curkey.pv, c->curkey.len);
184 277           CDB_DO_COW(sv);
185 277 50         if(c->is_utf8)
186 0           SvUTF8_on(sv);
187              
188 277           return sv;
189             }
190              
191 10           static int cdb_make_start(cdb_make *c) {
192 10           c->head = 0;
193 10           c->split = 0;
194 10           c->hash = 0;
195 10           c->numentries = 0;
196 10           c->pos = sizeof c->final;
197 10           return PerlIO_seek(c->f, c->pos, SEEK_SET);
198             }
199              
200 215           static int posplus(cdb_make *c, U32 len) {
201 215           U32 newpos = c->pos + len;
202 215 50         if (newpos < len) {
203 0           errno = ENOMEM; return -1;
204             }
205 215           c->pos = newpos;
206 215           return 0;
207             }
208              
209 43           static int cdb_make_addend(cdb_make *c, unsigned int keylen, unsigned int datalen, U32 h) {
210             struct cdb_hplist *head;
211              
212 43           head = c->head;
213 43 100         if (!head || (head->num >= CDB_HPLIST)) {
    50          
214 9           New(0xCDB, head, 1, struct cdb_hplist);
215 9           head->num = 0;
216 9           head->next = c->head;
217 9           c->head = head;
218             }
219 43           head->hp[head->num].h = h;
220 43           head->hp[head->num].p = c->pos;
221 43           ++head->num;
222 43           ++c->numentries;
223 43 50         if (posplus(c, 8) == -1)
224 0           return -1;
225 43 50         if (posplus(c, keylen) == -1)
226 0           return -1;
227 43 50         if (posplus(c, datalen) == -1)
228 0           return -1;
229 43           return 0;
230             }
231              
232             #define CDB_HASHSTART 5381
233              
234             #define cdb_hashadd(hh, cc) ((hh + (hh << 5)) ^ (unsigned char) cc)
235              
236 183           static U32 cdb_hash(char *buf, unsigned int len) {
237             U32 h;
238              
239 183           h = CDB_HASHSTART;
240 855 100         while (len) {
241 672           h = cdb_hashadd(h,*buf++);
242 672           --len;
243             }
244 183           return h;
245             }
246              
247 5378           static void uint32_pack(char s[4], U32 u) {
248 5378           s[0] = u & 255;
249 5378           u >>= 8;
250 5378           s[1] = u & 255;
251 5378           u >>= 8;
252 5378           s[2] = u & 255;
253 5378           s[3] = u >> 8;
254 5378           }
255              
256 2263           static void uint32_unpack(char s[4], U32 *u) {
257             U32 result;
258              
259 2263           result = (unsigned char) s[3];
260 2263           result <<= 8;
261 2263           result += (unsigned char) s[2];
262 2263           result <<= 8;
263 2263           result += (unsigned char) s[1];
264 2263           result <<= 8;
265 2263           result += (unsigned char) s[0];
266              
267 2263           *u = result;
268 2263           }
269              
270 140           static void cdb_findstart(cdb *c) {
271 140           c->loop = 0;
272 140           }
273              
274             #ifdef HASMMAP
275 140           static inline char * cdb_map_addr(cdb *c, STRLEN len, U32 pos) {
276 140 50         if(c->map == NULL) croak("Called cdb_map_addr on a system without mmap");
277              
278 140 50         if ((pos > c->size) || (c->size - pos < len)) {
    50          
279 0           errno = EFTYPE;
280 0           return NULL;
281             }
282 140           return c->map + pos;
283             }
284             #endif
285              
286 2151           static int cdb_read(cdb *c, char *buf, unsigned int len, U32 pos) {
287              
288             #ifdef HASMMAP
289 2151 100         if (c->map) {
290 2150 50         if ((pos > c->size) || (c->size - pos < len)) {
    50          
291 0           errno = EFTYPE;
292 0           return -1;
293             }
294 2150           memcpy(buf, c->map + pos, len);
295 2150           return 0;
296             }
297             #endif
298              
299 1 50         if (PerlIO_seek(c->fh, pos, SEEK_SET) == -1) return -1;
300 1 50         while (len > 0) {
301             int r;
302             do
303 1           r = PerlIO_read(c->fh, buf, len);
304 1 50         while ((r == -1) && (errno == EINTR));
    0          
305 1 50         if (r == -1) return -1;
306 1 50         if (r == 0) {
307 1           errno = EFTYPE;
308 1           return -1;
309             }
310 0           buf += r;
311 0           len -= r;
312             }
313 0           return 0;
314             }
315              
316 462           static bool cdb_key_eq (string_finder *left, string_finder *right) {
317              
318             #if PERL_VERSION_GT(5,13,7)
319 462 50         if( left->is_utf8 != right->is_utf8 ) {
320 0 0         if(left->is_utf8)
321 0           return (bytes_cmp_utf8( (const U8 *) right->pv, right->len, (const U8 *) left->pv, left->len) == 0);
322             else
323 0           return (bytes_cmp_utf8( (const U8 *) left->pv, left->len, (const U8 *) right->pv, right->len) == 0);
324             }
325             #endif
326              
327 462 50         return (left->len == right->len) && memEQ(left->pv, right->pv, right->len);
    100          
328             }
329              
330             #define CDB_MATCH_BUFFER 256
331              
332 140           static int match(cdb *c, string_finder *to_find, U32 pos) {
333             string_finder nextkey;
334              
335             #ifdef HASMMAP
336             /* We don't have to allocate any memory if we're using mmap. */
337 140           nextkey.is_utf8 = c->is_utf8;
338 140           SET_FINDER_LEN(nextkey, to_find->len);
339 140           nextkey.pv = cdb_map_addr(c, to_find->len, pos);
340 140           return cdb_key_eq(&nextkey, to_find);
341             #else
342             /* If we don't have windows, then we have to read the file in*/
343             int ret;
344             int len;
345             char static_buffer[CDB_MATCH_BUFFER];
346              
347             nextkey.is_utf8 = c->is_utf8;
348             SET_FINDER_LEN(nextkey, to_find->len);
349             len = nextkey.len;
350              
351             /* We only need to malloc a buffer if len >= 256 */
352             if(len < CDB_MATCH_BUFFER)
353             nextkey.pv = static_buffer;
354             else
355             Newx(nextkey.pv, len, char);
356              
357             if(cdb_read(c, nextkey.pv, len, pos) == -1)
358             return -1;
359              
360             ret = cdb_key_eq(&nextkey, to_find) ? 1 : 0;
361              
362             /* Only free if we had to malloc */
363             if (len >= CDB_MATCH_BUFFER)
364             Safefree(nextkey.pv);
365              
366             return ret;
367             #endif
368             }
369              
370 158           static int cdb_findnext(cdb *c, string_finder *to_find) {
371             char buf[8];
372             U32 pos;
373             U32 u;
374             U32 next_key_len;
375            
376             /* Matt: reset these so if a search fails they are zero'd */
377 158           c->dpos = 0;
378 158           c->dlen = 0;
379 158 100         if (!c->loop) {
380 140 50         if(to_find->hash != 0) /* hash cache (except when the value is 0) */
381 0           u = to_find->hash;
382             else
383 140           u = to_find->hash = cdb_hash(to_find->pv, to_find->len);
384              
385              
386 140 100         if (cdb_read(c,buf,8,(u << 3) & 2047) == -1)
387 1           return -1;
388 139           uint32_unpack(buf + 4, &c->hslots);
389 139 100         if (!c->hslots)
390 6           return 0;
391 133           uint32_unpack(buf,&c->hpos);
392 133           c->khash = u;
393 133           u >>= 8;
394 133           u %= c->hslots;
395 133           u <<= 3;
396 133           c->kpos = c->hpos + u;
397             }
398              
399 151 50         while (c->loop < c->hslots) {
400 151 50         if (cdb_read(c,buf,8,c->kpos) == -1)
401 0           return -1;
402 151           uint32_unpack(buf + 4,&pos);
403 151 100         if (!pos)
404 11           return 0;
405 140           c->loop += 1;
406 140           c->kpos += 8;
407 140 100         if (c->kpos == c->hpos + (c->hslots << 3))
408 116           c->kpos = c->hpos;
409 140           uint32_unpack(buf,&u);
410 140 50         if (u == c->khash) {
411 140 50         if (cdb_read(c,buf,8,pos) == -1)
412 0           return -1;
413 140           uint32_unpack(buf, &next_key_len);
414 140 50         if (next_key_len == to_find->len) {
415 140           switch(match(c, to_find, pos + 8)) {
416             case -1:
417 0           return -1;
418             case 0:
419 0           return 0;
420             default:
421 140           uint32_unpack(buf + 4,&c->dlen);
422 140           c->dpos = pos + 8 + next_key_len;
423 140           return 1;
424             }
425             }
426             }
427             }
428              
429 158           return 0;
430             }
431              
432 8           static int cdb_find(cdb *c, string_finder *to_find) {
433 8           cdb_findstart(c);
434 8           return cdb_findnext( c, to_find );
435             }
436              
437             #define CDB_DEFAULT_BUFFER_LEN 256
438             #define CDB_MAX_BUFFER_LEN 1024 * 64
439              
440 555           static inline void CDB_ASSURE_CURKEY_MEM(cdb *c, STRLEN len) {
441             STRLEN newlen;
442              
443             /* Nothing to do. We already have enough memory. */
444 555 100         if (c->curkey_allocated >= len && c->curkey_allocated < CDB_MAX_BUFFER_LEN) return;
    50          
445              
446             /* What's the new size? */
447 7 50         if(len < CDB_MAX_BUFFER_LEN && c->curkey_allocated > CDB_MAX_BUFFER_LEN) {
    50          
448 0           newlen = (len > CDB_DEFAULT_BUFFER_LEN) ? len : CDB_DEFAULT_BUFFER_LEN;
449             }
450             else {
451 7           newlen = len - len % 1024 + 1024; /* Grow by a multiple of 1024. */
452             }
453              
454 7 50         if(c->curkey.pv)
455 0           Renew(c->curkey.pv, newlen, char);
456             else
457 7           Newx (c->curkey.pv, newlen, char);
458              
459 7           c->curkey.pv[newlen-1] = 0;
460              
461 7           c->curkey_allocated = newlen;
462             }
463              
464 53           static void iter_start(cdb *c) {
465             char buf[4];
466              
467 53           c->curpos = 2048;
468 53 50         if (cdb_read(c, buf, 4, 0) == -1)
469 0           readerror();
470 53           uint32_unpack(buf, &c->end);
471              
472 53           SET_FINDER_LEN(c->curkey, 0);
473 53           c->fetch_advance = 0;
474 53           }
475              
476 453           static int iter_key(cdb *c) {
477             char buf[8];
478             U32 klen;
479              
480 453 100         if (c->curpos < c->end) {
481 409 50         if (cdb_read(c, buf, 8, c->curpos) == -1)
482 0           readerror();
483 409           uint32_unpack(buf, &klen);
484              
485 409           SET_FINDER_LEN(c->curkey, klen);
486 409           CDB_ASSURE_CURKEY_MEM(c, klen);
487 409 50         if (cdb_read(c, c->curkey.pv, klen, c->curpos + 8) == -1)
488 0           readerror();
489 409           return 1;
490             }
491 453           return 0;
492             }
493              
494 401           static void iter_advance(cdb *c) {
495             char buf[8];
496             U32 klen, dlen;
497              
498 401 50         if (cdb_read(c, buf, 8, c->curpos) == -1)
499 0           readerror();
500 401           uint32_unpack(buf, &klen);
501 401           uint32_unpack(buf + 4, &dlen);
502 401           c->curpos += 8 + klen + dlen;
503 401           }
504              
505 36           static void iter_end(cdb *c) {
506 36 100         if (c->end != 0) {
507 30           c->end = 0;
508 30           SET_FINDER_LEN(c->curkey, 0);
509             }
510 36           }
511              
512             typedef PerlIO * InputStream;
513              
514             MODULE = CDB_File PACKAGE = CDB_File PREFIX = cdb_
515              
516             PROTOTYPES: DISABLED
517              
518             # Some accessor methods.
519              
520             # WARNING: I don't really understand enough about Perl's guts (file
521             # handles / globs, etc.) to write this code. I think this is right, and
522             # it seems to work, but input from anybody with a deeper
523             # understanding would be most welcome.
524              
525             # Additional: fixed by someone with a deeper understanding ;-) (Matt Sergeant)
526              
527             InputStream
528             cdb_handle(this)
529             cdb * this
530              
531             CODE:
532             /* here we dup the filehandle, because perl space will try and close
533             it when it goes out of scope */
534 1           RETVAL = PerlIO_fdopen(PerlIO_fileno(this->fh), "r");
535             OUTPUT:
536             RETVAL
537              
538             U32
539             cdb_datalen(db)
540             cdb *db
541              
542             CODE:
543 6           RETVAL = cdb_datalen(db);
544              
545             OUTPUT:
546             RETVAL
547              
548             U32
549             cdb_datapos(db)
550             cdb *db
551              
552             CODE:
553 6           RETVAL = cdb_datapos(db);
554              
555             OUTPUT:
556             RETVAL
557              
558             cdb *
559             cdb_TIEHASH(CLASS, filename, option_key="", is_utf8=FALSE)
560             char *CLASS
561             char *filename
562             char *option_key
563             bool is_utf8
564              
565             PREINIT:
566             PerlIO *f;
567 13           bool utf8_chosen = FALSE;
568              
569             CODE:
570 13 50         if(strlen(option_key) == 4 && strnEQ("utf8", option_key, 4) && is_utf8 )
    0          
    0          
571             #if PERL_VERSION_LE(5,13,7)
572             croak("utf8 CDB_Files are not supported below Perl 5.14");
573             #else
574 0           utf8_chosen = TRUE;
575             #endif
576              
577 13           Newxz(RETVAL, 1, cdb);
578 13           RETVAL->fh = f = PerlIO_open(filename, "rb");
579 13           RETVAL->is_utf8 = utf8_chosen;
580              
581 13 100         if (!f)
582 1           XSRETURN_NO;
583             #ifdef HASMMAP
584             {
585             struct stat st;
586 12           int fd = PerlIO_fileno(f);
587              
588 12           RETVAL->map = 0;
589 12 50         if (fstat(fd, &st) == 0) {
590 12 50         if (st.st_size <= 0xffffffff) {
591             char *x;
592              
593 12           x = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
594 12 100         if (x != (char *)-1) {
595 11           RETVAL->size = st.st_size;
596 11           RETVAL->map = x;
597             }
598             }
599             }
600             }
601             #endif
602             OUTPUT:
603             RETVAL
604              
605             SV *
606             cdb_FETCH(this, k)
607             cdb *this
608             SV *k
609              
610             PREINIT:
611             char buf[8];
612             int found;
613             string_finder to_find;
614              
615             CODE:
616 167 100         if (!SvOK(k)) {
    50          
    50          
617 1           XSRETURN_UNDEF;
618             }
619              
620 166 50         to_find.pv = this->is_utf8 ? SvPVutf8(k, to_find.len) : SvPV(k, to_find.len);
    0          
    50          
621 166           to_find.hash = 0;
622 166 50         to_find.is_utf8 = this->is_utf8 && SvUTF8(k);
    0          
623              
624             /* Already advanced to the key we need. */
625 166 100         if (this->end && cdb_key_eq(&this->curkey, &to_find)) {
    100          
626 156 50         if (cdb_read(this, buf, 8, this->curpos) == -1)
627 0           readerror();
628 156           uint32_unpack(buf + 4, &this->dlen);
629 156           this->dpos = this->curpos + 8 + to_find.len;
630 156 100         if (this->fetch_advance) {
631 126           iter_advance(this);
632 126 100         if (!iter_key(this)) {
633 13           iter_end(this);
634             }
635             }
636 156           found = 1;
637             } else {
638             /* Need to find the key first.. */
639 10           cdb_findstart(this);
640 10           found = cdb_findnext(this, &to_find);
641 10 100         if ((found != 0) && (found != 1)) readerror();
    100          
642             }
643              
644 165 100         if (found) {
645             U32 dlen;
646 164           dlen = cdb_datalen(this);
647 164           RETVAL = sv_from_datapos(this, dlen);
648             }
649             else {
650 1           XSRETURN_UNDEF;
651             }
652             OUTPUT:
653             RETVAL
654              
655             HV *
656             cdb_fetch_all(this)
657             cdb *this
658              
659             PREINIT:
660             U32 dlen;
661             SV *keyvalue;
662             SV *keysv;
663             int found;
664              
665             CODE:
666 11           RETVAL = newHV();
667 11           sv_2mortal((SV *)RETVAL);
668 11           iter_start(this);
669              
670 121 100         while(iter_key(this)) {
671 110           cdb_findstart(this);
672 110           found = cdb_findnext(this, &this->curkey);
673 110 50         if ((found != 0) && (found != 1))
    50          
674 0           readerror();
675              
676 110           dlen = cdb_datalen(this);
677              
678 110           keyvalue = sv_from_datapos(this, dlen);
679 110           keysv = sv_from_curkey(this);
680              
681 110 50         if (! hv_store_ent(RETVAL, keysv, keyvalue, 0)) {
682 0           SvREFCNT_dec(keyvalue);
683             }
684 110           SvREFCNT_dec(keysv);
685 110           iter_advance(this);
686             }
687 11           iter_end(this);
688              
689             OUTPUT:
690             RETVAL
691              
692              
693             AV *
694             cdb_multi_get(this, k)
695             cdb *this
696             SV *k
697              
698             PREINIT:
699             int found;
700             U32 dlen;
701             SV *x;
702             string_finder to_find;
703              
704             CODE:
705 12 50         if (!SvOK(k)) {
    0          
    0          
706 0           XSRETURN_UNDEF;
707             }
708 12           cdb_findstart(this);
709 12           RETVAL = newAV();
710 12           sv_2mortal((SV *)RETVAL);
711              
712 12 50         to_find.pv = this->is_utf8 ? SvPVutf8(k, to_find.len) : SvPV(k, to_find.len);
    0          
    100          
713 12           to_find.hash = 0;
714 12           to_find.is_utf8 = SvUTF8(k);
715              
716             for (;;) {
717 30           found = cdb_findnext(this, &to_find);
718 30 100         if ((found != 0) && (found != 1))
    50          
719 0           readerror();
720 30 100         if (!found)
721 12           break;
722              
723 18           dlen = cdb_datalen(this);
724              
725 18           x = sv_from_datapos(this, dlen);
726 18           av_push(RETVAL, x);
727 18           }
728              
729             OUTPUT:
730             RETVAL
731              
732             int
733             cdb_EXISTS(this, k)
734             cdb *this
735             SV *k
736              
737             PREINIT:
738             string_finder to_find;
739              
740             CODE:
741 9 100         if (!SvOK(k)) {
    50          
    50          
742 1           XSRETURN_NO;
743             }
744              
745 8 50         to_find.pv = this->is_utf8 ? SvPVutf8(k, to_find.len) : SvPV(k, to_find.len);
    0          
    50          
746 8           to_find.hash = 0;
747 8           to_find.is_utf8 = SvUTF8(k);
748              
749 8           RETVAL = cdb_find(this, &to_find);
750 8 100         if (RETVAL != 0 && RETVAL != 1)
    50          
751 0           readerror();
752              
753             OUTPUT:
754             RETVAL
755              
756             void
757             cdb_DESTROY(db)
758             SV *db
759              
760             PREINIT:
761             cdb *this;
762              
763             CODE:
764 13 50         if (sv_isobject(db) && (SvTYPE(SvRV(db)) == SVt_PVMG) ) {
    100          
765 12 50         this = (cdb*)SvIV(SvRV(db));
766              
767 12 100         if (this->curkey.pv)
768 7           Safefree(this->curkey.pv);
769              
770 12           iter_end(this);
771             #ifdef HASMMAP
772 12 100         if (this->map) {
773 11           munmap(this->map, this->size);
774 11           this->map = 0;
775             }
776             #endif
777 12           PerlIO_close(this->fh); /* close() on O_RDONLY cannot fail */
778 12           Safefree(this);
779             }
780              
781             SV *
782             cdb_FIRSTKEY(this)
783             cdb *this
784              
785             CODE:
786 22           iter_start(this);
787 22 100         if (iter_key(this)) {
788 21           RETVAL = sv_from_curkey(this);
789             } else {
790 1           XSRETURN_UNDEF; /* empty database */
791             }
792             OUTPUT:
793             RETVAL
794              
795             SV *
796             cdb_NEXTKEY(this, k)
797             cdb *this
798             SV *k
799              
800             PREINIT:
801             string_finder to_find;
802              
803             CODE:
804 165 50         if (!SvOK(k)) {
    0          
    0          
805 0           XSRETURN_UNDEF;
806             }
807              
808 165 50         to_find.pv = this->is_utf8 ? SvPVutf8(k, to_find.len) : SvPV(k, to_find.len);
    0          
    50          
809 165           to_find.hash = 0;
810 165           to_find.is_utf8 = SvUTF8(k);
811              
812             /* Sometimes NEXTKEY gets called before FIRSTKEY if the hash
813             * gets re-tied so we call iter_start() anyway here */
814 165 100         if (this->end == 0 || !cdb_key_eq(&this->curkey, &to_find))
    50          
815 1           iter_start(this);
816 165           iter_advance(this);
817 165 100         if (iter_key(this)) {
818 146           CDB_ASSURE_CURKEY_MEM(this, this->curkey.len);
819 146           RETVAL = sv_from_curkey(this);
820             } else {
821 19           iter_start(this);
822 19           (void)iter_key(this); /* prepare curkey for FETCH */
823 19           this->fetch_advance = 1;
824 19           XSRETURN_UNDEF;
825             }
826             OUTPUT:
827             RETVAL
828              
829             cdb_make *
830             cdb_new(CLASS, fn, fntemp, option_key="", is_utf8=FALSE)
831             char * CLASS
832             char * fn
833             char * fntemp
834             char * option_key
835             bool is_utf8;
836              
837             PREINIT:
838             cdb_make *cdbmake;
839 11           bool utf8_chosen = FALSE;
840              
841             CODE:
842 11 50         if(strlen(option_key) == 4 && strnEQ("utf8", option_key, 4) && is_utf8 )
    0          
    0          
843             #if PERL_VERSION_LE(5,13,7)
844             croak("utf8 CDB_Files are not supported below Perl 5.14");
845             #else
846 0           utf8_chosen = TRUE;
847             #endif
848              
849 11           Newxz(cdbmake, 1, cdb_make);
850 11           cdbmake->f = PerlIO_open(fntemp, "wb");
851 11           cdbmake->is_utf8 = utf8_chosen;
852              
853 11 100         if (!cdbmake->f) XSRETURN_UNDEF;
854              
855 10 50         if (cdb_make_start(cdbmake) < 0) XSRETURN_UNDEF;
856              
857             /* Oh, for referential transparency. */
858 10           New(0, cdbmake->fn, strlen(fn) + 1, char);
859 10           New(0, cdbmake->fntemp, strlen(fntemp) + 1, char);
860 10           strcpy(cdbmake->fn, fn);
861 10           strcpy(cdbmake->fntemp, fntemp);
862              
863 10           CLASS = "CDB_File::Maker"; /* OK, so this is a hack */
864              
865 10           RETVAL = cdbmake;
866              
867             OUTPUT:
868             RETVAL
869              
870             MODULE = CDB_File PACKAGE = CDB_File::Maker PREFIX = cdbmaker_
871              
872             void
873             cdbmaker_DESTROY(sv)
874             SV * sv
875              
876             PREINIT:
877             cdb_make * this;
878              
879             CODE:
880 10 50         if (sv_isobject(sv) && (SvTYPE(SvRV(sv)) == SVt_PVMG) ) {
    50          
881 10 50         this = (cdb_make*)SvIV(SvRV(sv));
882 10 50         if(this->f) {
883 0           PerlIO_close(this->f);
884             }
885 10           Safefree(this);
886             }
887              
888             void
889             cdbmaker_insert(this, ...)
890             cdb_make * this
891              
892             PREINIT:
893             char *kp, *vp, packbuf[8];
894             int x;
895             bool is_utf8;
896             STRLEN klen, vlen;
897             U32 h;
898             SV *k;
899             SV *v;
900              
901             PPCODE:
902 42           is_utf8 = this->is_utf8;
903              
904 85 100         for (x = 1; x < items; x += 2) {
905 43           k = ST(x);
906 43           v = ST(x+1);
907              
908 43 50         if(!SvOK(k)) {
    0          
    0          
909 0           warn("Use of uninitialized value in hash key");
910 0           k = sv_2mortal(newSVpv("", 0));
911             }
912              
913 43 50         if(!SvOK(v)) {
    0          
    0          
914 0           warn("undef values cannot be stored in CDB_File. Storing an empty string instead");
915 0           v = sv_2mortal(newSVpv("", 0));
916             }
917              
918 43 50         kp = is_utf8 ? SvPVutf8(k, klen) : SvPV(k, klen);
    0          
    100          
919 43 50         vp = is_utf8 ? SvPVutf8(v, vlen) : SvPV(v, vlen);
    0          
    100          
920              
921 43           uint32_pack(packbuf, klen);
922 43           uint32_pack(packbuf + 4, vlen);
923              
924 43 50         if (PerlIO_write(this->f, packbuf, 8) < 8)
925 0           writeerror();
926              
927 43           h = cdb_hash(kp, klen);
928 43 50         if (PerlIO_write(this->f, kp, klen) < klen)
929 0           writeerror();
930 43 50         if (PerlIO_write(this->f, vp, vlen) < vlen)
931 0           writeerror();
932              
933 43 50         if (cdb_make_addend(this, klen, vlen, h) == -1)
934 0           nomem();
935             }
936              
937             int
938             cdbmaker_finish(this)
939             cdb_make *this
940              
941             PREINIT:
942             char buf[8];
943             int i;
944             U32 len, u;
945             U32 count, memsize, where;
946             struct cdb_hplist *x, *prev;
947             struct cdb_hp *hp;
948              
949             CODE:
950 2570 100         for (i = 0; i < 256; ++i)
951 2560           this->count[i] = 0;
952              
953 19 100         for (x = this->head; x; x = x->next) {
954 9           i = x->num;
955 52 100         while (i--) {
956 43           ++this->count[255 & x->hp[i].h];
957             }
958             }
959              
960 10           memsize = 1;
961 2570 100         for (i = 0; i < 256; ++i) {
962 2560           u = this->count[i] * 2;
963 2560 100         if (u > memsize)
964 9           memsize = u;
965             }
966              
967 10           memsize += this->numentries; /* no overflow possible up to now */
968 10           u = (U32) 0 - (U32) 1;
969 10           u /= sizeof(struct cdb_hp);
970 10 50         if (memsize > u) {
971 0           errno = ENOMEM;
972 0           XSRETURN_UNDEF;
973             }
974              
975 10 50         New(0xCDB, this->split, memsize, struct cdb_hp);
976              
977 10           this->hash = this->split + this->numentries;
978              
979 10           u = 0;
980 2570 100         for (i = 0; i < 256; ++i) {
981 2560           u += this->count[i]; /* bounded by numentries, so no overflow */
982 2560           this->start[i] = u;
983             }
984              
985 10           prev = 0;
986 19 100         for (x = this->head; x; x = x->next) {
987 9           i = x->num;
988 52 100         while (i--) {
989 43           this->split[--this->start[255 & x->hp[i].h]] = x->hp[i];
990             }
991              
992 9 50         if (prev)
993 0           Safefree(prev);
994 9           prev = x;
995             }
996              
997 10 100         if (prev)
998 9           Safefree(prev);
999              
1000 2570 100         for (i = 0; i < 256; ++i) {
1001 2560           count = this->count[i];
1002              
1003 2560           len = count + count; /* no overflow possible */
1004 2560           uint32_pack(this->final + 8 * i, this->pos);
1005 2560           uint32_pack(this->final + 8 * i + 4, len);
1006              
1007 2646 100         for (u = 0; u < len; ++u) {
1008 86           this->hash[u].h = this->hash[u].p = 0;
1009             }
1010              
1011 2560           hp = this->split + this->start[i];
1012 2603 100         for (u = 0; u < count; ++u) {
1013 43           where = (hp->h >> 8) % len;
1014 46 100         while (this->hash[where].p) {
1015 3 100         if (++where == len)
1016 2           where = 0;
1017             }
1018              
1019 43           this->hash[where] = *hp++;
1020             }
1021              
1022 2646 100         for (u = 0; u < len; ++u) {
1023 86           uint32_pack(buf, this->hash[u].h);
1024 86           uint32_pack(buf + 4, this->hash[u].p);
1025              
1026 86 50         if (PerlIO_write(this->f, buf, 8) == -1)
1027 0           XSRETURN_UNDEF;
1028              
1029 86 50         if (posplus(this, 8) == -1)
1030 0           XSRETURN_UNDEF;
1031             }
1032             }
1033              
1034 10           Safefree(this->split);
1035              
1036 10 50         if (PerlIO_flush(this->f) == EOF) writeerror();
1037 10           PerlIO_rewind(this->f);
1038              
1039 10 50         if (PerlIO_write(this->f, this->final, sizeof this->final) < sizeof this->final)
1040 0           writeerror();
1041              
1042 10 50         if (PerlIO_flush(this->f) == EOF)
1043 0           writeerror();
1044              
1045 10 50         if (fsync(PerlIO_fileno(this->f)) == -1)
1046 0           XSRETURN_NO;
1047              
1048 10 50         if (PerlIO_close(this->f) == EOF)
1049 0           XSRETURN_NO;
1050 10           this->f=0;
1051              
1052 10 50         if (rename(this->fntemp, this->fn)) {
1053 0           croak("Failed to rename %s to %s.", this->fntemp, this->fn);
1054             }
1055              
1056 10           Safefree(this->fn);
1057 10           Safefree(this->fntemp);
1058              
1059 10           RETVAL = 1;
1060              
1061             OUTPUT:
1062             RETVAL