You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cdb_make.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /* $Id: cdb_init.c,v 1.12 2008-11-06 18:07:04 mjt Exp $
  2. * cdb_init, cdb_free and cdb_read routines
  3. *
  4. * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
  5. * Public domain.
  6. */
  7. #include "cdb.h"
  8. void cdb_pack(unsigned num, unsigned char buf[4])
  9. {
  10. buf[0] = num & 255;
  11. num >>= 8;
  12. buf[1] = num & 255;
  13. num >>= 8;
  14. buf[2] = num & 255;
  15. buf[3] = num >> 8;
  16. }
  17. int cdb_make_start(struct cdb_make *cdbmp, int fd)
  18. {
  19. memset (cdbmp, 0, sizeof(*cdbmp));
  20. cdbmp->cdb_fd = fd;
  21. cdbmp->cdb_dpos = 2048;
  22. cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048;
  23. return 0;
  24. }
  25. int
  26. _cdb_make_fullwrite(int fd, const unsigned char *buf, unsigned len)
  27. {
  28. while (len) {
  29. int l = write (fd, buf, len);
  30. if (l > 0) {
  31. len -= l;
  32. buf += l;
  33. }
  34. else if (l < 0 && errno != EINTR)
  35. return -1;
  36. }
  37. return 0;
  38. }
  39. int
  40. _cdb_make_flush(struct cdb_make *cdbmp)
  41. {
  42. unsigned len = cdbmp->cdb_bpos - cdbmp->cdb_buf;
  43. if (len) {
  44. if (_cdb_make_fullwrite (cdbmp->cdb_fd, cdbmp->cdb_buf, len) < 0)
  45. return -1;
  46. cdbmp->cdb_bpos = cdbmp->cdb_buf;
  47. }
  48. return 0;
  49. }
  50. int
  51. _cdb_make_write(struct cdb_make *cdbmp, const unsigned char *ptr, unsigned len)
  52. {
  53. unsigned l = sizeof(cdbmp->cdb_buf) - (cdbmp->cdb_bpos - cdbmp->cdb_buf);
  54. cdbmp->cdb_dpos += len;
  55. if (len > l) {
  56. memcpy (cdbmp->cdb_bpos, ptr, l);
  57. cdbmp->cdb_bpos += l;
  58. if (_cdb_make_flush (cdbmp) < 0)
  59. return -1;
  60. ptr += l;
  61. len -= l;
  62. l = len / sizeof(cdbmp->cdb_buf);
  63. if (l) {
  64. l *= sizeof(cdbmp->cdb_buf);
  65. if (_cdb_make_fullwrite (cdbmp->cdb_fd, ptr, l) < 0)
  66. return -1;
  67. ptr += l;
  68. len -= l;
  69. }
  70. }
  71. if (len) {
  72. memcpy (cdbmp->cdb_bpos, ptr, len);
  73. cdbmp->cdb_bpos += len;
  74. }
  75. return 0;
  76. }
  77. static int cdb_make_finish_internal(struct cdb_make *cdbmp)
  78. {
  79. unsigned hcnt[256]; /* hash table counts */
  80. unsigned hpos[256]; /* hash table positions */
  81. struct cdb_rec *htab;
  82. unsigned char *p;
  83. struct cdb_rl *rl;
  84. unsigned hsize;
  85. unsigned t, i;
  86. if (((0xffffffff - cdbmp->cdb_dpos) >> 3) < cdbmp->cdb_rcnt)
  87. return errno = ENOMEM, -1;
  88. /* count htab sizes and reorder reclists */
  89. hsize = 0;
  90. for (t = 0; t < 256; ++t) {
  91. struct cdb_rl *rlt = NULL;
  92. i = 0;
  93. rl = cdbmp->cdb_rec[t];
  94. while (rl) {
  95. struct cdb_rl *rln = rl->next;
  96. rl->next = rlt;
  97. rlt = rl;
  98. i += rl->cnt;
  99. rl = rln;
  100. }
  101. cdbmp->cdb_rec[t] = rlt;
  102. if (hsize < (hcnt[t] = i << 1))
  103. hsize = hcnt[t];
  104. }
  105. /* allocate memory to hold max htable */
  106. htab = (struct cdb_rec*) malloc ((hsize + 2) * sizeof(struct cdb_rec));
  107. if (!htab)
  108. return errno = ENOENT, -1;
  109. p = (unsigned char *) htab;
  110. htab += 2;
  111. /* build hash tables */
  112. for (t = 0; t < 256; ++t) {
  113. unsigned len, hi;
  114. hpos[t] = cdbmp->cdb_dpos;
  115. if ((len = hcnt[t]) == 0)
  116. continue;
  117. for (i = 0; i < len; ++i)
  118. htab[i].hval = htab[i].rpos = 0;
  119. for (rl = cdbmp->cdb_rec[t]; rl; rl = rl->next)
  120. for (i = 0; i < rl->cnt; ++i) {
  121. hi = (rl->rec[i].hval >> 8) % len;
  122. while (htab[hi].rpos)
  123. if (++hi == len)
  124. hi = 0;
  125. htab[hi] = rl->rec[i];
  126. }
  127. for (i = 0; i < len; ++i) {
  128. cdb_pack (htab[i].hval, p + (i << 3));
  129. cdb_pack (htab[i].rpos, p + (i << 3) + 4);
  130. }
  131. if (_cdb_make_write (cdbmp, p, len << 3) < 0) {
  132. free (p);
  133. return -1;
  134. }
  135. }
  136. free (p);
  137. if (_cdb_make_flush (cdbmp) < 0)
  138. return -1;
  139. p = cdbmp->cdb_buf;
  140. for (t = 0; t < 256; ++t) {
  141. cdb_pack (hpos[t], p + (t << 3));
  142. cdb_pack (hcnt[t], p + (t << 3) + 4);
  143. }
  144. if (lseek (cdbmp->cdb_fd, 0, 0) != 0 || _cdb_make_fullwrite (cdbmp->cdb_fd,
  145. p, 2048) != 0)
  146. return -1;
  147. return 0;
  148. }
  149. static void cdb_make_free(struct cdb_make *cdbmp)
  150. {
  151. unsigned t;
  152. for (t = 0; t < 256; ++t) {
  153. struct cdb_rl *rl = cdbmp->cdb_rec[t];
  154. while (rl) {
  155. struct cdb_rl *tm = rl;
  156. rl = rl->next;
  157. free (tm);
  158. }
  159. }
  160. }
  161. int cdb_make_finish(struct cdb_make *cdbmp)
  162. {
  163. int r = cdb_make_finish_internal (cdbmp);
  164. cdb_make_free (cdbmp);
  165. return r;
  166. }
  167. int
  168. _cdb_make_add(struct cdb_make *cdbmp, unsigned hval, const void *key,
  169. unsigned klen, const void *val, unsigned vlen)
  170. {
  171. unsigned char rlen[8];
  172. struct cdb_rl *rl;
  173. unsigned i;
  174. if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) || vlen > 0xffffffff
  175. - (cdbmp->cdb_dpos + klen + 8))
  176. return errno = ENOMEM, -1;
  177. i = hval & 255;
  178. rl = cdbmp->cdb_rec[i];
  179. if (!rl || rl->cnt >= sizeof(rl->rec) / sizeof(rl->rec[0])) {
  180. rl = (struct cdb_rl*) malloc (sizeof(struct cdb_rl));
  181. if (!rl)
  182. return errno = ENOMEM, -1;
  183. rl->cnt = 0;
  184. rl->next = cdbmp->cdb_rec[i];
  185. cdbmp->cdb_rec[i] = rl;
  186. }
  187. i = rl->cnt++;
  188. rl->rec[i].hval = hval;
  189. rl->rec[i].rpos = cdbmp->cdb_dpos;
  190. ++cdbmp->cdb_rcnt;
  191. cdb_pack (klen, rlen);
  192. cdb_pack (vlen, rlen + 4);
  193. if (_cdb_make_write (cdbmp, rlen, 8) < 0 || _cdb_make_write (cdbmp, key,
  194. klen) < 0 || _cdb_make_write (cdbmp, val, vlen) < 0)
  195. return -1;
  196. return 0;
  197. }
  198. int cdb_make_add(struct cdb_make *cdbmp, const void *key, unsigned klen,
  199. const void *val, unsigned vlen)
  200. {
  201. return _cdb_make_add (cdbmp, cdb_hash (key, klen), key, klen, val, vlen);
  202. }
  203. static void fixup_rpos(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen)
  204. {
  205. unsigned i;
  206. struct cdb_rl *rl;
  207. register struct cdb_rec *rp, *rs;
  208. for (i = 0; i < 256; ++i) {
  209. for (rl = cdbmp->cdb_rec[i]; rl; rl = rl->next)
  210. for (rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;)
  211. if (rp->rpos <= rpos)
  212. goto nexthash;
  213. else
  214. rp->rpos -= rlen;
  215. nexthash: ;
  216. }
  217. }
  218. static int remove_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen)
  219. {
  220. unsigned pos, len;
  221. int r, fd;
  222. len = cdbmp->cdb_dpos - rpos - rlen;
  223. cdbmp->cdb_dpos -= rlen;
  224. if (!len)
  225. return 0; /* it was the last record, nothing to do */
  226. pos = rpos;
  227. fd = cdbmp->cdb_fd;
  228. do {
  229. r = len > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : len;
  230. if (lseek (fd, pos + rlen, SEEK_SET) < 0 || (r = read (fd,
  231. cdbmp->cdb_buf, r)) <= 0)
  232. return -1;
  233. if (lseek (fd, pos, SEEK_SET) < 0 || _cdb_make_fullwrite (fd,
  234. cdbmp->cdb_buf, r) < 0)
  235. return -1;
  236. pos += r;
  237. len -= r;
  238. } while (len);
  239. g_assert (cdbmp->cdb_dpos == pos);
  240. fixup_rpos (cdbmp, rpos, rlen);
  241. return 0;
  242. }
  243. static int zerofill_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen)
  244. {
  245. if (rpos + rlen == cdbmp->cdb_dpos) {
  246. cdbmp->cdb_dpos = rpos;
  247. return 0;
  248. }
  249. if (lseek (cdbmp->cdb_fd, rpos, SEEK_SET) < 0)
  250. return -1;
  251. memset (cdbmp->cdb_buf, 0, sizeof(cdbmp->cdb_buf));
  252. cdb_pack (rlen - 8, cdbmp->cdb_buf + 4);
  253. for (;;) {
  254. rpos = rlen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : rlen;
  255. if (_cdb_make_fullwrite (cdbmp->cdb_fd, cdbmp->cdb_buf, rpos) < 0)
  256. return -1;
  257. rlen -= rpos;
  258. if (!rlen)
  259. return 0;
  260. memset (cdbmp->cdb_buf + 4, 0, 4);
  261. }
  262. }
  263. /* return: 0 = not found, 1 = error, or record length */
  264. static unsigned match(struct cdb_make *cdbmp, unsigned pos, const char *key,
  265. unsigned klen)
  266. {
  267. int len;
  268. unsigned rlen;
  269. if (lseek (cdbmp->cdb_fd, pos, SEEK_SET) < 0)
  270. return 1;
  271. if (read (cdbmp->cdb_fd, cdbmp->cdb_buf, 8) != 8)
  272. return 1;
  273. if (cdb_unpack (cdbmp->cdb_buf) != klen)
  274. return 0;
  275. /* record length; check its validity */
  276. rlen = cdb_unpack (cdbmp->cdb_buf + 4);
  277. if (rlen > cdbmp->cdb_dpos - pos - klen - 8)
  278. return errno = EPROTO, 1; /* someone changed our file? */
  279. rlen += klen + 8;
  280. while (klen) {
  281. len = klen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : klen;
  282. len = read (cdbmp->cdb_fd, cdbmp->cdb_buf, len);
  283. if (len <= 0)
  284. return 1;
  285. if (memcmp (cdbmp->cdb_buf, key, len) != 0)
  286. return 0;
  287. key += len;
  288. klen -= len;
  289. }
  290. return rlen;
  291. }
  292. static int findrec(struct cdb_make *cdbmp, const void *key, unsigned klen,
  293. unsigned hval, enum cdb_put_mode mode)
  294. {
  295. struct cdb_rl *rl;
  296. struct cdb_rec *rp, *rs;
  297. unsigned r;
  298. int sought = 0;
  299. int ret = 0;
  300. for (rl = cdbmp->cdb_rec[hval & 255]; rl; rl = rl->next)
  301. for (rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;) {
  302. if (rp->hval != hval)
  303. continue;
  304. /*XXX this explicit flush may be unnecessary having
  305. * smarter match() that looks into cdb_buf too, but
  306. * most of a time here spent in finding hash values
  307. * (above), not keys */
  308. if (!sought && _cdb_make_flush (cdbmp) < 0)
  309. return -1;
  310. sought = 1;
  311. r = match (cdbmp, rp->rpos, key, klen);
  312. if (!r)
  313. continue;
  314. if (r == 1)
  315. return -1;
  316. ret = 1;
  317. switch (mode)
  318. {
  319. case CDB_FIND_REMOVE:
  320. if (remove_record (cdbmp, rp->rpos, r) < 0)
  321. return -1;
  322. break;
  323. case CDB_FIND_FILL0:
  324. if (zerofill_record (cdbmp, rp->rpos, r) < 0)
  325. return -1;
  326. break;
  327. default:
  328. goto finish;
  329. }
  330. memmove (rp, rp + 1, (rs + rl->cnt - 1 - rp) * sizeof(*rp));
  331. --rl->cnt;
  332. --cdbmp->cdb_rcnt;
  333. }
  334. finish: if (sought && lseek (cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
  335. return -1;
  336. return ret;
  337. }
  338. int cdb_make_find(struct cdb_make *cdbmp, const void *key, unsigned klen,
  339. enum cdb_put_mode mode)
  340. {
  341. return findrec (cdbmp, key, klen, cdb_hash (key, klen), mode);
  342. }
  343. int cdb_make_exists(struct cdb_make *cdbmp, const void *key, unsigned klen)
  344. {
  345. return cdb_make_find (cdbmp, key, klen, CDB_FIND);
  346. }
  347. int cdb_make_put(struct cdb_make *cdbmp, const void *key, unsigned klen,
  348. const void *val, unsigned vlen, enum cdb_put_mode mode)
  349. {
  350. unsigned hval = cdb_hash (key, klen);
  351. int r;
  352. switch (mode)
  353. {
  354. case CDB_PUT_REPLACE:
  355. case CDB_PUT_INSERT:
  356. case CDB_PUT_WARN:
  357. case CDB_PUT_REPLACE0:
  358. r = findrec (cdbmp, key, klen, hval, mode);
  359. if (r < 0)
  360. return -1;
  361. if (r && mode == CDB_PUT_INSERT)
  362. return errno = EEXIST, 1;
  363. break;
  364. case CDB_PUT_ADD:
  365. r = 0;
  366. break;
  367. default:
  368. return errno = EINVAL, -1;
  369. }
  370. if (_cdb_make_add (cdbmp, hval, key, klen, val, vlen) < 0)
  371. return -1;
  372. return r;
  373. }
  374. unsigned
  375. cdb_unpack(const unsigned char buf[4])
  376. {
  377. unsigned n = buf[3];
  378. n <<= 8; n |= buf[2];
  379. n <<= 8; n |= buf[1];
  380. n <<= 8; n |= buf[0];
  381. return n;
  382. }
  383. int
  384. cdb_seqnext(unsigned *cptr, struct cdb *cdbp) {
  385. unsigned klen, vlen;
  386. unsigned pos = *cptr;
  387. unsigned dend = cdbp->cdb_dend;
  388. const unsigned char *mem = cdbp->cdb_mem;
  389. if (pos > dend - 8)
  390. return 0;
  391. klen = cdb_unpack(mem + pos);
  392. vlen = cdb_unpack(mem + pos + 4);
  393. pos += 8;
  394. if (dend - klen < pos || dend - vlen < pos + klen)
  395. return errno = EPROTO, -1;
  396. cdbp->cdb_kpos = pos;
  397. cdbp->cdb_klen = klen;
  398. cdbp->cdb_vpos = pos + klen;
  399. cdbp->cdb_vlen = vlen;
  400. *cptr = pos + klen + vlen;
  401. return 1;
  402. }
  403. /* read a chunk from file, ignoring interrupts (EINTR) */
  404. int
  405. cdb_bread(int fd, void *buf, int len)
  406. {
  407. int l;
  408. while(len > 0) {
  409. do l = read(fd, buf, len);
  410. while(l < 0 && errno == EINTR);
  411. if (l <= 0) {
  412. if (!l)
  413. errno = EIO;
  414. return -1;
  415. }
  416. buf = (char*)buf + l;
  417. len -= l;
  418. }
  419. return 0;
  420. }
  421. /* find a given key in cdb file, seek a file pointer to it's value and
  422. place data length to *dlenp. */
  423. int
  424. cdb_seek(int fd, const void *key, unsigned klen, unsigned *dlenp)
  425. {
  426. unsigned htstart; /* hash table start position */
  427. unsigned htsize; /* number of elements in a hash table */
  428. unsigned httodo; /* hash table elements left to look */
  429. unsigned hti; /* hash table index */
  430. unsigned pos; /* position in a file */
  431. unsigned hval; /* key's hash value */
  432. unsigned char rbuf[64]; /* read buffer */
  433. int needseek = 1; /* if we should seek to a hash slot */
  434. hval = cdb_hash(key, klen);
  435. pos = (hval & 0xff) << 3; /* position in TOC */
  436. /* read the hash table parameters */
  437. if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
  438. return -1;
  439. if ((htsize = cdb_unpack(rbuf + 4)) == 0)
  440. return 0;
  441. hti = (hval >> 8) % htsize; /* start position in hash table */
  442. httodo = htsize;
  443. htstart = cdb_unpack(rbuf);
  444. for(;;) {
  445. if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0)
  446. return -1;
  447. if (cdb_bread(fd, rbuf, 8) < 0)
  448. return -1;
  449. if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */
  450. return 0;
  451. if (cdb_unpack(rbuf) != hval) /* hash value not matched */
  452. needseek = 0;
  453. else { /* hash value matched */
  454. if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
  455. return -1;
  456. if (cdb_unpack(rbuf) == klen) { /* key length matches */
  457. /* read the key from file and compare with wanted */
  458. unsigned l = klen, c;
  459. const char *k = (const char*)key;
  460. if (dlenp)
  461. *dlenp = cdb_unpack(rbuf + 4); /* save value length */
  462. for(;;) {
  463. if (!l) /* the whole key read and matches, return */
  464. return 1;
  465. c = l > sizeof(rbuf) ? sizeof(rbuf) : l;
  466. if (cdb_bread(fd, rbuf, c) < 0)
  467. return -1;
  468. if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */
  469. break;
  470. k += c; l -= c;
  471. }
  472. }
  473. needseek = 1; /* we're looked to other place, should seek back */
  474. }
  475. if (!--httodo)
  476. return 0;
  477. if (++hti == htsize) {
  478. hti = 0;
  479. needseek = 1;
  480. }
  481. }
  482. }