Branch data Line data Source code
1 : : /* cdb_make_put.c: "advanced" cdb_make_put routine
2 : : *
3 : : * This file is a part of tinycdb package.
4 : : * Copyright (C) 2001-2023 Michael Tokarev <mjt+cdb@corpit.ru>
5 : : *
6 : : * Permission is hereby granted, free of charge, to any person obtaining a
7 : : * copy of this software and associated documentation files (the "Software"),
8 : : * to deal in the Software without restriction, including without limitation
9 : : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 : : * and/or sell copies of the Software, and to permit persons to whom the
11 : : * Software is furnished to do so, subject to the following conditions:
12 : : *
13 : : * The above copyright notice and this permission notice shall be included
14 : : * in all copies or substantial portions of the Software.
15 : : *
16 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 : : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 : : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 : : * DEALINGS IN THE SOFTWARE.
23 : : */
24 : :
25 : : #include <stdlib.h>
26 : : #include <unistd.h>
27 : : #include <assert.h>
28 : : #include "cdb_int.h"
29 : :
30 : : static void
31 : 0 : fixup_rpos(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
32 : : unsigned i;
33 : : struct cdb_rl *rl;
34 : : register struct cdb_rec *rp, *rs;
35 [ # # ]: 0 : for (i = 0; i < 256; ++i) {
36 [ # # ]: 0 : for (rl = cdbmp->cdb_rec[i]; rl; rl = rl->next)
37 [ # # ]: 0 : for (rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;)
38 [ # # ]: 0 : if (rp->rpos <= rpos) goto nexthash;
39 : 0 : else rp->rpos -= rlen;
40 : 0 : nexthash:;
41 : : }
42 : 0 : }
43 : :
44 : : static int
45 : 0 : remove_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
46 : : unsigned pos, len;
47 : : int r, fd;
48 : :
49 : 0 : len = cdbmp->cdb_dpos - rpos - rlen;
50 : 0 : cdbmp->cdb_dpos -= rlen;
51 [ # # ]: 0 : if (!len)
52 : 0 : return 0; /* it was the last record, nothing to do */
53 : 0 : pos = rpos;
54 : 0 : fd = cdbmp->cdb_fd;
55 : : do {
56 : 0 : r = len > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : len;
57 [ # # ]: 0 : if (lseek(fd, pos + rlen, SEEK_SET) < 0 ||
58 [ # # ]: 0 : (r = read(fd, cdbmp->cdb_buf, r)) <= 0)
59 : 0 : return -1;
60 [ # # # # ]: 0 : if (lseek(fd, pos, SEEK_SET) < 0 ||
61 : 0 : _cdb_make_fullwrite(fd, cdbmp->cdb_buf, r) < 0)
62 : 0 : return -1;
63 : 0 : pos += r;
64 : 0 : len -= r;
65 [ # # ]: 0 : } while(len);
66 [ # # ]: 0 : assert(cdbmp->cdb_dpos == pos);
67 : 0 : fixup_rpos(cdbmp, rpos, rlen);
68 : 0 : return 0;
69 : : }
70 : :
71 : : static int
72 : 0 : zerofill_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
73 [ # # ]: 0 : if (rpos + rlen == cdbmp->cdb_dpos) {
74 : 0 : cdbmp->cdb_dpos = rpos;
75 : 0 : return 0;
76 : : }
77 [ # # ]: 0 : if (lseek(cdbmp->cdb_fd, rpos, SEEK_SET) < 0)
78 : 0 : return -1;
79 : 0 : memset(cdbmp->cdb_buf, 0, sizeof(cdbmp->cdb_buf));
80 : 0 : cdb_pack(rlen - 8, cdbmp->cdb_buf + 4);
81 : : for(;;) {
82 : 0 : rpos = rlen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : rlen;
83 [ # # ]: 0 : if (_cdb_make_fullwrite(cdbmp->cdb_fd, cdbmp->cdb_buf, rpos) < 0)
84 : 0 : return -1;
85 : 0 : rlen -= rpos;
86 [ # # ]: 0 : if (!rlen) return 0;
87 : 0 : memset(cdbmp->cdb_buf + 4, 0, 4);
88 : : }
89 : : }
90 : :
91 : : /* return: 0 = not found, 1 = error, or record length */
92 : : static unsigned
93 : 0 : match(struct cdb_make *cdbmp, unsigned pos, const char *key, unsigned klen)
94 : : {
95 : : int len;
96 : : unsigned rlen;
97 [ # # ]: 0 : if (lseek(cdbmp->cdb_fd, pos, SEEK_SET) < 0)
98 : 0 : return 1;
99 [ # # ]: 0 : if (read(cdbmp->cdb_fd, cdbmp->cdb_buf, 8) != 8)
100 : 0 : return 1;
101 [ # # ]: 0 : if (cdb_unpack(cdbmp->cdb_buf) != klen)
102 : 0 : return 0;
103 : :
104 : : /* record length; check its validity */
105 : 0 : rlen = cdb_unpack(cdbmp->cdb_buf + 4);
106 [ # # ]: 0 : if (rlen > cdbmp->cdb_dpos - pos - klen - 8)
107 : 0 : return errno = EPROTO, 1; /* someone changed our file? */
108 : 0 : rlen += klen + 8;
109 : :
110 [ # # ]: 0 : while(klen) {
111 : 0 : len = klen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : klen;
112 : 0 : len = read(cdbmp->cdb_fd, cdbmp->cdb_buf, len);
113 [ # # ]: 0 : if (len <= 0)
114 : 0 : return 1;
115 [ # # ]: 0 : if (memcmp(cdbmp->cdb_buf, key, len) != 0)
116 : 0 : return 0;
117 : 0 : key += len;
118 : 0 : klen -= len;
119 : : }
120 : :
121 : 0 : return rlen;
122 : : }
123 : :
124 : : static int
125 : 8 : findrec(struct cdb_make *cdbmp,
126 : : const void *key, unsigned klen, unsigned hval,
127 : : enum cdb_put_mode mode)
128 : : {
129 : : struct cdb_rl *rl;
130 : : struct cdb_rec *rp, *rs;
131 : : unsigned r;
132 : 8 : int seeked = 0;
133 : 8 : int ret = 0;
134 [ - + ]: 8 : for(rl = cdbmp->cdb_rec[hval&255]; rl; rl = rl->next)
135 [ # # ]: 0 : for(rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;) {
136 [ # # ]: 0 : if (rp->hval != hval)
137 : 0 : continue;
138 : : /*XXX this explicit flush may be unnecessary having
139 : : * smarter match() that looks into cdb_buf too, but
140 : : * most of a time here spent in finding hash values
141 : : * (above), not keys */
142 [ # # # # ]: 0 : if (!seeked && _cdb_make_flush(cdbmp) < 0)
143 : 0 : return -1;
144 : 0 : seeked = 1;
145 : 0 : r = match(cdbmp, rp->rpos, key, klen);
146 [ # # ]: 0 : if (!r)
147 : 0 : continue;
148 [ # # ]: 0 : if (r == 1)
149 : 0 : return -1;
150 : 0 : ret = 1;
151 [ # # # ]: 0 : switch(mode) {
152 : 0 : case CDB_FIND_REMOVE:
153 [ # # ]: 0 : if (remove_record(cdbmp, rp->rpos, r) < 0)
154 : 0 : return -1;
155 : 0 : break;
156 : 0 : case CDB_FIND_FILL0:
157 [ # # ]: 0 : if (zerofill_record(cdbmp, rp->rpos, r) < 0)
158 : 0 : return -1;
159 : 0 : break;
160 : 0 : default: goto finish;
161 : : }
162 : 0 : memmove(rp, rp + 1, (rs + rl->cnt - 1 - rp) * sizeof(*rp));
163 : 0 : --rl->cnt;
164 : 0 : --cdbmp->cdb_rcnt;
165 : : }
166 : 8 : finish:
167 [ - + - - ]: 8 : if (seeked && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
168 : 0 : return -1;
169 : 8 : return ret;
170 : : }
171 : :
172 : : int
173 : 0 : cdb_make_find(struct cdb_make *cdbmp,
174 : : const void *key, unsigned klen,
175 : : enum cdb_put_mode mode)
176 : : {
177 : 0 : return findrec(cdbmp, key, klen, cdb_hash(key, klen), mode);
178 : : }
179 : :
180 : : int
181 : 0 : cdb_make_exists(struct cdb_make *cdbmp,
182 : : const void *key, unsigned klen)
183 : : {
184 : 0 : return cdb_make_find(cdbmp, key, klen, CDB_FIND);
185 : : }
186 : :
187 : : int
188 : 8 : cdb_make_put(struct cdb_make *cdbmp,
189 : : const void *key, unsigned klen,
190 : : const void *val, unsigned vlen,
191 : : enum cdb_put_mode mode)
192 : : {
193 : 8 : unsigned hval = cdb_hash(key, klen);
194 : : int r;
195 : :
196 [ + - - ]: 8 : switch(mode) {
197 : 8 : case CDB_PUT_REPLACE:
198 : : case CDB_PUT_INSERT:
199 : : case CDB_PUT_WARN:
200 : : case CDB_PUT_REPLACE0:
201 : 8 : r = findrec(cdbmp, key, klen, hval, mode);
202 [ - + ]: 8 : if (r < 0)
203 : 0 : return -1;
204 [ - + - - ]: 8 : if (r && mode == CDB_PUT_INSERT)
205 : 0 : return errno = EEXIST, 1;
206 : 8 : break;
207 : :
208 : 0 : case CDB_PUT_ADD:
209 : 0 : r = 0;
210 : 0 : break;
211 : :
212 : 0 : default:
213 : 0 : return errno = EINVAL, -1;
214 : : }
215 : :
216 [ - + ]: 8 : if (_cdb_make_add(cdbmp, hval, key, klen, val, vlen) < 0)
217 : 0 : return -1;
218 : :
219 : 8 : return r;
220 : : }
221 : :
|