123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /* $Id: cdb_init.c,v 1.12 2008-11-06 18:07:04 mjt Exp $
- * cdb_init, cdb_free and cdb_read routines
- *
- * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
- * Public domain.
- */
-
- #include "cdb.h"
-
- void cdb_pack(unsigned num, unsigned char buf[4])
- {
- buf[0] = num & 255;
- num >>= 8;
- buf[1] = num & 255;
- num >>= 8;
- buf[2] = num & 255;
- buf[3] = num >> 8;
- }
-
- int cdb_make_start(struct cdb_make *cdbmp, int fd)
- {
- memset (cdbmp, 0, sizeof(*cdbmp));
- cdbmp->cdb_fd = fd;
- cdbmp->cdb_dpos = 2048;
- cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048;
- return 0;
- }
-
- int
- _cdb_make_fullwrite(int fd, const unsigned char *buf, unsigned len)
- {
- while (len) {
- int l = write (fd, buf, len);
- if (l > 0) {
- len -= l;
- buf += l;
- }
- else if (l < 0 && errno != EINTR)
- return -1;
- }
- return 0;
- }
-
- int
- _cdb_make_flush(struct cdb_make *cdbmp)
- {
- unsigned len = cdbmp->cdb_bpos - cdbmp->cdb_buf;
- if (len) {
- if (_cdb_make_fullwrite (cdbmp->cdb_fd, cdbmp->cdb_buf, len) < 0)
- return -1;
- cdbmp->cdb_bpos = cdbmp->cdb_buf;
- }
- return 0;
- }
-
- int
- _cdb_make_write(struct cdb_make *cdbmp, const unsigned char *ptr, unsigned len)
- {
- unsigned l = sizeof(cdbmp->cdb_buf) - (cdbmp->cdb_bpos - cdbmp->cdb_buf);
- cdbmp->cdb_dpos += len;
- if (len > l) {
- memcpy (cdbmp->cdb_bpos, ptr, l);
- cdbmp->cdb_bpos += l;
- if (_cdb_make_flush (cdbmp) < 0)
- return -1;
- ptr += l;
- len -= l;
- l = len / sizeof(cdbmp->cdb_buf);
- if (l) {
- l *= sizeof(cdbmp->cdb_buf);
- if (_cdb_make_fullwrite (cdbmp->cdb_fd, ptr, l) < 0)
- return -1;
- ptr += l;
- len -= l;
- }
- }
- if (len) {
- memcpy (cdbmp->cdb_bpos, ptr, len);
- cdbmp->cdb_bpos += len;
- }
- return 0;
- }
-
- static int cdb_make_finish_internal(struct cdb_make *cdbmp)
- {
- unsigned hcnt[256]; /* hash table counts */
- unsigned hpos[256]; /* hash table positions */
- struct cdb_rec *htab;
- unsigned char *p;
- struct cdb_rl *rl;
- unsigned hsize;
- unsigned t, i;
-
- if (((0xffffffff - cdbmp->cdb_dpos) >> 3) < cdbmp->cdb_rcnt)
- return errno = ENOMEM, -1;
-
- /* count htab sizes and reorder reclists */
- hsize = 0;
- for (t = 0; t < 256; ++t) {
- struct cdb_rl *rlt = NULL;
- i = 0;
- rl = cdbmp->cdb_rec[t];
- while (rl) {
- struct cdb_rl *rln = rl->next;
- rl->next = rlt;
- rlt = rl;
- i += rl->cnt;
- rl = rln;
- }
- cdbmp->cdb_rec[t] = rlt;
- if (hsize < (hcnt[t] = i << 1))
- hsize = hcnt[t];
- }
-
- /* allocate memory to hold max htable */
- htab = (struct cdb_rec*) malloc ((hsize + 2) * sizeof(struct cdb_rec));
- if (!htab)
- return errno = ENOENT, -1;
- p = (unsigned char *) htab;
- htab += 2;
-
- /* build hash tables */
- for (t = 0; t < 256; ++t) {
- unsigned len, hi;
- hpos[t] = cdbmp->cdb_dpos;
- if ((len = hcnt[t]) == 0)
- continue;
- for (i = 0; i < len; ++i)
- htab[i].hval = htab[i].rpos = 0;
- for (rl = cdbmp->cdb_rec[t]; rl; rl = rl->next)
- for (i = 0; i < rl->cnt; ++i) {
- hi = (rl->rec[i].hval >> 8) % len;
- while (htab[hi].rpos)
- if (++hi == len)
- hi = 0;
- htab[hi] = rl->rec[i];
- }
- for (i = 0; i < len; ++i) {
- cdb_pack (htab[i].hval, p + (i << 3));
- cdb_pack (htab[i].rpos, p + (i << 3) + 4);
- }
- if (_cdb_make_write (cdbmp, p, len << 3) < 0) {
- free (p);
- return -1;
- }
- }
- free (p);
- if (_cdb_make_flush (cdbmp) < 0)
- return -1;
- p = cdbmp->cdb_buf;
- for (t = 0; t < 256; ++t) {
- cdb_pack (hpos[t], p + (t << 3));
- cdb_pack (hcnt[t], p + (t << 3) + 4);
- }
- if (lseek (cdbmp->cdb_fd, 0, 0) != 0 || _cdb_make_fullwrite (cdbmp->cdb_fd,
- p, 2048) != 0)
- return -1;
-
- return 0;
- }
-
- static void cdb_make_free(struct cdb_make *cdbmp)
- {
- unsigned t;
- for (t = 0; t < 256; ++t) {
- struct cdb_rl *rl = cdbmp->cdb_rec[t];
- while (rl) {
- struct cdb_rl *tm = rl;
- rl = rl->next;
- free (tm);
- }
- }
- }
-
- int cdb_make_finish(struct cdb_make *cdbmp)
- {
- int r = cdb_make_finish_internal (cdbmp);
- cdb_make_free (cdbmp);
- return r;
- }
-
- int
- _cdb_make_add(struct cdb_make *cdbmp, unsigned hval, const void *key,
- unsigned klen, const void *val, unsigned vlen)
- {
- unsigned char rlen[8];
- struct cdb_rl *rl;
- unsigned i;
- if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) || vlen > 0xffffffff
- - (cdbmp->cdb_dpos + klen + 8))
- return errno = ENOMEM, -1;
- i = hval & 255;
- rl = cdbmp->cdb_rec[i];
- if (!rl || rl->cnt >= sizeof(rl->rec) / sizeof(rl->rec[0])) {
- rl = (struct cdb_rl*) malloc (sizeof(struct cdb_rl));
- if (!rl)
- return errno = ENOMEM, -1;
- rl->cnt = 0;
- rl->next = cdbmp->cdb_rec[i];
- cdbmp->cdb_rec[i] = rl;
- }
- i = rl->cnt++;
- rl->rec[i].hval = hval;
- rl->rec[i].rpos = cdbmp->cdb_dpos;
- ++cdbmp->cdb_rcnt;
- cdb_pack (klen, rlen);
- cdb_pack (vlen, rlen + 4);
- if (_cdb_make_write (cdbmp, rlen, 8) < 0 || _cdb_make_write (cdbmp, key,
- klen) < 0 || _cdb_make_write (cdbmp, val, vlen) < 0)
- return -1;
- return 0;
- }
-
- int cdb_make_add(struct cdb_make *cdbmp, const void *key, unsigned klen,
- const void *val, unsigned vlen)
- {
- return _cdb_make_add (cdbmp, cdb_hash (key, klen), key, klen, val, vlen);
- }
-
- 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);
- g_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 sought = 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 (!sought && _cdb_make_flush (cdbmp) < 0)
- return -1;
- sought = 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 (sought && 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;
- }
-
- unsigned
- cdb_unpack(const unsigned char buf[4])
- {
- unsigned n = buf[3];
- n <<= 8; n |= buf[2];
- n <<= 8; n |= buf[1];
- n <<= 8; n |= buf[0];
- return n;
- }
-
- int
- cdb_seqnext(unsigned *cptr, struct cdb *cdbp) {
- unsigned klen, vlen;
- unsigned pos = *cptr;
- unsigned dend = cdbp->cdb_dend;
- const unsigned char *mem = cdbp->cdb_mem;
- if (pos > dend - 8)
- return 0;
- klen = cdb_unpack(mem + pos);
- vlen = cdb_unpack(mem + pos + 4);
- pos += 8;
- if (dend - klen < pos || dend - vlen < pos + klen)
- return errno = EPROTO, -1;
- cdbp->cdb_kpos = pos;
- cdbp->cdb_klen = klen;
- cdbp->cdb_vpos = pos + klen;
- cdbp->cdb_vlen = vlen;
- *cptr = pos + klen + vlen;
- return 1;
- }
-
- /* read a chunk from file, ignoring interrupts (EINTR) */
-
- int
- cdb_bread(int fd, void *buf, int len)
- {
- int l;
- while(len > 0) {
- do l = read(fd, buf, len);
- while(l < 0 && errno == EINTR);
- if (l <= 0) {
- if (!l)
- errno = EIO;
- return -1;
- }
- buf = (char*)buf + l;
- len -= l;
- }
- return 0;
- }
-
- /* find a given key in cdb file, seek a file pointer to it's value and
- place data length to *dlenp. */
-
- int
- cdb_seek(int fd, const void *key, unsigned klen, unsigned *dlenp)
- {
- unsigned htstart; /* hash table start position */
- unsigned htsize; /* number of elements in a hash table */
- unsigned httodo; /* hash table elements left to look */
- unsigned hti; /* hash table index */
- unsigned pos; /* position in a file */
- unsigned hval; /* key's hash value */
- unsigned char rbuf[64]; /* read buffer */
- int needseek = 1; /* if we should seek to a hash slot */
-
- hval = cdb_hash(key, klen);
- pos = (hval & 0xff) << 3; /* position in TOC */
- /* read the hash table parameters */
- if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
- return -1;
- if ((htsize = cdb_unpack(rbuf + 4)) == 0)
- return 0;
- hti = (hval >> 8) % htsize; /* start position in hash table */
- httodo = htsize;
- htstart = cdb_unpack(rbuf);
-
- for(;;) {
- if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0)
- return -1;
- if (cdb_bread(fd, rbuf, 8) < 0)
- return -1;
- if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */
- return 0;
-
- if (cdb_unpack(rbuf) != hval) /* hash value not matched */
- needseek = 0;
- else { /* hash value matched */
- if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
- return -1;
- if (cdb_unpack(rbuf) == klen) { /* key length matches */
- /* read the key from file and compare with wanted */
- unsigned l = klen, c;
- const char *k = (const char*)key;
- if (dlenp)
- *dlenp = cdb_unpack(rbuf + 4); /* save value length */
- for(;;) {
- if (!l) /* the whole key read and matches, return */
- return 1;
- c = l > sizeof(rbuf) ? sizeof(rbuf) : l;
- if (cdb_bread(fd, rbuf, c) < 0)
- return -1;
- if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */
- break;
- k += c; l -= c;
- }
- }
- needseek = 1; /* we're looked to other place, should seek back */
- }
- if (!--httodo)
- return 0;
- if (++hti == htsize) {
- hti = 0;
- needseek = 1;
- }
- }
- }
|