1 /* cdb_seek.c: old interface for reading cdb file
2 *
3 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
4 * Public domain.
5 */
6
7 #include <unistd.h>
8 #include "cdb_int.h"
9
10 #ifndef SEEK_SET
11 # define SEEK_SET 0
12 #endif
13
14 /* read a chunk from file, ignoring interrupts (EINTR) */
15
16 int
cdb_bread(int fd,void * buf,int len)17 cdb_bread(int fd, void *buf, int len)
18 {
19 int l;
20 while(len > 0) {
21 do l = read(fd, buf, len);
22 while(l < 0 && errno == EINTR);
23 if (l <= 0) {
24 if (!l)
25 errno = EIO;
26 return -1;
27 }
28 buf = (char*)buf + l;
29 len -= l;
30 }
31 return 0;
32 }
33
34 /* find a given key in cdb file, seek a file pointer to it's value and
35 place data length to *dlenp. */
36
37 int
cdb_seek(int fd,const void * key,unsigned klen,unsigned * dlenp)38 cdb_seek(int fd, const void *key, unsigned klen, unsigned *dlenp)
39 {
40 unsigned htstart; /* hash table start position */
41 unsigned htsize; /* number of elements in a hash table */
42 unsigned httodo; /* hash table elements left to look */
43 unsigned hti; /* hash table index */
44 unsigned pos; /* position in a file */
45 unsigned hval; /* key's hash value */
46 unsigned char rbuf[64]; /* read buffer */
47 int needseek = 1; /* if we should seek to a hash slot */
48
49 hval = cdb_hash(key, klen);
50 pos = (hval & 0xff) << 3; /* position in TOC */
51 /* read the hash table parameters */
52 if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
53 return -1;
54 if ((htsize = cdb_unpack(rbuf + 4)) == 0)
55 return 0;
56 hti = (hval >> 8) % htsize; /* start position in hash table */
57 httodo = htsize;
58 htstart = cdb_unpack(rbuf);
59
60 for(;;) {
61 if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0)
62 return -1;
63 if (cdb_bread(fd, rbuf, 8) < 0)
64 return -1;
65 if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */
66 return 0;
67
68 if (cdb_unpack(rbuf) != hval) /* hash value not matched */
69 needseek = 0;
70 else { /* hash value matched */
71 if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
72 return -1;
73 if (cdb_unpack(rbuf) == klen) { /* key length matches */
74 /* read the key from file and compare with wanted */
75 unsigned l = klen, c;
76 const char *k = (const char*)key;
77 if (dlenp)
78 *dlenp = cdb_unpack(rbuf + 4); /* save value length */
79 for(;;) {
80 if (!l) /* the whole key read and matches, return */
81 return 1;
82 c = l > sizeof(rbuf) ? sizeof(rbuf) : l;
83 if (cdb_bread(fd, rbuf, c) < 0)
84 return -1;
85 if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */
86 break;
87 k += c; l -= c;
88 }
89 }
90 needseek = 1; /* we're looked to other place, should seek back */
91 }
92 if (!--httodo)
93 return 0;
94 if (++hti == htsize) {
95 hti = 0;
96 needseek = 1;
97 }
98 }
99 }
100