asb / lua-tinycdb
- Source
- Commits
- Network (0)
- Issues (0)
- Downloads (2)
- Wiki (1)
- Graphs
-
Tree:
f5ea285
lua-tinycdb / cdb_make_put.c
| b058edee » | asb | 2008-07-19 | 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 | |||||
