prosperon/source/engine/thirdparty/tinycdb/cdb_make_put.c

222 lines
6 KiB
C
Raw Normal View History

/* cdb_make_put.c: "advanced" cdb_make_put routine
*
2024-03-27 17:54:58 -05:00
* This file is a part of tinycdb package.
* Copyright (C) 2001-2023 Michael Tokarev <mjt+cdb@corpit.ru>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include "cdb_int.h"
static void
fixup_rpos(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
unsigned i;
struct cdb_rl *rl;
register struct cdb_rec *rp, *rs;
for (i = 0; i < 256; ++i) {
for (rl = cdbmp->cdb_rec[i]; rl; rl = rl->next)
for (rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;)
if (rp->rpos <= rpos) goto nexthash;
else rp->rpos -= rlen;
nexthash:;
}
}
static int
remove_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
unsigned pos, len;
int r, fd;
len = cdbmp->cdb_dpos - rpos - rlen;
cdbmp->cdb_dpos -= rlen;
if (!len)
return 0; /* it was the last record, nothing to do */
pos = rpos;
fd = cdbmp->cdb_fd;
do {
r = len > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : len;
if (lseek(fd, pos + rlen, SEEK_SET) < 0 ||
(r = read(fd, cdbmp->cdb_buf, r)) <= 0)
return -1;
if (lseek(fd, pos, SEEK_SET) < 0 ||
_cdb_make_fullwrite(fd, cdbmp->cdb_buf, r) < 0)
return -1;
pos += r;
len -= r;
} while(len);
assert(cdbmp->cdb_dpos == pos);
fixup_rpos(cdbmp, rpos, rlen);
return 0;
}
static int
zerofill_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
if (rpos + rlen == cdbmp->cdb_dpos) {
cdbmp->cdb_dpos = rpos;
return 0;
}
if (lseek(cdbmp->cdb_fd, rpos, SEEK_SET) < 0)
return -1;
memset(cdbmp->cdb_buf, 0, sizeof(cdbmp->cdb_buf));
cdb_pack(rlen - 8, cdbmp->cdb_buf + 4);
for(;;) {
rpos = rlen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : rlen;
if (_cdb_make_fullwrite(cdbmp->cdb_fd, cdbmp->cdb_buf, rpos) < 0)
return -1;
rlen -= rpos;
if (!rlen) return 0;
memset(cdbmp->cdb_buf + 4, 0, 4);
}
}
/* return: 0 = not found, 1 = error, or record length */
static unsigned
match(struct cdb_make *cdbmp, unsigned pos, const char *key, unsigned klen)
{
int len;
unsigned rlen;
if (lseek(cdbmp->cdb_fd, pos, SEEK_SET) < 0)
return 1;
if (read(cdbmp->cdb_fd, cdbmp->cdb_buf, 8) != 8)
return 1;
if (cdb_unpack(cdbmp->cdb_buf) != klen)
return 0;
/* record length; check its validity */
rlen = cdb_unpack(cdbmp->cdb_buf + 4);
if (rlen > cdbmp->cdb_dpos - pos - klen - 8)
return errno = EPROTO, 1; /* someone changed our file? */
rlen += klen + 8;
while(klen) {
len = klen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : klen;
len = read(cdbmp->cdb_fd, cdbmp->cdb_buf, len);
if (len <= 0)
return 1;
if (memcmp(cdbmp->cdb_buf, key, len) != 0)
return 0;
key += len;
klen -= len;
}
return rlen;
}
static int
findrec(struct cdb_make *cdbmp,
const void *key, unsigned klen, unsigned hval,
enum cdb_put_mode mode)
{
struct cdb_rl *rl;
struct cdb_rec *rp, *rs;
unsigned r;
int seeked = 0;
int ret = 0;
for(rl = cdbmp->cdb_rec[hval&255]; rl; rl = rl->next)
for(rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;) {
if (rp->hval != hval)
continue;
/*XXX this explicit flush may be unnecessary having
* smarter match() that looks into cdb_buf too, but
* most of a time here spent in finding hash values
* (above), not keys */
if (!seeked && _cdb_make_flush(cdbmp) < 0)
return -1;
seeked = 1;
r = match(cdbmp, rp->rpos, key, klen);
if (!r)
continue;
if (r == 1)
return -1;
ret = 1;
switch(mode) {
case CDB_FIND_REMOVE:
if (remove_record(cdbmp, rp->rpos, r) < 0)
return -1;
break;
case CDB_FIND_FILL0:
if (zerofill_record(cdbmp, rp->rpos, r) < 0)
return -1;
break;
default: goto finish;
}
memmove(rp, rp + 1, (rs + rl->cnt - 1 - rp) * sizeof(*rp));
--rl->cnt;
--cdbmp->cdb_rcnt;
}
finish:
if (seeked && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
return -1;
return ret;
}
int
cdb_make_find(struct cdb_make *cdbmp,
const void *key, unsigned klen,
enum cdb_put_mode mode)
{
return findrec(cdbmp, key, klen, cdb_hash(key, klen), mode);
}
int
cdb_make_exists(struct cdb_make *cdbmp,
const void *key, unsigned klen)
{
return cdb_make_find(cdbmp, key, klen, CDB_FIND);
}
int
cdb_make_put(struct cdb_make *cdbmp,
const void *key, unsigned klen,
const void *val, unsigned vlen,
enum cdb_put_mode mode)
{
unsigned hval = cdb_hash(key, klen);
int r;
switch(mode) {
case CDB_PUT_REPLACE:
case CDB_PUT_INSERT:
case CDB_PUT_WARN:
case CDB_PUT_REPLACE0:
r = findrec(cdbmp, key, klen, hval, mode);
if (r < 0)
return -1;
if (r && mode == CDB_PUT_INSERT)
return errno = EEXIST, 1;
break;
case CDB_PUT_ADD:
r = 0;
break;
default:
return errno = EINVAL, -1;
}
if (_cdb_make_add(cdbmp, hval, key, klen, val, vlen) < 0)
return -1;
return r;
}