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