asb / lua-tinycdb

A lua binding to the tinycdb library by Michael Tokarev

This URL has Read+Write access

lua-tinycdb / cdb_make_put.c
b058edee » asb 2008-07-19 Check in v0.1 sources 1 /* $Id: cdb_make_put.c,v 1.13 2005/04/18 09:46:50 mjt Exp $
2 * "advanced" cdb_make_put routine
3 *
4 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
5 * Public domain.
6 */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <assert.h>
11 #include "cdb_int.h"
12
13 static void
14 fixup_rpos(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
15 unsigned i;
16 struct cdb_rl *rl;
17 register struct cdb_rec *rp, *rs;
18 for (i = 0; i < 256; ++i) {
19 for (rl = cdbmp->cdb_rec[i]; rl; rl = rl->next)
20 for (rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;)
21 if (rp->rpos <= rpos) goto nexthash;
22 else rp->rpos -= rlen;
23 nexthash:;
24 }
25 }
26
27 static int
28 remove_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
29 unsigned pos, len;
30 int r, fd;
31
32 len = cdbmp->cdb_dpos - rpos - rlen;
33 cdbmp->cdb_dpos -= rlen;
34 if (!len)
35 return 0; /* it was the last record, nothing to do */
36 pos = rpos;
37 fd = cdbmp->cdb_fd;
38 do {
39 r = len > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : len;
40 if (lseek(fd, pos + rlen, SEEK_SET) < 0 ||
41 (r = read(fd, cdbmp->cdb_buf, r)) <= 0)
42 return -1;
43 if (lseek(fd, pos, SEEK_SET) < 0 ||
44 _cdb_make_fullwrite(fd, cdbmp->cdb_buf, r) < 0)
45 return -1;
46 pos += r;
47 len -= r;
48 } while(len);
49 assert(cdbmp->cdb_dpos == pos);
50 fixup_rpos(cdbmp, rpos, rlen);
51 return 0;
52 }
53
54 static int
55 zerofill_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
56 if (rpos + rlen == cdbmp->cdb_dpos) {
57 cdbmp->cdb_dpos = rpos;
58 return 0;
59 }
60 if (lseek(cdbmp->cdb_fd, rpos, SEEK_SET) < 0)
61 return -1;
62 memset(cdbmp->cdb_buf, 0, sizeof(cdbmp->cdb_buf));
63 cdb_pack(rlen - 8, cdbmp->cdb_buf + 4);
64 for(;;) {
65 rpos = rlen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : rlen;
66 if (_cdb_make_fullwrite(cdbmp->cdb_fd, cdbmp->cdb_buf, rpos) < 0)
67 return -1;
68 rlen -= rpos;
69 if (!rlen) return 0;
70 memset(cdbmp->cdb_buf + 4, 0, 4);
71 }
72 }
73
74 /* return: 0 = not found, 1 = error, or record length */
75 static unsigned
76 match(struct cdb_make *cdbmp, unsigned pos, const char *key, unsigned klen)
77 {
78 int len;
79 unsigned rlen;
80 if (lseek(cdbmp->cdb_fd, pos, SEEK_SET) < 0)
81 return 1;
82 if (read(cdbmp->cdb_fd, cdbmp->cdb_buf, 8) != 8)
83 return 1;
84 if (cdb_unpack(cdbmp->cdb_buf) != klen)
85 return 0;
86
87 /* record length; check its validity */
88 rlen = cdb_unpack(cdbmp->cdb_buf + 4);
89 if (rlen > cdbmp->cdb_dpos - pos - klen - 8)
90 return errno = EPROTO, 1; /* someone changed our file? */
91 rlen += klen + 8;
92
93 while(klen) {
94 len = klen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : klen;
95 len = read(cdbmp->cdb_fd, cdbmp->cdb_buf, len);
96 if (len <= 0)
97 return 1;
98 if (memcmp(cdbmp->cdb_buf, key, len) != 0)
99 return 0;
100 key += len;
101 klen -= len;
102 }
103
104 return rlen;
105 }
106
107 static int
108 findrec(struct cdb_make *cdbmp,
109 const void *key, unsigned klen, unsigned hval,
110 enum cdb_put_mode mode)
111 {
112 struct cdb_rl *rl;
113 struct cdb_rec *rp, *rs;
114 unsigned r;
115 int seeked = 0;
116 int ret = 0;
117 for(rl = cdbmp->cdb_rec[hval&255]; rl; rl = rl->next)
118 for(rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;) {
119 if (rp->hval != hval)
120 continue;
121 /*XXX this explicit flush may be unnecessary having
122 * smarter match() that looks into cdb_buf too, but
123 * most of a time here spent in finding hash values
124 * (above), not keys */
125 if (!seeked && _cdb_make_flush(cdbmp) < 0)
126 return -1;
127 seeked = 1;
128 r = match(cdbmp, rp->rpos, key, klen);
129 if (!r)
130 continue;
131 if (r == 1)
132 return -1;
133 ret = 1;
134 switch(mode) {
135 case CDB_FIND_REMOVE:
136 if (remove_record(cdbmp, rp->rpos, r) < 0)
137 return -1;
138 break;
139 case CDB_FIND_FILL0:
140 if (zerofill_record(cdbmp, rp->rpos, r) < 0)
141 return -1;
142 break;
143 default: goto finish;
144 }
145 memmove(rp, rp + 1, (rs + rl->cnt - 1 - rp) * sizeof(*rp));
146 --rl->cnt;
147 --cdbmp->cdb_rcnt;
148 }
149 finish:
150 if (seeked && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
151 return -1;
152 return ret;
153 }
154
155 int
156 cdb_make_find(struct cdb_make *cdbmp,
157 const void *key, unsigned klen,
158 enum cdb_put_mode mode)
159 {
160 return findrec(cdbmp, key, klen, cdb_hash(key, klen), mode);
161 }
162
163 int
164 cdb_make_exists(struct cdb_make *cdbmp,
165 const void *key, unsigned klen)
166 {
167 return cdb_make_find(cdbmp, key, klen, CDB_FIND);
168 }
169
170 int
171 cdb_make_put(struct cdb_make *cdbmp,
172 const void *key, unsigned klen,
173 const void *val, unsigned vlen,
174 enum cdb_put_mode mode)
175 {
176 unsigned hval = cdb_hash(key, klen);
177 int r;
178
179 switch(mode) {
180 case CDB_PUT_REPLACE:
181 case CDB_PUT_INSERT:
182 case CDB_PUT_WARN:
183 case CDB_PUT_REPLACE0:
184 r = findrec(cdbmp, key, klen, hval, mode);
185 if (r < 0)
186 return -1;
187 if (r && mode == CDB_PUT_INSERT)
188 return errno = EEXIST, 1;
189 break;
190
191 case CDB_PUT_ADD:
192 r = 0;
193 break;
194
195 default:
196 return errno = EINVAL, -1;
197 }
198
199 if (_cdb_make_add(cdbmp, hval, key, klen, val, vlen) < 0)
200 return -1;
201
202 return r;
203 }
204