1 /*************************************************************************************************
2 * The hash database API of Tokyo Cabinet
3 * Copyright (C) 2006-2012 FAL Labs
4 * This file is part of Tokyo Cabinet.
5 * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
6 * the GNU Lesser General Public License as published by the Free Software Foundation; either
7 * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope
8 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10 * License for more details.
11 * You should have received a copy of the GNU Lesser General Public License along with Tokyo
12 * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13 * Boston, MA 02111-1307 USA.
14 *************************************************************************************************/
15
16
17 #include "tcutil.h"
18 #include "tchdb.h"
19 #include "tcbdb.h"
20 #include "myconf.h"
21
22 #define HDBFILEMODE 00644 // permission of created files
23 #define HDBIOBUFSIZ 8192 // size of an I/O buffer
24
25 #define HDBMAGICDATA "ToKyO CaBiNeT" // magic data for identification
26 #define HDBHEADSIZ 256 // size of the reagion of the header
27 #define HDBTYPEOFF 32 // offset of the region for the database type
28 #define HDBFLAGSOFF 33 // offset of the region for the additional flags
29 #define HDBAPOWOFF 34 // offset of the region for the alignment power
30 #define HDBFPOWOFF 35 // offset of the region for the free block pool power
31 #define HDBOPTSOFF 36 // offset of the region for the options
32 #define HDBBNUMOFF 40 // offset of the region for the bucket number
33 #define HDBRNUMOFF 48 // offset of the region for the record number
34 #define HDBFSIZOFF 56 // offset of the region for the file size
35 #define HDBFRECOFF 64 // offset of the region for the first record offset
36 #define HDBOPAQUEOFF 128 // offset of the region for the opaque field
37
38 #define HDBDEFBNUM 131071 // default bucket number
39 #define HDBDEFAPOW 4 // default alignment power
40 #define HDBMAXAPOW 16 // maximum alignment power
41 #define HDBDEFFPOW 10 // default free block pool power
42 #define HDBMAXFPOW 20 // maximum free block pool power
43 #define HDBDEFXMSIZ (64LL<<20) // default size of the extra mapped memory
44 #define HDBXFSIZINC 32768 // increment of extra file size
45 #define HDBMINRUNIT 48 // minimum record reading unit
46 #define HDBMAXHSIZ 32 // maximum record header size
47 #define HDBFBPALWRAT 2 // allowance ratio of the free block pool
48 #define HDBFBPBSIZ 64 // base region size of the free block pool
49 #define HDBFBPESIZ 4 // size of each region of the free block pool
50 #define HDBFBPMGFREQ 4096 // frequency to merge the free block pool
51 #define HDBDRPUNIT 65536 // unit size of the delayed record pool
52 #define HDBDRPLAT 2048 // latitude size of the delayed record pool
53 #define HDBDFRSRAT 2 // step ratio of auto defragmentation
54 #define HDBFBMAXSIZ (INT32_MAX/4) // maximum size of a free block pool
55 #define HDBCACHEOUT 128 // number of records in a process of cacheout
56 #define HDBWALSUFFIX "wal" // suffix of write ahead logging file
57
58 typedef struct { // type of structure for a record
59 uint64_t off; // offset of the record
60 uint32_t rsiz; // size of the whole record
61 uint8_t magic; // magic number
62 uint8_t hash; // second hash value
63 uint64_t left; // offset of the left child record
64 uint64_t right; // offset of the right child record
65 uint32_t ksiz; // size of the key
66 uint32_t vsiz; // size of the value
67 uint16_t psiz; // size of the padding
68 const char *kbuf; // pointer to the key
69 const char *vbuf; // pointer to the value
70 uint64_t boff; // offset of the body
71 char *bbuf; // buffer of the body
72 } TCHREC;
73
74 typedef struct { // type of structure for a free block
75 uint64_t off; // offset of the block
76 uint32_t rsiz; // size of the block
77 } HDBFB;
78
79 enum { // enumeration for magic data
80 HDBMAGICREC = 0xc8, // for data block
81 HDBMAGICFB = 0xb0 // for free block
82 };
83
84 enum { // enumeration for duplication behavior
85 HDBPDOVER, // overwrite an existing value
86 HDBPDKEEP, // keep the existing value
87 HDBPDCAT, // concatenate values
88 HDBPDADDINT, // add an integer
89 HDBPDADDDBL, // add a real number
90 HDBPDPROC // process by a callback function
91 };
92
93 typedef struct { // type of structure for a duplication callback
94 TCPDPROC proc; // function pointer
95 void *op; // opaque pointer
96 } HDBPDPROCOP;
97
98
99 /* private macros */
100 #define HDBLOCKMETHOD(TC_hdb, TC_wr) \
101 ((TC_hdb)->mmtx ? tchdblockmethod((TC_hdb), (TC_wr)) : true)
102 #define HDBUNLOCKMETHOD(TC_hdb) \
103 ((TC_hdb)->mmtx ? tchdbunlockmethod(TC_hdb) : true)
104 #define HDBLOCKRECORD(TC_hdb, TC_bidx, TC_wr) \
105 ((TC_hdb)->mmtx ? tchdblockrecord((TC_hdb), (uint8_t)(TC_bidx), (TC_wr)) : true)
106 #define HDBUNLOCKRECORD(TC_hdb, TC_bidx) \
107 ((TC_hdb)->mmtx ? tchdbunlockrecord((TC_hdb), (uint8_t)(TC_bidx)) : true)
108 #define HDBLOCKALLRECORDS(TC_hdb, TC_wr) \
109 ((TC_hdb)->mmtx ? tchdblockallrecords((TC_hdb), (TC_wr)) : true)
110 #define HDBUNLOCKALLRECORDS(TC_hdb) \
111 ((TC_hdb)->mmtx ? tchdbunlockallrecords(TC_hdb) : true)
112 #define HDBLOCKDB(TC_hdb) \
113 ((TC_hdb)->mmtx ? tchdblockdb(TC_hdb) : true)
114 #define HDBUNLOCKDB(TC_hdb) \
115 ((TC_hdb)->mmtx ? tchdbunlockdb(TC_hdb) : true)
116 #define HDBLOCKWAL(TC_hdb) \
117 ((TC_hdb)->mmtx ? tchdblockwal(TC_hdb) : true)
118 #define HDBUNLOCKWAL(TC_hdb) \
119 ((TC_hdb)->mmtx ? tchdbunlockwal(TC_hdb) : true)
120 #define HDBTHREADYIELD(TC_hdb) \
121 do { if((TC_hdb)->mmtx) sched_yield(); } while(false)
122
123
124 /* private function prototypes */
125 static uint64_t tcgetprime(uint64_t num);
126 static bool tchdbseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size);
127 static bool tchdbseekread(TCHDB *hdb, off_t off, void *buf, size_t size);
128 static bool tchdbseekreadtry(TCHDB *hdb, off_t off, void *buf, size_t size);
129 static void tchdbdumpmeta(TCHDB *hdb, char *hbuf);
130 static void tchdbloadmeta(TCHDB *hdb, const char *hbuf);
131 static void tchdbclear(TCHDB *hdb);
132 static int32_t tchdbpadsize(TCHDB *hdb, uint64_t off);
133 static void tchdbsetflag(TCHDB *hdb, int flag, bool sign);
134 static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp);
135 static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx);
136 static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off);
137 static bool tchdbsavefbp(TCHDB *hdb);
138 static bool tchdbloadfbp(TCHDB *hdb);
139 static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum);
140 static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum);
141 static void tchdbfbpmerge(TCHDB *hdb);
142 static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz);
143 static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec);
144 static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz);
145 static void tchdbfbptrim(TCHDB *hdb, uint64_t base, uint64_t next, uint64_t off, uint32_t rsiz);
146 static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz);
147 static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff);
148 static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf);
149 static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec);
150 static bool tchdbremoverec(TCHDB *hdb, TCHREC *rec, char *rbuf, uint64_t bidx, off_t entoff);
151 static bool tchdbshiftrec(TCHDB *hdb, TCHREC *rec, char *rbuf, off_t destoff);
152 static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
153 static bool tchdbflushdrp(TCHDB *hdb);
154 static void tchdbcacheadjust(TCHDB *hdb);
155 static bool tchdbwalinit(TCHDB *hdb);
156 static bool tchdbwalwrite(TCHDB *hdb, uint64_t off, int64_t size);
157 static int tchdbwalrestore(TCHDB *hdb, const char *path);
158 static bool tchdbwalremove(TCHDB *hdb, const char *path);
159 static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode);
160 static bool tchdbcloseimpl(TCHDB *hdb);
161 static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash,
162 const char *vbuf, int vsiz, int dmode);
163 static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
164 uint8_t hash);
165 static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx,
166 uint8_t hash, const char *vbuf, int vsiz);
167 static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash);
168 static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash,
169 int *sp);
170 static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash,
171 char *vbuf, int max);
172 static char *tchdbgetnextimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp,
173 const char **vbp, int *vsp);
174 static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash);
175 static bool tchdbiterinitimpl(TCHDB *hdb);
176 static char *tchdbiternextimpl(TCHDB *hdb, int *sp);
177 static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr);
178 static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
179 static bool tchdbvanishimpl(TCHDB *hdb);
180 static bool tchdbcopyimpl(TCHDB *hdb, const char *path);
181 static bool tchdbdefragimpl(TCHDB *hdb, int64_t step);
182 static bool tchdbiterjumpimpl(TCHDB *hdb, const char *kbuf, int ksiz);
183 static bool tchdbforeachimpl(TCHDB *hdb, TCITER iter, void *op);
184 static bool tchdblockmethod(TCHDB *hdb, bool wr);
185 static bool tchdbunlockmethod(TCHDB *hdb);
186 static bool tchdblockrecord(TCHDB *hdb, uint8_t bidx, bool wr);
187 static bool tchdbunlockrecord(TCHDB *hdb, uint8_t bidx);
188 static bool tchdblockallrecords(TCHDB *hdb, bool wr);
189 static bool tchdbunlockallrecords(TCHDB *hdb);
190 static bool tchdblockdb(TCHDB *hdb);
191 static bool tchdbunlockdb(TCHDB *hdb);
192 static bool tchdblockwal(TCHDB *hdb);
193 static bool tchdbunlockwal(TCHDB *hdb);
194
195
196 /* debugging function prototypes */
197 void tchdbprintmeta(TCHDB *hdb);
198 void tchdbprintrec(TCHDB *hdb, TCHREC *rec);
199
200
201
202 /*************************************************************************************************
203 * API
204 *************************************************************************************************/
205
206
207 /* Get the message string corresponding to an error code. */
tchdberrmsg(int ecode)208 const char *tchdberrmsg(int ecode){
209 return tcerrmsg(ecode);
210 }
211
212
213 /* Create a hash database object. */
tchdbnew(void)214 TCHDB *tchdbnew(void){
215 TCHDB *hdb;
216 TCMALLOC(hdb, sizeof(*hdb));
217 tchdbclear(hdb);
218 return hdb;
219 }
220
221
222 /* Delete a hash database object. */
tchdbdel(TCHDB * hdb)223 void tchdbdel(TCHDB *hdb){
224 assert(hdb);
225 if(hdb->fd >= 0) tchdbclose(hdb);
226 if(hdb->mmtx){
227 pthread_key_delete(*(pthread_key_t *)hdb->eckey);
228 pthread_mutex_destroy(hdb->wmtx);
229 pthread_mutex_destroy(hdb->dmtx);
230 for(int i = UINT8_MAX; i >= 0; i--){
231 pthread_rwlock_destroy((pthread_rwlock_t *)hdb->rmtxs + i);
232 }
233 pthread_rwlock_destroy(hdb->mmtx);
234 TCFREE(hdb->eckey);
235 TCFREE(hdb->wmtx);
236 TCFREE(hdb->dmtx);
237 TCFREE(hdb->rmtxs);
238 TCFREE(hdb->mmtx);
239 }
240 TCFREE(hdb);
241 }
242
243
244 /* Get the last happened error code of a hash database object. */
tchdbecode(TCHDB * hdb)245 int tchdbecode(TCHDB *hdb){
246 assert(hdb);
247 return hdb->mmtx ?
248 (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)hdb->eckey) : hdb->ecode;
249 }
250
251
252 /* Set mutual exclusion control of a hash database object for threading. */
tchdbsetmutex(TCHDB * hdb)253 bool tchdbsetmutex(TCHDB *hdb){
254 assert(hdb);
255 if(!TCUSEPTHREAD) return true;
256 if(hdb->mmtx || hdb->fd >= 0){
257 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
258 return false;
259 }
260 pthread_mutexattr_t rma;
261 pthread_mutexattr_init(&rma);
262 TCMALLOC(hdb->mmtx, sizeof(pthread_rwlock_t));
263 TCMALLOC(hdb->rmtxs, (UINT8_MAX + 1) * sizeof(pthread_rwlock_t));
264 TCMALLOC(hdb->dmtx, sizeof(pthread_mutex_t));
265 TCMALLOC(hdb->wmtx, sizeof(pthread_mutex_t));
266 TCMALLOC(hdb->eckey, sizeof(pthread_key_t));
267 bool err = false;
268 if(pthread_mutexattr_settype(&rma, PTHREAD_MUTEX_RECURSIVE) != 0) err = true;
269 if(pthread_rwlock_init(hdb->mmtx, NULL) != 0) err = true;
270 for(int i = 0; i <= UINT8_MAX; i++){
271 if(pthread_rwlock_init((pthread_rwlock_t *)hdb->rmtxs + i, NULL) != 0) err = true;
272 }
273 if(pthread_mutex_init(hdb->dmtx, &rma) != 0) err = true;
274 if(pthread_mutex_init(hdb->wmtx, NULL) != 0) err = true;
275 if(pthread_key_create(hdb->eckey, NULL) != 0) err = true;
276 if(err){
277 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
278 pthread_mutexattr_destroy(&rma);
279 TCFREE(hdb->eckey);
280 TCFREE(hdb->wmtx);
281 TCFREE(hdb->dmtx);
282 TCFREE(hdb->rmtxs);
283 TCFREE(hdb->mmtx);
284 hdb->eckey = NULL;
285 hdb->wmtx = NULL;
286 hdb->dmtx = NULL;
287 hdb->rmtxs = NULL;
288 hdb->mmtx = NULL;
289 return false;
290 }
291 pthread_mutexattr_destroy(&rma);
292 return true;
293 }
294
295
296 /* Set the tuning parameters of a hash database object. */
tchdbtune(TCHDB * hdb,int64_t bnum,int8_t apow,int8_t fpow,uint8_t opts)297 bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
298 assert(hdb);
299 if(hdb->fd >= 0){
300 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
301 return false;
302 }
303 hdb->bnum = (bnum > 0) ? tcgetprime(bnum) : HDBDEFBNUM;
304 hdb->apow = (apow >= 0) ? tclmin(apow, HDBMAXAPOW) : HDBDEFAPOW;
305 hdb->fpow = (fpow >= 0) ? tclmin(fpow, HDBMAXFPOW) : HDBDEFFPOW;
306 hdb->opts = opts;
307 if(!_tc_deflate) hdb->opts &= ~HDBTDEFLATE;
308 if(!_tc_bzcompress) hdb->opts &= ~HDBTBZIP;
309 return true;
310 }
311
312
313 /* Set the caching parameters of a hash database object. */
tchdbsetcache(TCHDB * hdb,int32_t rcnum)314 bool tchdbsetcache(TCHDB *hdb, int32_t rcnum){
315 assert(hdb);
316 if(hdb->fd >= 0){
317 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
318 return false;
319 }
320 hdb->rcnum = (rcnum > 0) ? tclmin(tclmax(rcnum, HDBCACHEOUT * 2), INT_MAX / 4) : 0;
321 return true;
322 }
323
324
325 /* Set the size of the extra mapped memory of a hash database object. */
tchdbsetxmsiz(TCHDB * hdb,int64_t xmsiz)326 bool tchdbsetxmsiz(TCHDB *hdb, int64_t xmsiz){
327 assert(hdb);
328 if(hdb->fd >= 0){
329 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
330 return false;
331 }
332 hdb->xmsiz = (xmsiz > 0) ? tcpagealign(xmsiz) : 0;
333 return true;
334 }
335
336
337 /* Set the unit step number of auto defragmentation of a hash database object. */
tchdbsetdfunit(TCHDB * hdb,int32_t dfunit)338 bool tchdbsetdfunit(TCHDB *hdb, int32_t dfunit){
339 assert(hdb);
340 if(hdb->fd >= 0){
341 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
342 return false;
343 }
344 hdb->dfunit = (dfunit > 0) ? dfunit : 0;
345 return true;
346 }
347
348
349 /* Open a database file and connect a hash database object. */
tchdbopen(TCHDB * hdb,const char * path,int omode)350 bool tchdbopen(TCHDB *hdb, const char *path, int omode){
351 assert(hdb && path);
352 if(!HDBLOCKMETHOD(hdb, true)) return false;
353 if(hdb->fd >= 0){
354 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
355 HDBUNLOCKMETHOD(hdb);
356 return false;
357 }
358 char *rpath = tcrealpath(path);
359 if(!rpath){
360 int ecode = TCEOPEN;
361 switch(errno){
362 case EACCES: ecode = TCENOPERM; break;
363 case ENOENT: ecode = TCENOFILE; break;
364 case ENOTDIR: ecode = TCENOFILE; break;
365 }
366 tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__);
367 HDBUNLOCKMETHOD(hdb);
368 return false;
369 }
370 if(!tcpathlock(rpath)){
371 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
372 TCFREE(rpath);
373 HDBUNLOCKMETHOD(hdb);
374 return false;
375 }
376 bool rv = tchdbopenimpl(hdb, path, omode);
377 if(rv){
378 hdb->rpath = rpath;
379 } else {
380 tcpathunlock(rpath);
381 TCFREE(rpath);
382 }
383 HDBUNLOCKMETHOD(hdb);
384 return rv;
385 }
386
387
388 /* Close a database object. */
tchdbclose(TCHDB * hdb)389 bool tchdbclose(TCHDB *hdb){
390 assert(hdb);
391 if(!HDBLOCKMETHOD(hdb, true)) return false;
392 if(hdb->fd < 0){
393 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
394 HDBUNLOCKMETHOD(hdb);
395 return false;
396 }
397 bool rv = tchdbcloseimpl(hdb);
398 tcpathunlock(hdb->rpath);
399 TCFREE(hdb->rpath);
400 hdb->rpath = NULL;
401 HDBUNLOCKMETHOD(hdb);
402 return rv;
403 }
404
405
406 /* Store a record into a hash database object. */
tchdbput(TCHDB * hdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)407 bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
408 assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
409 if(!HDBLOCKMETHOD(hdb, false)) return false;
410 uint8_t hash;
411 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
412 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
413 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
414 HDBUNLOCKMETHOD(hdb);
415 return false;
416 }
417 if(hdb->async && !tchdbflushdrp(hdb)){
418 HDBUNLOCKMETHOD(hdb);
419 return false;
420 }
421 if(!HDBLOCKRECORD(hdb, bidx, true)){
422 HDBUNLOCKMETHOD(hdb);
423 return false;
424 }
425 if(hdb->zmode){
426 char *zbuf;
427 if(hdb->opts & HDBTDEFLATE){
428 zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
429 } else if(hdb->opts & HDBTBZIP){
430 zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz);
431 } else if(hdb->opts & HDBTTCBS){
432 zbuf = tcbsencode(vbuf, vsiz, &vsiz);
433 } else {
434 zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop);
435 }
436 if(!zbuf){
437 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
438 HDBUNLOCKRECORD(hdb, bidx);
439 HDBUNLOCKMETHOD(hdb);
440 return false;
441 }
442 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDOVER);
443 TCFREE(zbuf);
444 HDBUNLOCKRECORD(hdb, bidx);
445 HDBUNLOCKMETHOD(hdb);
446 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
447 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
448 return rv;
449 }
450 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDOVER);
451 HDBUNLOCKRECORD(hdb, bidx);
452 HDBUNLOCKMETHOD(hdb);
453 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
454 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
455 return rv;
456 }
457
458
459 /* Store a string record into a hash database object. */
tchdbput2(TCHDB * hdb,const char * kstr,const char * vstr)460 bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr){
461 assert(hdb && kstr && vstr);
462 return tchdbput(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
463 }
464
465
466 /* Store a new record into a hash database object. */
tchdbputkeep(TCHDB * hdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)467 bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
468 assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
469 if(!HDBLOCKMETHOD(hdb, false)) return false;
470 uint8_t hash;
471 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
472 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
473 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
474 HDBUNLOCKMETHOD(hdb);
475 return false;
476 }
477 if(hdb->async && !tchdbflushdrp(hdb)){
478 HDBUNLOCKMETHOD(hdb);
479 return false;
480 }
481 if(!HDBLOCKRECORD(hdb, bidx, true)){
482 HDBUNLOCKMETHOD(hdb);
483 return false;
484 }
485 if(hdb->zmode){
486 char *zbuf;
487 if(hdb->opts & HDBTDEFLATE){
488 zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
489 } else if(hdb->opts & HDBTBZIP){
490 zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz);
491 } else if(hdb->opts & HDBTTCBS){
492 zbuf = tcbsencode(vbuf, vsiz, &vsiz);
493 } else {
494 zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop);
495 }
496 if(!zbuf){
497 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
498 HDBUNLOCKRECORD(hdb, bidx);
499 HDBUNLOCKMETHOD(hdb);
500 return false;
501 }
502 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDKEEP);
503 TCFREE(zbuf);
504 HDBUNLOCKRECORD(hdb, bidx);
505 HDBUNLOCKMETHOD(hdb);
506 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
507 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
508 return rv;
509 }
510 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDKEEP);
511 HDBUNLOCKRECORD(hdb, bidx);
512 HDBUNLOCKMETHOD(hdb);
513 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
514 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
515 return rv;
516 }
517
518
519 /* Store a new string record into a hash database object. */
tchdbputkeep2(TCHDB * hdb,const char * kstr,const char * vstr)520 bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr){
521 return tchdbputkeep(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
522 }
523
524
525 /* Concatenate a value at the end of the existing record in a hash database object. */
tchdbputcat(TCHDB * hdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)526 bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
527 assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
528 if(!HDBLOCKMETHOD(hdb, false)) return false;
529 uint8_t hash;
530 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
531 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
532 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
533 HDBUNLOCKMETHOD(hdb);
534 return false;
535 }
536 if(hdb->async && !tchdbflushdrp(hdb)){
537 HDBUNLOCKMETHOD(hdb);
538 return false;
539 }
540 if(!HDBLOCKRECORD(hdb, bidx, true)){
541 HDBUNLOCKMETHOD(hdb);
542 return false;
543 }
544 if(hdb->zmode){
545 char *zbuf;
546 int osiz;
547 char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz);
548 if(obuf){
549 TCREALLOC(obuf, obuf, osiz + vsiz + 1);
550 memcpy(obuf + osiz, vbuf, vsiz);
551 if(hdb->opts & HDBTDEFLATE){
552 zbuf = _tc_deflate(obuf, osiz + vsiz, &vsiz, _TCZMRAW);
553 } else if(hdb->opts & HDBTBZIP){
554 zbuf = _tc_bzcompress(obuf, osiz + vsiz, &vsiz);
555 } else if(hdb->opts & HDBTTCBS){
556 zbuf = tcbsencode(obuf, osiz + vsiz, &vsiz);
557 } else {
558 zbuf = hdb->enc(obuf, osiz + vsiz, &vsiz, hdb->encop);
559 }
560 TCFREE(obuf);
561 } else {
562 if(hdb->opts & HDBTDEFLATE){
563 zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
564 } else if(hdb->opts & HDBTBZIP){
565 zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz);
566 } else if(hdb->opts & HDBTTCBS){
567 zbuf = tcbsencode(vbuf, vsiz, &vsiz);
568 } else {
569 zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop);
570 }
571 }
572 if(!zbuf){
573 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
574 HDBUNLOCKRECORD(hdb, bidx);
575 HDBUNLOCKMETHOD(hdb);
576 return false;
577 }
578 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDOVER);
579 TCFREE(zbuf);
580 HDBUNLOCKRECORD(hdb, bidx);
581 HDBUNLOCKMETHOD(hdb);
582 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
583 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
584 return rv;
585 }
586 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDCAT);
587 HDBUNLOCKRECORD(hdb, bidx);
588 HDBUNLOCKMETHOD(hdb);
589 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
590 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
591 return rv;
592 }
593
594
595 /* Concatenate a string value at the end of the existing record in a hash database object. */
tchdbputcat2(TCHDB * hdb,const char * kstr,const char * vstr)596 bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr){
597 assert(hdb && kstr && vstr);
598 return tchdbputcat(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
599 }
600
601
602 /* Store a record into a hash database object in asynchronous fashion. */
tchdbputasync(TCHDB * hdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)603 bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
604 assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
605 if(!HDBLOCKMETHOD(hdb, true)) return false;
606 uint8_t hash;
607 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
608 hdb->async = true;
609 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
610 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
611 HDBUNLOCKMETHOD(hdb);
612 return false;
613 }
614 if(hdb->zmode){
615 char *zbuf;
616 if(hdb->opts & HDBTDEFLATE){
617 zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
618 } else if(hdb->opts & HDBTBZIP){
619 zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz);
620 } else if(hdb->opts & HDBTTCBS){
621 zbuf = tcbsencode(vbuf, vsiz, &vsiz);
622 } else {
623 zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop);
624 }
625 if(!zbuf){
626 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
627 HDBUNLOCKMETHOD(hdb);
628 return false;
629 }
630 bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz);
631 TCFREE(zbuf);
632 HDBUNLOCKMETHOD(hdb);
633 return rv;
634 }
635 bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz);
636 HDBUNLOCKMETHOD(hdb);
637 return rv;
638 }
639
640
641 /* Store a string record into a hash database object in asynchronous fashion. */
tchdbputasync2(TCHDB * hdb,const char * kstr,const char * vstr)642 bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr){
643 assert(hdb && kstr && vstr);
644 return tchdbputasync(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
645 }
646
647
648 /* Remove a record of a hash database object. */
tchdbout(TCHDB * hdb,const void * kbuf,int ksiz)649 bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz){
650 assert(hdb && kbuf && ksiz >= 0);
651 if(!HDBLOCKMETHOD(hdb, false)) return false;
652 uint8_t hash;
653 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
654 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
655 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
656 HDBUNLOCKMETHOD(hdb);
657 return false;
658 }
659 if(hdb->async && !tchdbflushdrp(hdb)){
660 HDBUNLOCKMETHOD(hdb);
661 return false;
662 }
663 if(!HDBLOCKRECORD(hdb, bidx, true)){
664 HDBUNLOCKMETHOD(hdb);
665 return false;
666 }
667 bool rv = tchdboutimpl(hdb, kbuf, ksiz, bidx, hash);
668 HDBUNLOCKRECORD(hdb, bidx);
669 HDBUNLOCKMETHOD(hdb);
670 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
671 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
672 return rv;
673 }
674
675
676 /* Remove a string record of a hash database object. */
tchdbout2(TCHDB * hdb,const char * kstr)677 bool tchdbout2(TCHDB *hdb, const char *kstr){
678 assert(hdb && kstr);
679 return tchdbout(hdb, kstr, strlen(kstr));
680 }
681
682
683 /* Retrieve a record in a hash database object. */
tchdbget(TCHDB * hdb,const void * kbuf,int ksiz,int * sp)684 void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp){
685 assert(hdb && kbuf && ksiz >= 0 && sp);
686 if(!HDBLOCKMETHOD(hdb, false)) return NULL;
687 uint8_t hash;
688 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
689 if(hdb->fd < 0){
690 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
691 HDBUNLOCKMETHOD(hdb);
692 return NULL;
693 }
694 if(hdb->async && !tchdbflushdrp(hdb)){
695 HDBUNLOCKMETHOD(hdb);
696 return NULL;
697 }
698 if(!HDBLOCKRECORD(hdb, bidx, false)){
699 HDBUNLOCKMETHOD(hdb);
700 return false;
701 }
702 char *rv = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, sp);
703 HDBUNLOCKRECORD(hdb, bidx);
704 HDBUNLOCKMETHOD(hdb);
705 return rv;
706 }
707
708
709 /* Retrieve a string record in a hash database object. */
tchdbget2(TCHDB * hdb,const char * kstr)710 char *tchdbget2(TCHDB *hdb, const char *kstr){
711 assert(hdb && kstr);
712 int vsiz;
713 return tchdbget(hdb, kstr, strlen(kstr), &vsiz);
714 }
715
716
717 /* Retrieve a record in a hash database object and write the value into a buffer. */
tchdbget3(TCHDB * hdb,const void * kbuf,int ksiz,void * vbuf,int max)718 int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max){
719 assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0);
720 if(!HDBLOCKMETHOD(hdb, false)) return -1;
721 uint8_t hash;
722 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
723 if(hdb->fd < 0){
724 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
725 HDBUNLOCKMETHOD(hdb);
726 return -1;
727 }
728 if(hdb->async && !tchdbflushdrp(hdb)){
729 HDBUNLOCKMETHOD(hdb);
730 return -1;
731 }
732 if(!HDBLOCKRECORD(hdb, bidx, false)){
733 HDBUNLOCKMETHOD(hdb);
734 return -1;
735 }
736 int rv = tchdbgetintobuf(hdb, kbuf, ksiz, bidx, hash, vbuf, max);
737 HDBUNLOCKRECORD(hdb, bidx);
738 HDBUNLOCKMETHOD(hdb);
739 return rv;
740 }
741
742
743 /* Get the size of the value of a record in a hash database object. */
tchdbvsiz(TCHDB * hdb,const void * kbuf,int ksiz)744 int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz){
745 assert(hdb && kbuf && ksiz >= 0);
746 if(!HDBLOCKMETHOD(hdb, false)) return -1;
747 uint8_t hash;
748 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
749 if(hdb->fd < 0){
750 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
751 HDBUNLOCKMETHOD(hdb);
752 return -1;
753 }
754 if(hdb->async && !tchdbflushdrp(hdb)){
755 HDBUNLOCKMETHOD(hdb);
756 return -1;
757 }
758 if(!HDBLOCKRECORD(hdb, bidx, false)){
759 HDBUNLOCKMETHOD(hdb);
760 return -1;
761 }
762 int rv = tchdbvsizimpl(hdb, kbuf, ksiz, bidx, hash);
763 HDBUNLOCKRECORD(hdb, bidx);
764 HDBUNLOCKMETHOD(hdb);
765 return rv;
766 }
767
768
769 /* Get the size of the value of a string record in a hash database object. */
tchdbvsiz2(TCHDB * hdb,const char * kstr)770 int tchdbvsiz2(TCHDB *hdb, const char *kstr){
771 assert(hdb && kstr);
772 return tchdbvsiz(hdb, kstr, strlen(kstr));
773 }
774
775
776 /* Initialize the iterator of a hash database object. */
tchdbiterinit(TCHDB * hdb)777 bool tchdbiterinit(TCHDB *hdb){
778 assert(hdb);
779 if(!HDBLOCKMETHOD(hdb, true)) return false;
780 if(hdb->fd < 0){
781 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
782 HDBUNLOCKMETHOD(hdb);
783 return false;
784 }
785 if(hdb->async && !tchdbflushdrp(hdb)){
786 HDBUNLOCKMETHOD(hdb);
787 return false;
788 }
789 bool rv = tchdbiterinitimpl(hdb);
790 HDBUNLOCKMETHOD(hdb);
791 return rv;
792 }
793
794
795 /* Get the next key of the iterator of a hash database object. */
tchdbiternext(TCHDB * hdb,int * sp)796 void *tchdbiternext(TCHDB *hdb, int *sp){
797 assert(hdb && sp);
798 if(!HDBLOCKMETHOD(hdb, true)) return NULL;
799 if(hdb->fd < 0 || hdb->iter < 1){
800 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
801 HDBUNLOCKMETHOD(hdb);
802 return NULL;
803 }
804 if(hdb->async && !tchdbflushdrp(hdb)){
805 HDBUNLOCKMETHOD(hdb);
806 return NULL;
807 }
808 char *rv = tchdbiternextimpl(hdb, sp);
809 HDBUNLOCKMETHOD(hdb);
810 return rv;
811 }
812
813
814 /* Get the next key string of the iterator of a hash database object. */
tchdbiternext2(TCHDB * hdb)815 char *tchdbiternext2(TCHDB *hdb){
816 assert(hdb);
817 int vsiz;
818 return tchdbiternext(hdb, &vsiz);
819 }
820
821
822 /* Get the next extensible objects of the iterator of a hash database object. */
tchdbiternext3(TCHDB * hdb,TCXSTR * kxstr,TCXSTR * vxstr)823 bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){
824 assert(hdb && kxstr && vxstr);
825 if(!HDBLOCKMETHOD(hdb, true)) return false;
826 if(hdb->fd < 0 || hdb->iter < 1){
827 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
828 HDBUNLOCKMETHOD(hdb);
829 return false;
830 }
831 if(hdb->async && !tchdbflushdrp(hdb)){
832 HDBUNLOCKMETHOD(hdb);
833 return false;
834 }
835 bool rv = tchdbiternextintoxstr(hdb, kxstr, vxstr);
836 HDBUNLOCKMETHOD(hdb);
837 return rv;
838 }
839
840
841 /* Get forward matching keys in a hash database object. */
tchdbfwmkeys(TCHDB * hdb,const void * pbuf,int psiz,int max)842 TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max){
843 assert(hdb && pbuf && psiz >= 0);
844 TCLIST* keys = tclistnew();
845 if(!HDBLOCKMETHOD(hdb, true)) return keys;
846 if(hdb->fd < 0){
847 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
848 HDBUNLOCKMETHOD(hdb);
849 return keys;
850 }
851 if(hdb->async && !tchdbflushdrp(hdb)){
852 HDBUNLOCKMETHOD(hdb);
853 return keys;
854 }
855 if(max < 0) max = INT_MAX;
856 uint64_t iter = hdb->iter;
857 tchdbiterinitimpl(hdb);
858 char *kbuf;
859 int ksiz;
860 while(TCLISTNUM(keys) < max && (kbuf = tchdbiternextimpl(hdb, &ksiz)) != NULL){
861 if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)){
862 tclistpushmalloc(keys, kbuf, ksiz);
863 } else {
864 TCFREE(kbuf);
865 }
866 }
867 hdb->iter = iter;
868 HDBUNLOCKMETHOD(hdb);
869 return keys;
870 }
871
872
873 /* Get forward matching string keys in a hash database object. */
tchdbfwmkeys2(TCHDB * hdb,const char * pstr,int max)874 TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max){
875 assert(hdb && pstr);
876 return tchdbfwmkeys(hdb, pstr, strlen(pstr), max);
877 }
878
879
880 /* Add an integer to a record in a hash database object. */
tchdbaddint(TCHDB * hdb,const void * kbuf,int ksiz,int num)881 int tchdbaddint(TCHDB *hdb, const void *kbuf, int ksiz, int num){
882 assert(hdb && kbuf && ksiz >= 0);
883 if(!HDBLOCKMETHOD(hdb, false)) return INT_MIN;
884 uint8_t hash;
885 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
886 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
887 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
888 HDBUNLOCKMETHOD(hdb);
889 return INT_MIN;
890 }
891 if(hdb->async && !tchdbflushdrp(hdb)){
892 HDBUNLOCKMETHOD(hdb);
893 return INT_MIN;
894 }
895 if(!HDBLOCKRECORD(hdb, bidx, true)){
896 HDBUNLOCKMETHOD(hdb);
897 return INT_MIN;
898 }
899 if(hdb->zmode){
900 char *zbuf;
901 int osiz;
902 char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz);
903 if(obuf){
904 if(osiz != sizeof(num)){
905 tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
906 TCFREE(obuf);
907 HDBUNLOCKRECORD(hdb, bidx);
908 HDBUNLOCKMETHOD(hdb);
909 return INT_MIN;
910 }
911 num += *(int *)obuf;
912 TCFREE(obuf);
913 }
914 int zsiz;
915 if(hdb->opts & HDBTDEFLATE){
916 zbuf = _tc_deflate((char *)&num, sizeof(num), &zsiz, _TCZMRAW);
917 } else if(hdb->opts & HDBTBZIP){
918 zbuf = _tc_bzcompress((char *)&num, sizeof(num), &zsiz);
919 } else if(hdb->opts & HDBTTCBS){
920 zbuf = tcbsencode((char *)&num, sizeof(num), &zsiz);
921 } else {
922 zbuf = hdb->enc((char *)&num, sizeof(num), &zsiz, hdb->encop);
923 }
924 if(!zbuf){
925 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
926 HDBUNLOCKRECORD(hdb, bidx);
927 HDBUNLOCKMETHOD(hdb);
928 return INT_MIN;
929 }
930 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, zsiz, HDBPDOVER);
931 TCFREE(zbuf);
932 HDBUNLOCKRECORD(hdb, bidx);
933 HDBUNLOCKMETHOD(hdb);
934 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
935 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
936 return rv ? num : INT_MIN;
937 }
938 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, (char *)&num, sizeof(num), HDBPDADDINT);
939 HDBUNLOCKRECORD(hdb, bidx);
940 HDBUNLOCKMETHOD(hdb);
941 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
942 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
943 return rv ? num : INT_MIN;
944 }
945
946
947 /* Add a real number to a record in a hash database object. */
tchdbadddouble(TCHDB * hdb,const void * kbuf,int ksiz,double num)948 double tchdbadddouble(TCHDB *hdb, const void *kbuf, int ksiz, double num){
949 assert(hdb && kbuf && ksiz >= 0);
950 if(!HDBLOCKMETHOD(hdb, false)) return nan("");
951 uint8_t hash;
952 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
953 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
954 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
955 HDBUNLOCKMETHOD(hdb);
956 return nan("");
957 }
958 if(hdb->async && !tchdbflushdrp(hdb)){
959 HDBUNLOCKMETHOD(hdb);
960 return nan("");
961 }
962 if(!HDBLOCKRECORD(hdb, bidx, true)){
963 HDBUNLOCKMETHOD(hdb);
964 return nan("");
965 }
966 if(hdb->zmode){
967 char *zbuf;
968 int osiz;
969 char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz);
970 if(obuf){
971 if(osiz != sizeof(num)){
972 tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
973 TCFREE(obuf);
974 HDBUNLOCKRECORD(hdb, bidx);
975 HDBUNLOCKMETHOD(hdb);
976 return nan("");
977 }
978 num += *(double *)obuf;
979 TCFREE(obuf);
980 }
981 int zsiz;
982 if(hdb->opts & HDBTDEFLATE){
983 zbuf = _tc_deflate((char *)&num, sizeof(num), &zsiz, _TCZMRAW);
984 } else if(hdb->opts & HDBTBZIP){
985 zbuf = _tc_bzcompress((char *)&num, sizeof(num), &zsiz);
986 } else if(hdb->opts & HDBTTCBS){
987 zbuf = tcbsencode((char *)&num, sizeof(num), &zsiz);
988 } else {
989 zbuf = hdb->enc((char *)&num, sizeof(num), &zsiz, hdb->encop);
990 }
991 if(!zbuf){
992 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
993 HDBUNLOCKRECORD(hdb, bidx);
994 HDBUNLOCKMETHOD(hdb);
995 return nan("");
996 }
997 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, zsiz, HDBPDOVER);
998 TCFREE(zbuf);
999 HDBUNLOCKRECORD(hdb, bidx);
1000 HDBUNLOCKMETHOD(hdb);
1001 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
1002 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
1003 return rv ? num : nan("");
1004 }
1005 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, (char *)&num, sizeof(num), HDBPDADDDBL);
1006 HDBUNLOCKRECORD(hdb, bidx);
1007 HDBUNLOCKMETHOD(hdb);
1008 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
1009 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
1010 return rv ? num : nan("");
1011 }
1012
1013
1014 /* Synchronize updated contents of a hash database object with the file and the device. */
tchdbsync(TCHDB * hdb)1015 bool tchdbsync(TCHDB *hdb){
1016 assert(hdb);
1017 if(!HDBLOCKMETHOD(hdb, true)) return false;
1018 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->tran){
1019 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1020 HDBUNLOCKMETHOD(hdb);
1021 return false;
1022 }
1023 if(hdb->async && !tchdbflushdrp(hdb)){
1024 HDBUNLOCKMETHOD(hdb);
1025 return false;
1026 }
1027 bool rv = tchdbmemsync(hdb, true);
1028 HDBUNLOCKMETHOD(hdb);
1029 return rv;
1030 }
1031
1032
1033 /* Optimize the file of a hash database object. */
tchdboptimize(TCHDB * hdb,int64_t bnum,int8_t apow,int8_t fpow,uint8_t opts)1034 bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
1035 assert(hdb);
1036 if(!HDBLOCKMETHOD(hdb, true)) return false;
1037 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->tran){
1038 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1039 HDBUNLOCKMETHOD(hdb);
1040 return false;
1041 }
1042 if(hdb->async && !tchdbflushdrp(hdb)){
1043 HDBUNLOCKMETHOD(hdb);
1044 return false;
1045 }
1046 HDBTHREADYIELD(hdb);
1047 bool rv = tchdboptimizeimpl(hdb, bnum, apow, fpow, opts);
1048 HDBUNLOCKMETHOD(hdb);
1049 return rv;
1050 }
1051
1052
1053 /* Remove all records of a hash database object. */
tchdbvanish(TCHDB * hdb)1054 bool tchdbvanish(TCHDB *hdb){
1055 assert(hdb);
1056 if(!HDBLOCKMETHOD(hdb, true)) return false;
1057 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->tran){
1058 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1059 HDBUNLOCKMETHOD(hdb);
1060 return false;
1061 }
1062 if(hdb->async && !tchdbflushdrp(hdb)){
1063 HDBUNLOCKMETHOD(hdb);
1064 return false;
1065 }
1066 HDBTHREADYIELD(hdb);
1067 bool rv = tchdbvanishimpl(hdb);
1068 HDBUNLOCKMETHOD(hdb);
1069 return rv;
1070 }
1071
1072
1073 /* Copy the database file of a hash database object. */
tchdbcopy(TCHDB * hdb,const char * path)1074 bool tchdbcopy(TCHDB *hdb, const char *path){
1075 assert(hdb && path);
1076 if(!HDBLOCKMETHOD(hdb, false)) return false;
1077 if(hdb->fd < 0){
1078 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1079 HDBUNLOCKMETHOD(hdb);
1080 return false;
1081 }
1082 if(hdb->async && !tchdbflushdrp(hdb)){
1083 HDBUNLOCKMETHOD(hdb);
1084 return false;
1085 }
1086 if(!HDBLOCKALLRECORDS(hdb, false)){
1087 HDBUNLOCKMETHOD(hdb);
1088 return false;
1089 }
1090 HDBTHREADYIELD(hdb);
1091 bool rv = tchdbcopyimpl(hdb, path);
1092 HDBUNLOCKALLRECORDS(hdb);
1093 HDBUNLOCKMETHOD(hdb);
1094 return rv;
1095 }
1096
1097
1098 /* Begin the transaction of a hash database object. */
tchdbtranbegin(TCHDB * hdb)1099 bool tchdbtranbegin(TCHDB *hdb){
1100 assert(hdb);
1101 for(double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2){
1102 if(!HDBLOCKMETHOD(hdb, true)) return false;
1103 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->fatal){
1104 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1105 HDBUNLOCKMETHOD(hdb);
1106 return false;
1107 }
1108 if(!hdb->tran) break;
1109 HDBUNLOCKMETHOD(hdb);
1110 if(wsec > 1.0) wsec = 1.0;
1111 tcsleep(wsec);
1112 }
1113 if(hdb->async && !tchdbflushdrp(hdb)){
1114 HDBUNLOCKMETHOD(hdb);
1115 return false;
1116 }
1117 if(!tchdbmemsync(hdb, false)){
1118 HDBUNLOCKMETHOD(hdb);
1119 return false;
1120 }
1121 if((hdb->omode & HDBOTSYNC) && fsync(hdb->fd) == -1){
1122 tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__);
1123 return false;
1124 }
1125 if(hdb->walfd < 0){
1126 char *tpath = tcsprintf("%s%c%s", hdb->path, MYEXTCHR, HDBWALSUFFIX);
1127 int walfd = open(tpath, O_RDWR | O_CREAT | O_TRUNC, HDBFILEMODE);
1128 TCFREE(tpath);
1129 if(walfd < 0){
1130 int ecode = TCEOPEN;
1131 switch(errno){
1132 case EACCES: ecode = TCENOPERM; break;
1133 case ENOENT: ecode = TCENOFILE; break;
1134 case ENOTDIR: ecode = TCENOFILE; break;
1135 }
1136 tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__);
1137 HDBUNLOCKMETHOD(hdb);
1138 return false;
1139 }
1140 hdb->walfd = walfd;
1141 }
1142 tchdbsetflag(hdb, HDBFOPEN, false);
1143 if(!tchdbwalinit(hdb)){
1144 tchdbsetflag(hdb, HDBFOPEN, true);
1145 HDBUNLOCKMETHOD(hdb);
1146 return false;
1147 }
1148 tchdbsetflag(hdb, HDBFOPEN, true);
1149 hdb->tran = true;
1150 HDBUNLOCKMETHOD(hdb);
1151 return true;
1152 }
1153
1154
1155 /* Commit the transaction of a hash database object. */
tchdbtrancommit(TCHDB * hdb)1156 bool tchdbtrancommit(TCHDB *hdb){
1157 assert(hdb);
1158 if(!HDBLOCKMETHOD(hdb, true)) return false;
1159 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->fatal || !hdb->tran){
1160 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1161 HDBUNLOCKMETHOD(hdb);
1162 return false;
1163 }
1164 bool err = false;
1165 if(hdb->async && !tchdbflushdrp(hdb)) err = true;
1166 if(!tchdbmemsync(hdb, hdb->omode & HDBOTSYNC)) err = true;
1167 if(!err && ftruncate(hdb->walfd, 0) == -1){
1168 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
1169 err = true;
1170 }
1171 hdb->tran = false;
1172 HDBUNLOCKMETHOD(hdb);
1173 return !err;
1174 }
1175
1176
1177 /* Abort the transaction of a hash database object. */
tchdbtranabort(TCHDB * hdb)1178 bool tchdbtranabort(TCHDB *hdb){
1179 assert(hdb);
1180 if(!HDBLOCKMETHOD(hdb, true)) return false;
1181 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || !hdb->tran){
1182 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1183 HDBUNLOCKMETHOD(hdb);
1184 return false;
1185 }
1186 bool err = false;
1187 if(hdb->async && !tchdbflushdrp(hdb)) err = true;
1188 if(!tchdbmemsync(hdb, false)) err = true;
1189 if(!tchdbwalrestore(hdb, hdb->path)) err = true;
1190 char hbuf[HDBHEADSIZ];
1191 if(lseek(hdb->fd, 0, SEEK_SET) == -1){
1192 tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__);
1193 err = false;
1194 } else if(!tcread(hdb->fd, hbuf, HDBHEADSIZ)){
1195 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
1196 err = false;
1197 } else {
1198 tchdbloadmeta(hdb, hbuf);
1199 }
1200 hdb->dfcur = hdb->frec;
1201 hdb->iter = 0;
1202 hdb->xfsiz = 0;
1203 hdb->fbpnum = 0;
1204 if(hdb->recc) tcmdbvanish(hdb->recc);
1205 hdb->tran = false;
1206 HDBUNLOCKMETHOD(hdb);
1207 return !err;
1208 }
1209
1210
1211 /* Get the file path of a hash database object. */
tchdbpath(TCHDB * hdb)1212 const char *tchdbpath(TCHDB *hdb){
1213 assert(hdb);
1214 if(!HDBLOCKMETHOD(hdb, false)) return NULL;
1215 if(hdb->fd < 0){
1216 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1217 HDBUNLOCKMETHOD(hdb);
1218 return NULL;
1219 }
1220 const char *rv = hdb->path;
1221 HDBUNLOCKMETHOD(hdb);
1222 return rv;
1223 }
1224
1225
1226 /* Get the number of records of a hash database object. */
tchdbrnum(TCHDB * hdb)1227 uint64_t tchdbrnum(TCHDB *hdb){
1228 assert(hdb);
1229 if(!HDBLOCKMETHOD(hdb, false)) return 0;
1230 if(hdb->fd < 0){
1231 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1232 HDBUNLOCKMETHOD(hdb);
1233 return 0;
1234 }
1235 uint64_t rv = hdb->rnum;
1236 HDBUNLOCKMETHOD(hdb);
1237 return rv;
1238 }
1239
1240
1241 /* Get the size of the database file of a hash database object. */
tchdbfsiz(TCHDB * hdb)1242 uint64_t tchdbfsiz(TCHDB *hdb){
1243 assert(hdb);
1244 if(!HDBLOCKMETHOD(hdb, false)) return 0;
1245 if(hdb->fd < 0){
1246 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1247 HDBUNLOCKMETHOD(hdb);
1248 return 0;
1249 }
1250 uint64_t rv = hdb->fsiz;
1251 HDBUNLOCKMETHOD(hdb);
1252 return rv;
1253 }
1254
1255
1256
1257 /*************************************************************************************************
1258 * features for experts
1259 *************************************************************************************************/
1260
1261
1262 /* Set the error code of a hash database object. */
tchdbsetecode(TCHDB * hdb,int ecode,const char * filename,int line,const char * func)1263 void tchdbsetecode(TCHDB *hdb, int ecode, const char *filename, int line, const char *func){
1264 assert(hdb && filename && line >= 1 && func);
1265 int myerrno = errno;
1266 if(!hdb->fatal){
1267 if(hdb->mmtx){
1268 pthread_setspecific(*(pthread_key_t *)hdb->eckey, (void *)(intptr_t)ecode);
1269 } else {
1270 hdb->ecode = ecode;
1271 }
1272 }
1273 if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){
1274 hdb->fatal = true;
1275 if(hdb->fd >= 0 && (hdb->omode & HDBOWRITER)) tchdbsetflag(hdb, HDBFFATAL, true);
1276 }
1277 if(hdb->dbgfd >= 0 && (hdb->dbgfd != UINT16_MAX || hdb->fatal)){
1278 int dbgfd = (hdb->dbgfd == UINT16_MAX) ? 1 : hdb->dbgfd;
1279 char obuf[HDBIOBUFSIZ];
1280 int osiz = sprintf(obuf, "ERROR:%s:%d:%s:%s:%d:%s:%d:%s\n", filename, line, func,
1281 hdb->path ? hdb->path : "-", ecode, tchdberrmsg(ecode),
1282 myerrno, strerror(myerrno));
1283 tcwrite(dbgfd, obuf, osiz);
1284 }
1285 }
1286
1287
1288 /* Set the type of a hash database object. */
tchdbsettype(TCHDB * hdb,uint8_t type)1289 void tchdbsettype(TCHDB *hdb, uint8_t type){
1290 assert(hdb);
1291 if(hdb->fd >= 0){
1292 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1293 return;
1294 }
1295 hdb->type = type;
1296 }
1297
1298
1299 /* Set the file descriptor for debugging output. */
tchdbsetdbgfd(TCHDB * hdb,int fd)1300 void tchdbsetdbgfd(TCHDB *hdb, int fd){
1301 assert(hdb && fd >= 0);
1302 hdb->dbgfd = fd;
1303 }
1304
1305
1306 /* Get the file descriptor for debugging output. */
tchdbdbgfd(TCHDB * hdb)1307 int tchdbdbgfd(TCHDB *hdb){
1308 assert(hdb);
1309 return hdb->dbgfd;
1310 }
1311
1312
1313 /* Check whether mutual exclusion control is set to a hash database object. */
tchdbhasmutex(TCHDB * hdb)1314 bool tchdbhasmutex(TCHDB *hdb){
1315 assert(hdb);
1316 return hdb->mmtx != NULL;
1317 }
1318
1319
1320 /* Synchronize updating contents on memory of a hash database object. */
tchdbmemsync(TCHDB * hdb,bool phys)1321 bool tchdbmemsync(TCHDB *hdb, bool phys){
1322 assert(hdb);
1323 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
1324 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1325 return false;
1326 }
1327 bool err = false;
1328 char hbuf[HDBHEADSIZ];
1329 tchdbdumpmeta(hdb, hbuf);
1330 memcpy(hdb->map, hbuf, HDBOPAQUEOFF);
1331 if(phys){
1332 size_t xmsiz = (hdb->xmsiz > hdb->msiz) ? hdb->xmsiz : hdb->msiz;
1333 if(msync(hdb->map, xmsiz, MS_SYNC) == -1){
1334 tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
1335 err = true;
1336 }
1337 if(fsync(hdb->fd) == -1){
1338 tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__);
1339 err = true;
1340 }
1341 }
1342 return !err;
1343 }
1344
1345
1346 /* Get the number of elements of the bucket array of a hash database object. */
tchdbbnum(TCHDB * hdb)1347 uint64_t tchdbbnum(TCHDB *hdb){
1348 assert(hdb);
1349 if(hdb->fd < 0){
1350 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1351 return 0;
1352 }
1353 return hdb->bnum;
1354 }
1355
1356
1357 /* Get the record alignment a hash database object. */
tchdbalign(TCHDB * hdb)1358 uint32_t tchdbalign(TCHDB *hdb){
1359 assert(hdb);
1360 if(hdb->fd < 0){
1361 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1362 return 0;
1363 }
1364 return hdb->align;
1365 }
1366
1367
1368 /* Get the maximum number of the free block pool of a a hash database object. */
tchdbfbpmax(TCHDB * hdb)1369 uint32_t tchdbfbpmax(TCHDB *hdb){
1370 assert(hdb);
1371 if(hdb->fd < 0){
1372 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1373 return 0;
1374 }
1375 return hdb->fbpmax;
1376 }
1377
1378
1379 /* Get the size of the extra mapped memory of a hash database object. */
tchdbxmsiz(TCHDB * hdb)1380 uint64_t tchdbxmsiz(TCHDB *hdb){
1381 assert(hdb);
1382 if(hdb->fd < 0){
1383 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1384 return 0;
1385 }
1386 return hdb->xmsiz;
1387 }
1388
1389
1390 /* Get the inode number of the database file of a hash database object. */
tchdbinode(TCHDB * hdb)1391 uint64_t tchdbinode(TCHDB *hdb){
1392 assert(hdb);
1393 if(hdb->fd < 0){
1394 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1395 return 0;
1396 }
1397 return hdb->inode;
1398 }
1399
1400
1401 /* Get the modification time of the database file of a hash database object. */
tchdbmtime(TCHDB * hdb)1402 time_t tchdbmtime(TCHDB *hdb){
1403 assert(hdb);
1404 if(hdb->fd < 0){
1405 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1406 return 0;
1407 }
1408 return hdb->mtime;
1409 }
1410
1411
1412 /* Get the connection mode of a hash database object. */
tchdbomode(TCHDB * hdb)1413 int tchdbomode(TCHDB *hdb){
1414 assert(hdb);
1415 if(hdb->fd < 0){
1416 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1417 return 0;
1418 }
1419 return hdb->omode;
1420 }
1421
1422
1423 /* Get the database type of a hash database object. */
tchdbtype(TCHDB * hdb)1424 uint8_t tchdbtype(TCHDB *hdb){
1425 assert(hdb);
1426 if(hdb->fd < 0){
1427 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1428 return 0;
1429 }
1430 return hdb->type;
1431 }
1432
1433
1434 /* Get the additional flags of a hash database object. */
tchdbflags(TCHDB * hdb)1435 uint8_t tchdbflags(TCHDB *hdb){
1436 assert(hdb);
1437 if(hdb->fd < 0){
1438 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1439 return 0;
1440 }
1441 return hdb->flags;
1442 }
1443
1444
1445 /* Get the options of a hash database object. */
tchdbopts(TCHDB * hdb)1446 uint8_t tchdbopts(TCHDB *hdb){
1447 assert(hdb);
1448 if(hdb->fd < 0){
1449 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1450 return 0;
1451 }
1452 return hdb->opts;
1453 }
1454
1455
1456 /* Get the pointer to the opaque field of a hash database object. */
tchdbopaque(TCHDB * hdb)1457 char *tchdbopaque(TCHDB *hdb){
1458 assert(hdb);
1459 if(hdb->fd < 0){
1460 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1461 return NULL;
1462 }
1463 return hdb->map + HDBOPAQUEOFF;
1464 }
1465
1466
1467 /* Get the number of used elements of the bucket array of a hash database object. */
tchdbbnumused(TCHDB * hdb)1468 uint64_t tchdbbnumused(TCHDB *hdb){
1469 assert(hdb);
1470 if(hdb->fd < 0){
1471 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1472 return 0;
1473 }
1474 uint64_t unum = 0;
1475 if(hdb->ba64){
1476 uint64_t *buckets = hdb->ba64;
1477 for(int i = 0; i < hdb->bnum; i++){
1478 if(buckets[i]) unum++;
1479 }
1480 } else {
1481 uint32_t *buckets = hdb->ba32;
1482 for(int i = 0; i < hdb->bnum; i++){
1483 if(buckets[i]) unum++;
1484 }
1485 }
1486 return unum;
1487 }
1488
1489
1490 /* Set the custom codec functions of a hash database object. */
tchdbsetcodecfunc(TCHDB * hdb,TCCODEC enc,void * encop,TCCODEC dec,void * decop)1491 bool tchdbsetcodecfunc(TCHDB *hdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop){
1492 assert(hdb && enc && dec);
1493 if(!HDBLOCKMETHOD(hdb, true)) return false;
1494 if(hdb->fd >= 0){
1495 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1496 HDBUNLOCKMETHOD(hdb);
1497 return false;
1498 }
1499 hdb->enc = enc;
1500 hdb->encop = encop;
1501 hdb->dec = dec;
1502 hdb->decop = decop;
1503 HDBUNLOCKMETHOD(hdb);
1504 return true;
1505 }
1506
1507
1508 /* Get the unit step number of auto defragmentation of a hash database object. */
tchdbdfunit(TCHDB * hdb)1509 uint32_t tchdbdfunit(TCHDB *hdb){
1510 assert(hdb);
1511 return hdb->dfunit;
1512 }
1513
1514
1515 /* Perform dynamic defragmentation of a hash database object. */
tchdbdefrag(TCHDB * hdb,int64_t step)1516 bool tchdbdefrag(TCHDB *hdb, int64_t step){
1517 assert(hdb);
1518 if(step > 0){
1519 if(!HDBLOCKMETHOD(hdb, true)) return false;
1520 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
1521 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1522 HDBUNLOCKMETHOD(hdb);
1523 return false;
1524 }
1525 if(hdb->async && !tchdbflushdrp(hdb)){
1526 HDBUNLOCKMETHOD(hdb);
1527 return false;
1528 }
1529 bool rv = tchdbdefragimpl(hdb, step);
1530 HDBUNLOCKMETHOD(hdb);
1531 return rv;
1532 }
1533 if(!HDBLOCKMETHOD(hdb, false)) return false;
1534 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
1535 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1536 HDBUNLOCKMETHOD(hdb);
1537 return false;
1538 }
1539 if(hdb->async && !tchdbflushdrp(hdb)){
1540 HDBUNLOCKMETHOD(hdb);
1541 return false;
1542 }
1543 bool err = false;
1544 if(HDBLOCKALLRECORDS(hdb, true)){
1545 hdb->dfcur = hdb->frec;
1546 HDBUNLOCKALLRECORDS(hdb);
1547 } else {
1548 err = true;
1549 }
1550 bool stop = false;
1551 while(!err && !stop){
1552 if(HDBLOCKALLRECORDS(hdb, true)){
1553 uint64_t cur = hdb->dfcur;
1554 if(!tchdbdefragimpl(hdb, UINT8_MAX)) err = true;
1555 if(hdb->dfcur <= cur) stop = true;
1556 HDBUNLOCKALLRECORDS(hdb);
1557 HDBTHREADYIELD(hdb);
1558 } else {
1559 err = true;
1560 }
1561 }
1562 HDBUNLOCKMETHOD(hdb);
1563 return !err;
1564 }
1565
1566
1567 /* Clear the cache of a hash tree database object. */
tchdbcacheclear(TCHDB * hdb)1568 bool tchdbcacheclear(TCHDB *hdb){
1569 assert(hdb);
1570 if(!HDBLOCKMETHOD(hdb, true)) return false;
1571 if(hdb->fd < 0){
1572 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1573 HDBUNLOCKMETHOD(hdb);
1574 return false;
1575 }
1576 HDBTHREADYIELD(hdb);
1577 if(hdb->recc) tcmdbvanish(hdb->recc);
1578 HDBUNLOCKMETHOD(hdb);
1579 return true;
1580 }
1581
1582
1583 /* Store a record into a hash database object with a duplication handler. */
tchdbputproc(TCHDB * hdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz,TCPDPROC proc,void * op)1584 bool tchdbputproc(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
1585 TCPDPROC proc, void *op){
1586 assert(hdb && kbuf && ksiz >= 0 && proc);
1587 if(!HDBLOCKMETHOD(hdb, false)) return false;
1588 uint8_t hash;
1589 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
1590 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
1591 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1592 HDBUNLOCKMETHOD(hdb);
1593 return false;
1594 }
1595 if(hdb->async && !tchdbflushdrp(hdb)){
1596 HDBUNLOCKMETHOD(hdb);
1597 return false;
1598 }
1599 if(!HDBLOCKRECORD(hdb, bidx, true)){
1600 HDBUNLOCKMETHOD(hdb);
1601 return false;
1602 }
1603 if(hdb->zmode){
1604 char *zbuf;
1605 int osiz;
1606 char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz);
1607 if(obuf){
1608 int nsiz;
1609 char *nbuf = proc(obuf, osiz, &nsiz, op);
1610 if(nbuf == (void *)-1){
1611 bool rv = tchdboutimpl(hdb, kbuf, ksiz, bidx, hash);
1612 TCFREE(obuf);
1613 HDBUNLOCKRECORD(hdb, bidx);
1614 HDBUNLOCKMETHOD(hdb);
1615 return rv;
1616 } else if(nbuf){
1617 if(hdb->opts & HDBTDEFLATE){
1618 zbuf = _tc_deflate(nbuf, nsiz, &vsiz, _TCZMRAW);
1619 } else if(hdb->opts & HDBTBZIP){
1620 zbuf = _tc_bzcompress(nbuf, nsiz, &vsiz);
1621 } else if(hdb->opts & HDBTTCBS){
1622 zbuf = tcbsencode(nbuf, nsiz, &vsiz);
1623 } else {
1624 zbuf = hdb->enc(nbuf, nsiz, &vsiz, hdb->encop);
1625 }
1626 TCFREE(nbuf);
1627 } else {
1628 zbuf = NULL;
1629 }
1630 TCFREE(obuf);
1631 } else if(vbuf){
1632 if(hdb->opts & HDBTDEFLATE){
1633 zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
1634 } else if(hdb->opts & HDBTBZIP){
1635 zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz);
1636 } else if(hdb->opts & HDBTTCBS){
1637 zbuf = tcbsencode(vbuf, vsiz, &vsiz);
1638 } else {
1639 zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop);
1640 }
1641 } else {
1642 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
1643 HDBUNLOCKRECORD(hdb, bidx);
1644 HDBUNLOCKMETHOD(hdb);
1645 return false;
1646 }
1647 if(!zbuf){
1648 tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
1649 HDBUNLOCKRECORD(hdb, bidx);
1650 HDBUNLOCKMETHOD(hdb);
1651 return false;
1652 }
1653 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDOVER);
1654 TCFREE(zbuf);
1655 HDBUNLOCKRECORD(hdb, bidx);
1656 HDBUNLOCKMETHOD(hdb);
1657 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
1658 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
1659 return rv;
1660 }
1661 HDBPDPROCOP procop;
1662 procop.proc = proc;
1663 procop.op = op;
1664 HDBPDPROCOP *procptr = &procop;
1665 tcgeneric_t stack[(TCNUMBUFSIZ*2)/sizeof(tcgeneric_t)+1];
1666 char *rbuf;
1667 if(ksiz <= sizeof(stack) - sizeof(procptr)){
1668 rbuf = (char *)stack;
1669 } else {
1670 TCMALLOC(rbuf, ksiz + sizeof(procptr));
1671 }
1672 char *wp = rbuf;
1673 memcpy(wp, &procptr, sizeof(procptr));
1674 wp += sizeof(procptr);
1675 memcpy(wp, kbuf, ksiz);
1676 kbuf = rbuf + sizeof(procptr);
1677 bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDPROC);
1678 if(rbuf != (char *)stack) TCFREE(rbuf);
1679 HDBUNLOCKRECORD(hdb, bidx);
1680 HDBUNLOCKMETHOD(hdb);
1681 if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit &&
1682 !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false;
1683 return rv;
1684 }
1685
1686
1687 /* Get the custom codec functions of a hash database object. */
tchdbcodecfunc(TCHDB * hdb,TCCODEC * ep,void ** eop,TCCODEC * dp,void ** dop)1688 void tchdbcodecfunc(TCHDB *hdb, TCCODEC *ep, void **eop, TCCODEC *dp, void **dop){
1689 assert(hdb && ep && eop && dp && dop);
1690 *ep = hdb->enc;
1691 *eop = hdb->encop;
1692 *dp = hdb->dec;
1693 *dop = hdb->decop;
1694 }
1695
1696
1697 /* Retrieve the next record of a record in a hash database object. */
tchdbgetnext(TCHDB * hdb,const void * kbuf,int ksiz,int * sp)1698 void *tchdbgetnext(TCHDB *hdb, const void *kbuf, int ksiz, int *sp){
1699 assert(hdb && sp);
1700 if(!HDBLOCKMETHOD(hdb, true)) return NULL;
1701 if(hdb->fd < 0){
1702 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1703 HDBUNLOCKMETHOD(hdb);
1704 return NULL;
1705 }
1706 if(hdb->async && !tchdbflushdrp(hdb)){
1707 HDBUNLOCKMETHOD(hdb);
1708 return NULL;
1709 }
1710 char *rv = tchdbgetnextimpl(hdb, kbuf, ksiz, sp, NULL, NULL);
1711 HDBUNLOCKMETHOD(hdb);
1712 return rv;
1713 }
1714
1715
1716 /* Retrieve the next record of a string record in a hash database object. */
tchdbgetnext2(TCHDB * hdb,const char * kstr)1717 char *tchdbgetnext2(TCHDB *hdb, const char *kstr){
1718 assert(hdb);
1719 int vsiz;
1720 return tchdbgetnext(hdb, kstr, strlen(kstr), &vsiz);
1721 }
1722
1723
1724 /* Retrieve the key and the value of the next record of a record in a hash database object. */
tchdbgetnext3(TCHDB * hdb,const char * kbuf,int ksiz,int * sp,const char ** vbp,int * vsp)1725 char *tchdbgetnext3(TCHDB *hdb, const char *kbuf, int ksiz, int *sp, const char **vbp, int *vsp){
1726 assert(hdb && sp && vbp && vsp);
1727 if(!HDBLOCKMETHOD(hdb, true)) return NULL;
1728 if(hdb->fd < 0){
1729 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1730 HDBUNLOCKMETHOD(hdb);
1731 return NULL;
1732 }
1733 if(hdb->async && !tchdbflushdrp(hdb)){
1734 HDBUNLOCKMETHOD(hdb);
1735 return NULL;
1736 }
1737 char *rv = tchdbgetnextimpl(hdb, kbuf, ksiz, sp, vbp, vsp);
1738 HDBUNLOCKMETHOD(hdb);
1739 return rv;
1740 }
1741
1742
1743 /* Move the iterator to the record corresponding a key of a hash database object. */
tchdbiterinit2(TCHDB * hdb,const void * kbuf,int ksiz)1744 bool tchdbiterinit2(TCHDB *hdb, const void *kbuf, int ksiz){
1745 assert(hdb && kbuf && ksiz >= 0);
1746 if(!HDBLOCKMETHOD(hdb, true)) return false;
1747 if(hdb->fd < 0){
1748 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1749 HDBUNLOCKMETHOD(hdb);
1750 return false;
1751 }
1752 if(hdb->async && !tchdbflushdrp(hdb)){
1753 HDBUNLOCKMETHOD(hdb);
1754 return false;
1755 }
1756 bool rv = tchdbiterjumpimpl(hdb, kbuf, ksiz);
1757 HDBUNLOCKMETHOD(hdb);
1758 return rv;
1759 }
1760
1761
1762 /* Move the iterator to the record corresponding a key string of a hash database object. */
tchdbiterinit3(TCHDB * hdb,const char * kstr)1763 bool tchdbiterinit3(TCHDB *hdb, const char *kstr){
1764 assert(hdb && kstr);
1765 return tchdbiterinit2(hdb, kstr, strlen(kstr));
1766 }
1767
1768
1769 /* Process each record atomically of a hash database object. */
tchdbforeach(TCHDB * hdb,TCITER iter,void * op)1770 bool tchdbforeach(TCHDB *hdb, TCITER iter, void *op){
1771 assert(hdb && iter);
1772 if(!HDBLOCKMETHOD(hdb, false)) return false;
1773 if(hdb->fd < 0){
1774 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1775 HDBUNLOCKMETHOD(hdb);
1776 return false;
1777 }
1778 if(hdb->async && !tchdbflushdrp(hdb)){
1779 HDBUNLOCKMETHOD(hdb);
1780 return false;
1781 }
1782 if(!HDBLOCKALLRECORDS(hdb, false)){
1783 HDBUNLOCKMETHOD(hdb);
1784 return false;
1785 }
1786 HDBTHREADYIELD(hdb);
1787 bool rv = tchdbforeachimpl(hdb, iter, op);
1788 HDBUNLOCKALLRECORDS(hdb);
1789 HDBUNLOCKMETHOD(hdb);
1790 return rv;
1791 }
1792
1793
1794 /* Void the transaction of a hash database object. */
tchdbtranvoid(TCHDB * hdb)1795 bool tchdbtranvoid(TCHDB *hdb){
1796 assert(hdb);
1797 if(!HDBLOCKMETHOD(hdb, true)) return false;
1798 if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->fatal || !hdb->tran){
1799 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
1800 HDBUNLOCKMETHOD(hdb);
1801 return false;
1802 }
1803 hdb->tran = false;
1804 HDBUNLOCKMETHOD(hdb);
1805 return true;
1806 }
1807
1808
1809
1810 /*************************************************************************************************
1811 * private features
1812 *************************************************************************************************/
1813
1814
1815 /* Get a natural prime number not less than a floor number.
1816 `num' specified the floor number.
1817 The return value is a prime number not less than the floor number. */
tcgetprime(uint64_t num)1818 static uint64_t tcgetprime(uint64_t num){
1819 uint64_t primes[] = {
1820 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 43, 47, 53, 59, 61, 71, 79, 83,
1821 89, 103, 109, 113, 127, 139, 157, 173, 191, 199, 223, 239, 251, 283, 317, 349,
1822 383, 409, 443, 479, 509, 571, 631, 701, 761, 829, 887, 953, 1021, 1151, 1279,
1823 1399, 1531, 1663, 1789, 1913, 2039, 2297, 2557, 2803, 3067, 3323, 3583, 3833,
1824 4093, 4603, 5119, 5623, 6143, 6653, 7159, 7673, 8191, 9209, 10223, 11261,
1825 12281, 13309, 14327, 15359, 16381, 18427, 20479, 22511, 24571, 26597, 28669,
1826 30713, 32749, 36857, 40949, 45053, 49139, 53239, 57331, 61417, 65521, 73727,
1827 81919, 90107, 98299, 106487, 114679, 122869, 131071, 147451, 163819, 180221,
1828 196597, 212987, 229373, 245759, 262139, 294911, 327673, 360439, 393209, 425977,
1829 458747, 491503, 524287, 589811, 655357, 720887, 786431, 851957, 917503, 982981,
1830 1048573, 1179641, 1310719, 1441771, 1572853, 1703903, 1835003, 1966079,
1831 2097143, 2359267, 2621431, 2883577, 3145721, 3407857, 3670013, 3932153,
1832 4194301, 4718579, 5242877, 5767129, 6291449, 6815741, 7340009, 7864301,
1833 8388593, 9437179, 10485751, 11534329, 12582893, 13631477, 14680063, 15728611,
1834 16777213, 18874367, 20971507, 23068667, 25165813, 27262931, 29360087, 31457269,
1835 33554393, 37748717, 41943023, 46137319, 50331599, 54525917, 58720253, 62914549,
1836 67108859, 75497467, 83886053, 92274671, 100663291, 109051903, 117440509,
1837 125829103, 134217689, 150994939, 167772107, 184549373, 201326557, 218103799,
1838 234881011, 251658227, 268435399, 301989881, 335544301, 369098707, 402653171,
1839 436207613, 469762043, 503316469, 536870909, 603979769, 671088637, 738197503,
1840 805306357, 872415211, 939524087, 1006632947, 1073741789, 1207959503,
1841 1342177237, 1476394991, 1610612711, 1744830457, 1879048183, 2013265907,
1842 2576980349, 3092376431, 3710851741, 4718021527, 6133428047, 7973456459,
1843 10365493393, 13475141413, 17517683831, 22772988923, 29604885677, 38486351381,
1844 50032256819, 65041933867, 84554514043, 109920868241, 153889215497, 0
1845 };
1846 int i;
1847 for(i = 0; primes[i] > 0; i++){
1848 if(num <= primes[i]) return primes[i];
1849 }
1850 return primes[i-1];
1851 }
1852
1853
1854 /* Seek and write data into a file.
1855 `hdb' specifies the hash database object.
1856 `off' specifies the offset of the region to seek.
1857 `buf' specifies the buffer to store into.
1858 `size' specifies the size of the buffer.
1859 The return value is true if successful, else, it is false. */
tchdbseekwrite(TCHDB * hdb,off_t off,const void * buf,size_t size)1860 static bool tchdbseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size){
1861 assert(hdb && off >= 0 && buf && size >= 0);
1862 if(hdb->tran && !tchdbwalwrite(hdb, off, size)) return false;
1863 off_t end = off + size;
1864 if(end <= hdb->xmsiz){
1865 if(end >= hdb->fsiz && end >= hdb->xfsiz){
1866 uint64_t xfsiz = end + HDBXFSIZINC;
1867 if(ftruncate(hdb->fd, xfsiz) == -1){
1868 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
1869 return false;
1870 }
1871 hdb->xfsiz = xfsiz;
1872 }
1873 memcpy(hdb->map + off, buf, size);
1874 return true;
1875 }
1876 if(!TCUBCACHE && off < hdb->xmsiz){
1877 if(end >= hdb->fsiz && end >= hdb->xfsiz){
1878 uint64_t xfsiz = end + HDBXFSIZINC;
1879 if(ftruncate(hdb->fd, xfsiz) == -1){
1880 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
1881 return false;
1882 }
1883 hdb->xfsiz = xfsiz;
1884 }
1885 int head = hdb->xmsiz - off;
1886 memcpy(hdb->map + off, buf, head);
1887 off += head;
1888 buf = (char *)buf + head;
1889 size -= head;
1890 }
1891 while(true){
1892 int wb = pwrite(hdb->fd, buf, size, off);
1893 if(wb >= size){
1894 return true;
1895 } else if(wb > 0){
1896 buf = (char *)buf + wb;
1897 size -= wb;
1898 off += wb;
1899 } else if(wb == -1){
1900 if(errno != EINTR){
1901 tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
1902 return false;
1903 }
1904 } else {
1905 if(size > 0){
1906 tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
1907 return false;
1908 }
1909 }
1910 }
1911 return true;
1912 }
1913
1914
1915 /* Seek and read data from a file.
1916 `hdb' specifies the hash database object.
1917 `off' specifies the offset of the region to seek.
1918 `buf' specifies the buffer to store into.
1919 `size' specifies the size of the buffer.
1920 The return value is true if successful, else, it is false. */
tchdbseekread(TCHDB * hdb,off_t off,void * buf,size_t size)1921 static bool tchdbseekread(TCHDB *hdb, off_t off, void *buf, size_t size){
1922 assert(hdb && off >= 0 && buf && size >= 0);
1923 if(off + size <= hdb->xmsiz){
1924 memcpy(buf, hdb->map + off, size);
1925 return true;
1926 }
1927 if(!TCUBCACHE && off < hdb->xmsiz){
1928 int head = hdb->xmsiz - off;
1929 memcpy(buf, hdb->map + off, head);
1930 off += head;
1931 buf = (char *)buf + head;
1932 size -= head;
1933 }
1934 while(true){
1935 int rb = pread(hdb->fd, buf, size, off);
1936 if(rb >= size){
1937 break;
1938 } else if(rb > 0){
1939 buf = (char *)buf + rb;
1940 size -= rb;
1941 off += rb;
1942 } else if(rb == -1){
1943 if(errno != EINTR){
1944 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
1945 return false;
1946 }
1947 } else {
1948 if(size > 0){
1949 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
1950 return false;
1951 }
1952 }
1953 }
1954 return true;
1955 }
1956
1957
1958 /* Try to seek and read data from a file.
1959 `hdb' specifies the hash database object.
1960 `off' specifies the offset of the region to seek.
1961 `buf' specifies the buffer to store into.
1962 `size' specifies the size of the buffer.
1963 The return value is true if successful, else, it is false. */
tchdbseekreadtry(TCHDB * hdb,off_t off,void * buf,size_t size)1964 static bool tchdbseekreadtry(TCHDB *hdb, off_t off, void *buf, size_t size){
1965 assert(hdb && off >= 0 && buf && size >= 0);
1966 off_t end = off + size;
1967 if(end > hdb->fsiz) return false;
1968 if(end <= hdb->xmsiz){
1969 memcpy(buf, hdb->map + off, size);
1970 return true;
1971 }
1972 if(!TCUBCACHE && off < hdb->xmsiz){
1973 int head = hdb->xmsiz - off;
1974 memcpy(buf, hdb->map + off, head);
1975 off += head;
1976 buf = (char *)buf + head;
1977 size -= head;
1978 }
1979 int rb = pread(hdb->fd, buf, size, off);
1980 if(rb == size) return true;
1981 if(rb == -1) tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
1982 return false;
1983 }
1984
1985
1986 /* Serialize meta data into a buffer.
1987 `hdb' specifies the hash database object.
1988 `hbuf' specifies the buffer. */
tchdbdumpmeta(TCHDB * hdb,char * hbuf)1989 static void tchdbdumpmeta(TCHDB *hdb, char *hbuf){
1990 memset(hbuf, 0, HDBHEADSIZ);
1991 sprintf(hbuf, "%s\n%s:%d\n", HDBMAGICDATA, _TC_FORMATVER, _TC_LIBVER);
1992 memcpy(hbuf + HDBTYPEOFF, &(hdb->type), sizeof(hdb->type));
1993 memcpy(hbuf + HDBFLAGSOFF, &(hdb->flags), sizeof(hdb->flags));
1994 memcpy(hbuf + HDBAPOWOFF, &(hdb->apow), sizeof(hdb->apow));
1995 memcpy(hbuf + HDBFPOWOFF, &(hdb->fpow), sizeof(hdb->fpow));
1996 memcpy(hbuf + HDBOPTSOFF, &(hdb->opts), sizeof(hdb->opts));
1997 uint64_t llnum;
1998 llnum = hdb->bnum;
1999 llnum = TCHTOILL(llnum);
2000 memcpy(hbuf + HDBBNUMOFF, &llnum, sizeof(llnum));
2001 llnum = hdb->rnum;
2002 llnum = TCHTOILL(llnum);
2003 memcpy(hbuf + HDBRNUMOFF, &llnum, sizeof(llnum));
2004 llnum = hdb->fsiz;
2005 llnum = TCHTOILL(llnum);
2006 memcpy(hbuf + HDBFSIZOFF, &llnum, sizeof(llnum));
2007 llnum = hdb->frec;
2008 llnum = TCHTOILL(llnum);
2009 memcpy(hbuf + HDBFRECOFF, &llnum, sizeof(llnum));
2010 }
2011
2012
2013 /* Deserialize meta data from a buffer.
2014 `hdb' specifies the hash database object.
2015 `hbuf' specifies the buffer. */
tchdbloadmeta(TCHDB * hdb,const char * hbuf)2016 static void tchdbloadmeta(TCHDB *hdb, const char *hbuf){
2017 memcpy(&(hdb->type), hbuf + HDBTYPEOFF, sizeof(hdb->type));
2018 memcpy(&(hdb->flags), hbuf + HDBFLAGSOFF, sizeof(hdb->flags));
2019 memcpy(&(hdb->apow), hbuf + HDBAPOWOFF, sizeof(hdb->apow));
2020 memcpy(&(hdb->fpow), hbuf + HDBFPOWOFF, sizeof(hdb->fpow));
2021 memcpy(&(hdb->opts), hbuf + HDBOPTSOFF, sizeof(hdb->opts));
2022 uint64_t llnum;
2023 memcpy(&llnum, hbuf + HDBBNUMOFF, sizeof(llnum));
2024 hdb->bnum = TCITOHLL(llnum);
2025 memcpy(&llnum, hbuf + HDBRNUMOFF, sizeof(llnum));
2026 hdb->rnum = TCITOHLL(llnum);
2027 memcpy(&llnum, hbuf + HDBFSIZOFF, sizeof(llnum));
2028 hdb->fsiz = TCITOHLL(llnum);
2029 memcpy(&llnum, hbuf + HDBFRECOFF, sizeof(llnum));
2030 hdb->frec = TCITOHLL(llnum);
2031 }
2032
2033
2034 /* Clear all members.
2035 `hdb' specifies the hash database object. */
tchdbclear(TCHDB * hdb)2036 static void tchdbclear(TCHDB *hdb){
2037 assert(hdb);
2038 hdb->mmtx = NULL;
2039 hdb->rmtxs = NULL;
2040 hdb->dmtx = NULL;
2041 hdb->wmtx = NULL;
2042 hdb->eckey = NULL;
2043 hdb->rpath = NULL;
2044 hdb->type = TCDBTHASH;
2045 hdb->flags = 0;
2046 hdb->bnum = HDBDEFBNUM;
2047 hdb->apow = HDBDEFAPOW;
2048 hdb->fpow = HDBDEFFPOW;
2049 hdb->opts = 0;
2050 hdb->path = NULL;
2051 hdb->fd = -1;
2052 hdb->omode = 0;
2053 hdb->rnum = 0;
2054 hdb->fsiz = 0;
2055 hdb->frec = 0;
2056 hdb->dfcur = 0;
2057 hdb->iter = 0;
2058 hdb->map = NULL;
2059 hdb->msiz = 0;
2060 hdb->xmsiz = HDBDEFXMSIZ;
2061 hdb->xfsiz = 0;
2062 hdb->ba32 = NULL;
2063 hdb->ba64 = NULL;
2064 hdb->align = 0;
2065 hdb->runit = 0;
2066 hdb->zmode = false;
2067 hdb->fbpmax = 0;
2068 hdb->fbpool = NULL;
2069 hdb->fbpnum = 0;
2070 hdb->fbpmis = 0;
2071 hdb->async = false;
2072 hdb->drpool = NULL;
2073 hdb->drpdef = NULL;
2074 hdb->drpoff = 0;
2075 hdb->recc = NULL;
2076 hdb->rcnum = 0;
2077 hdb->enc = NULL;
2078 hdb->encop = NULL;
2079 hdb->dec = NULL;
2080 hdb->decop = NULL;
2081 hdb->ecode = TCESUCCESS;
2082 hdb->fatal = false;
2083 hdb->inode = 0;
2084 hdb->mtime = 0;
2085 hdb->dfunit = 0;
2086 hdb->dfcnt = 0;
2087 hdb->tran = false;
2088 hdb->walfd = -1;
2089 hdb->walend = 0;
2090 hdb->dbgfd = -1;
2091 hdb->cnt_writerec = -1;
2092 hdb->cnt_reuserec = -1;
2093 hdb->cnt_moverec = -1;
2094 hdb->cnt_readrec = -1;
2095 hdb->cnt_searchfbp = -1;
2096 hdb->cnt_insertfbp = -1;
2097 hdb->cnt_splicefbp = -1;
2098 hdb->cnt_dividefbp = -1;
2099 hdb->cnt_mergefbp = -1;
2100 hdb->cnt_reducefbp = -1;
2101 hdb->cnt_appenddrp = -1;
2102 hdb->cnt_deferdrp = -1;
2103 hdb->cnt_flushdrp = -1;
2104 hdb->cnt_adjrecc = -1;
2105 hdb->cnt_defrag = -1;
2106 hdb->cnt_shiftrec = -1;
2107 hdb->cnt_trunc = -1;
2108 TCDODEBUG(hdb->cnt_writerec = 0);
2109 TCDODEBUG(hdb->cnt_reuserec = 0);
2110 TCDODEBUG(hdb->cnt_moverec = 0);
2111 TCDODEBUG(hdb->cnt_readrec = 0);
2112 TCDODEBUG(hdb->cnt_searchfbp = 0);
2113 TCDODEBUG(hdb->cnt_insertfbp = 0);
2114 TCDODEBUG(hdb->cnt_splicefbp = 0);
2115 TCDODEBUG(hdb->cnt_dividefbp = 0);
2116 TCDODEBUG(hdb->cnt_mergefbp = 0);
2117 TCDODEBUG(hdb->cnt_reducefbp = 0);
2118 TCDODEBUG(hdb->cnt_appenddrp = 0);
2119 TCDODEBUG(hdb->cnt_deferdrp = 0);
2120 TCDODEBUG(hdb->cnt_flushdrp = 0);
2121 TCDODEBUG(hdb->cnt_adjrecc = 0);
2122 TCDODEBUG(hdb->cnt_defrag = 0);
2123 TCDODEBUG(hdb->cnt_shiftrec = 0);
2124 TCDODEBUG(hdb->cnt_trunc = 0);
2125 }
2126
2127
2128 /* Get the padding size to record alignment.
2129 `hdb' specifies the hash database object.
2130 `off' specifies the current offset.
2131 The return value is the padding size. */
tchdbpadsize(TCHDB * hdb,uint64_t off)2132 static int32_t tchdbpadsize(TCHDB *hdb, uint64_t off){
2133 assert(hdb);
2134 int32_t diff = off & (hdb->align - 1);
2135 return (diff > 0) ? hdb->align - diff : 0;
2136 }
2137
2138
2139 /* Set the open flag.
2140 `hdb' specifies the hash database object.
2141 `flag' specifies the flag value.
2142 `sign' specifies the sign. */
tchdbsetflag(TCHDB * hdb,int flag,bool sign)2143 static void tchdbsetflag(TCHDB *hdb, int flag, bool sign){
2144 assert(hdb);
2145 char *fp = (char *)hdb->map + HDBFLAGSOFF;
2146 if(sign){
2147 *fp |= (uint8_t)flag;
2148 } else {
2149 *fp &= ~(uint8_t)flag;
2150 }
2151 hdb->flags = *fp;
2152 }
2153
2154
2155 /* Get the bucket index of a record.
2156 `hdb' specifies the hash database object.
2157 `kbuf' specifies the pointer to the region of the key.
2158 `ksiz' specifies the size of the region of the key.
2159 `hp' specifies the pointer to the variable into which the second hash value is assigned.
2160 The return value is the bucket index. */
tchdbbidx(TCHDB * hdb,const char * kbuf,int ksiz,uint8_t * hp)2161 static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp){
2162 assert(hdb && kbuf && ksiz >= 0 && hp);
2163 uint64_t idx = 19780211;
2164 uint32_t hash = 751;
2165 const char *rp = kbuf + ksiz;
2166 while(ksiz--){
2167 idx = idx * 37 + *(uint8_t *)kbuf++;
2168 hash = (hash * 31) ^ *(uint8_t *)--rp;
2169 }
2170 *hp = hash;
2171 return idx % hdb->bnum;
2172 }
2173
2174
2175 /* Get the offset of the record of a bucket element.
2176 `hdb' specifies the hash database object.
2177 `bidx' specifies the index of the bucket.
2178 The return value is the offset of the record. */
tchdbgetbucket(TCHDB * hdb,uint64_t bidx)2179 static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx){
2180 assert(hdb && bidx >= 0);
2181 if(hdb->ba64){
2182 uint64_t llnum = hdb->ba64[bidx];
2183 return TCITOHLL(llnum) << hdb->apow;
2184 }
2185 uint32_t lnum = hdb->ba32[bidx];
2186 return (off_t)TCITOHL(lnum) << hdb->apow;
2187 }
2188
2189
2190 /* Get the offset of the record of a bucket element.
2191 `hdb' specifies the hash database object.
2192 `bidx' specifies the index of the record.
2193 `off' specifies the offset of the record. */
tchdbsetbucket(TCHDB * hdb,uint64_t bidx,uint64_t off)2194 static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off){
2195 assert(hdb && bidx >= 0);
2196 if(hdb->ba64){
2197 uint64_t llnum = off >> hdb->apow;
2198 if(hdb->tran) tchdbwalwrite(hdb, HDBHEADSIZ + bidx * sizeof(llnum), sizeof(llnum));
2199 hdb->ba64[bidx] = TCHTOILL(llnum);
2200 } else {
2201 uint32_t lnum = off >> hdb->apow;
2202 if(hdb->tran) tchdbwalwrite(hdb, HDBHEADSIZ + bidx * sizeof(lnum), sizeof(lnum));
2203 hdb->ba32[bidx] = TCHTOIL(lnum);
2204 }
2205 }
2206
2207
2208 /* Load the free block pool from the file.
2209 The return value is true if successful, else, it is false. */
tchdbsavefbp(TCHDB * hdb)2210 static bool tchdbsavefbp(TCHDB *hdb){
2211 assert(hdb);
2212 if(hdb->fbpnum > hdb->fbpmax){
2213 tchdbfbpmerge(hdb);
2214 } else if(hdb->fbpnum > 1){
2215 tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum);
2216 }
2217 int bsiz = hdb->frec - hdb->msiz;
2218 char *buf;
2219 TCMALLOC(buf, bsiz);
2220 char *wp = buf;
2221 HDBFB *cur = hdb->fbpool;
2222 HDBFB *end = cur + hdb->fbpnum;
2223 uint64_t base = 0;
2224 bsiz -= sizeof(HDBFB) + sizeof(uint8_t) + sizeof(uint8_t);
2225 while(cur < end && bsiz > 0){
2226 uint64_t noff = cur->off >> hdb->apow;
2227 int step;
2228 uint64_t llnum = noff - base;
2229 TCSETVNUMBUF64(step, wp, llnum);
2230 wp += step;
2231 bsiz -= step;
2232 uint32_t lnum = cur->rsiz >> hdb->apow;
2233 TCSETVNUMBUF(step, wp, lnum);
2234 wp += step;
2235 bsiz -= step;
2236 base = noff;
2237 cur++;
2238 }
2239 *(wp++) = '\0';
2240 *(wp++) = '\0';
2241 if(!tchdbseekwrite(hdb, hdb->msiz, buf, wp - buf)){
2242 TCFREE(buf);
2243 return false;
2244 }
2245 TCFREE(buf);
2246 return true;
2247 }
2248
2249
2250 /* Save the free block pool into the file.
2251 The return value is true if successful, else, it is false. */
tchdbloadfbp(TCHDB * hdb)2252 static bool tchdbloadfbp(TCHDB *hdb){
2253 int bsiz = hdb->frec - hdb->msiz;
2254 char *buf;
2255 TCMALLOC(buf, bsiz);
2256 if(!tchdbseekread(hdb, hdb->msiz, buf, bsiz)){
2257 TCFREE(buf);
2258 return false;
2259 }
2260 const char *rp = buf;
2261 HDBFB *cur = hdb->fbpool;
2262 HDBFB *end = cur + hdb->fbpmax * HDBFBPALWRAT;
2263 uint64_t base = 0;
2264 while(cur < end && *rp != '\0'){
2265 int step;
2266 uint64_t llnum;
2267 TCREADVNUMBUF64(rp, llnum, step);
2268 base += llnum << hdb->apow;
2269 cur->off = base;
2270 rp += step;
2271 uint32_t lnum;
2272 TCREADVNUMBUF(rp, lnum, step);
2273 cur->rsiz = lnum << hdb->apow;
2274 rp += step;
2275 cur++;
2276 }
2277 hdb->fbpnum = cur - (HDBFB *)hdb->fbpool;
2278 TCFREE(buf);
2279 tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum);
2280 return true;
2281 }
2282
2283
2284 /* Sort the free block pool by offset.
2285 `fbpool' specifies the free block pool.
2286 `fbpnum' specifies the number of blocks. */
tcfbpsortbyoff(HDBFB * fbpool,int fbpnum)2287 static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum){
2288 assert(fbpool && fbpnum >= 0);
2289 fbpnum--;
2290 int bottom = fbpnum / 2 + 1;
2291 int top = fbpnum;
2292 while(bottom > 0){
2293 bottom--;
2294 int mybot = bottom;
2295 int i = mybot * 2;
2296 while(i <= top){
2297 if(i < top && fbpool[i+1].off > fbpool[i].off) i++;
2298 if(fbpool[mybot].off >= fbpool[i].off) break;
2299 HDBFB swap = fbpool[mybot];
2300 fbpool[mybot] = fbpool[i];
2301 fbpool[i] = swap;
2302 mybot = i;
2303 i = mybot * 2;
2304 }
2305 }
2306 while(top > 0){
2307 HDBFB swap = fbpool[0];
2308 fbpool[0] = fbpool[top];
2309 fbpool[top] = swap;
2310 top--;
2311 int mybot = bottom;
2312 int i = mybot * 2;
2313 while(i <= top){
2314 if(i < top && fbpool[i+1].off > fbpool[i].off) i++;
2315 if(fbpool[mybot].off >= fbpool[i].off) break;
2316 swap = fbpool[mybot];
2317 fbpool[mybot] = fbpool[i];
2318 fbpool[i] = swap;
2319 mybot = i;
2320 i = mybot * 2;
2321 }
2322 }
2323 }
2324
2325
2326 /* Sort the free block pool by record size.
2327 `fbpool' specifies the free block pool.
2328 `fbpnum' specifies the number of blocks. */
tcfbpsortbyrsiz(HDBFB * fbpool,int fbpnum)2329 static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum){
2330 assert(fbpool && fbpnum >= 0);
2331 fbpnum--;
2332 int bottom = fbpnum / 2 + 1;
2333 int top = fbpnum;
2334 while(bottom > 0){
2335 bottom--;
2336 int mybot = bottom;
2337 int i = mybot * 2;
2338 while(i <= top){
2339 if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++;
2340 if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break;
2341 HDBFB swap = fbpool[mybot];
2342 fbpool[mybot] = fbpool[i];
2343 fbpool[i] = swap;
2344 mybot = i;
2345 i = mybot * 2;
2346 }
2347 }
2348 while(top > 0){
2349 HDBFB swap = fbpool[0];
2350 fbpool[0] = fbpool[top];
2351 fbpool[top] = swap;
2352 top--;
2353 int mybot = bottom;
2354 int i = mybot * 2;
2355 while(i <= top){
2356 if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++;
2357 if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break;
2358 swap = fbpool[mybot];
2359 fbpool[mybot] = fbpool[i];
2360 fbpool[i] = swap;
2361 mybot = i;
2362 i = mybot * 2;
2363 }
2364 }
2365 }
2366
2367
2368 /* Merge successive records in the free block pool.
2369 `hdb' specifies the hash database object. */
tchdbfbpmerge(TCHDB * hdb)2370 static void tchdbfbpmerge(TCHDB *hdb){
2371 assert(hdb);
2372 TCDODEBUG(hdb->cnt_mergefbp++);
2373 tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum);
2374 HDBFB *wp = hdb->fbpool;
2375 HDBFB *cur = wp;
2376 HDBFB *end = wp + hdb->fbpnum - 1;
2377 while(cur < end){
2378 if(cur->off > 0){
2379 HDBFB *next = cur + 1;
2380 if(cur->off + cur->rsiz == next->off && cur->rsiz + next->rsiz <= HDBFBMAXSIZ){
2381 if(hdb->dfcur == next->off) hdb->dfcur += next->rsiz;
2382 if(hdb->iter == next->off) hdb->iter += next->rsiz;
2383 cur->rsiz += next->rsiz;
2384 next->off = 0;
2385 }
2386 *(wp++) = *cur;
2387 }
2388 cur++;
2389 }
2390 if(end->off > 0) *(wp++) = *end;
2391 hdb->fbpnum = wp - (HDBFB *)hdb->fbpool;
2392 hdb->fbpmis = hdb->fbpnum * -1;
2393 }
2394
2395
2396 /* Insert a block into the free block pool.
2397 `hdb' specifies the hash database object.
2398 `off' specifies the offset of the block.
2399 `rsiz' specifies the size of the block. */
tchdbfbpinsert(TCHDB * hdb,uint64_t off,uint32_t rsiz)2400 static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz){
2401 assert(hdb && off > 0 && rsiz > 0);
2402 TCDODEBUG(hdb->cnt_insertfbp++);
2403 hdb->dfcnt++;
2404 if(hdb->fpow < 1) return;
2405 HDBFB *pv = hdb->fbpool;
2406 if(hdb->fbpnum >= hdb->fbpmax * HDBFBPALWRAT){
2407 tchdbfbpmerge(hdb);
2408 tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum);
2409 int diff = hdb->fbpnum - hdb->fbpmax;
2410 if(diff > 0){
2411 TCDODEBUG(hdb->cnt_reducefbp++);
2412 memmove(pv, pv + diff, (hdb->fbpnum - diff) * sizeof(*pv));
2413 hdb->fbpnum -= diff;
2414 }
2415 hdb->fbpmis = 0;
2416 }
2417 int num = hdb->fbpnum;
2418 int left = 0;
2419 int right = num;
2420 int i = (left + right) / 2;
2421 int cand = -1;
2422 while(right >= left && i < num){
2423 int rv = (int)rsiz - (int)pv[i].rsiz;
2424 if(rv == 0){
2425 cand = i;
2426 break;
2427 } else if(rv <= 0){
2428 cand = i;
2429 right = i - 1;
2430 } else {
2431 left = i + 1;
2432 }
2433 i = (left + right) / 2;
2434 }
2435 if(cand >= 0){
2436 pv += cand;
2437 memmove(pv + 1, pv, sizeof(*pv) * (num - cand));
2438 } else {
2439 pv += num;
2440 }
2441 pv->off = off;
2442 pv->rsiz = rsiz;
2443 hdb->fbpnum++;
2444 }
2445
2446
2447 /* Search the free block pool for the minimum region.
2448 `hdb' specifies the hash database object.
2449 `rec' specifies the record object to be stored.
2450 The return value is true if successful, else, it is false. */
tchdbfbpsearch(TCHDB * hdb,TCHREC * rec)2451 static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec){
2452 assert(hdb && rec);
2453 TCDODEBUG(hdb->cnt_searchfbp++);
2454 if(hdb->fbpnum < 1){
2455 rec->off = hdb->fsiz;
2456 rec->rsiz = 0;
2457 return true;
2458 }
2459 uint32_t rsiz = rec->rsiz;
2460 HDBFB *pv = hdb->fbpool;
2461 int num = hdb->fbpnum;
2462 int left = 0;
2463 int right = num;
2464 int i = (left + right) / 2;
2465 int cand = -1;
2466 while(right >= left && i < num){
2467 int rv = (int)rsiz - (int)pv[i].rsiz;
2468 if(rv == 0){
2469 cand = i;
2470 break;
2471 } else if(rv <= 0){
2472 cand = i;
2473 right = i - 1;
2474 } else {
2475 left = i + 1;
2476 }
2477 i = (left + right) / 2;
2478 }
2479 if(cand >= 0){
2480 pv += cand;
2481 if(pv->rsiz > rsiz * 2){
2482 uint32_t psiz = tchdbpadsize(hdb, pv->off + rsiz);
2483 uint64_t noff = pv->off + rsiz + psiz;
2484 if(pv->rsiz >= (noff - pv->off) * 2){
2485 TCDODEBUG(hdb->cnt_dividefbp++);
2486 rec->off = pv->off;
2487 rec->rsiz = noff - pv->off;
2488 pv->off = noff;
2489 pv->rsiz -= rec->rsiz;
2490 return tchdbwritefb(hdb, pv->off, pv->rsiz);
2491 }
2492 }
2493 rec->off = pv->off;
2494 rec->rsiz = pv->rsiz;
2495 memmove(pv, pv + 1, sizeof(*pv) * (num - cand - 1));
2496 hdb->fbpnum--;
2497 return true;
2498 }
2499 rec->off = hdb->fsiz;
2500 rec->rsiz = 0;
2501 hdb->fbpmis++;
2502 if(hdb->fbpmis >= HDBFBPMGFREQ){
2503 tchdbfbpmerge(hdb);
2504 tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum);
2505 }
2506 return true;
2507 }
2508
2509
2510 /* Splice the trailing free block
2511 `hdb' specifies the hash database object.
2512 `rec' specifies the record object to be stored.
2513 `nsiz' specifies the needed size.
2514 The return value is whether splicing succeeded or not. */
tchdbfbpsplice(TCHDB * hdb,TCHREC * rec,uint32_t nsiz)2515 static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz){
2516 assert(hdb && rec && nsiz > 0);
2517 if(hdb->mmtx){
2518 if(hdb->fbpnum < 1) return false;
2519 uint64_t off = rec->off + rec->rsiz;
2520 uint32_t rsiz = rec->rsiz;
2521 uint8_t magic;
2522 if(tchdbseekreadtry(hdb, off, &magic, sizeof(magic)) && magic != HDBMAGICFB) return false;
2523 HDBFB *pv = hdb->fbpool;
2524 HDBFB *ep = pv + hdb->fbpnum;
2525 while(pv < ep){
2526 if(pv->off == off && rsiz + pv->rsiz >= nsiz){
2527 if(hdb->dfcur == pv->off) hdb->dfcur += pv->rsiz;
2528 if(hdb->iter == pv->off) hdb->iter += pv->rsiz;
2529 rec->rsiz += pv->rsiz;
2530 memmove(pv, pv + 1, sizeof(*pv) * ((ep - pv) - 1));
2531 hdb->fbpnum--;
2532 return true;
2533 }
2534 pv++;
2535 }
2536 return false;
2537 }
2538 uint64_t off = rec->off + rec->rsiz;
2539 TCHREC nrec;
2540 char nbuf[HDBIOBUFSIZ];
2541 while(off < hdb->fsiz){
2542 nrec.off = off;
2543 if(!tchdbreadrec(hdb, &nrec, nbuf)) return false;
2544 if(nrec.magic != HDBMAGICFB) break;
2545 if(hdb->dfcur == off) hdb->dfcur += nrec.rsiz;
2546 if(hdb->iter == off) hdb->iter += nrec.rsiz;
2547 off += nrec.rsiz;
2548 }
2549 uint32_t jsiz = off - rec->off;
2550 if(jsiz < nsiz) return false;
2551 rec->rsiz = jsiz;
2552 uint64_t base = rec->off;
2553 HDBFB *wp = hdb->fbpool;
2554 HDBFB *cur = wp;
2555 HDBFB *end = wp + hdb->fbpnum;
2556 while(cur < end){
2557 if(cur->off < base || cur->off > off) *(wp++) = *cur;
2558 cur++;
2559 }
2560 hdb->fbpnum = wp - (HDBFB *)hdb->fbpool;
2561 if(jsiz > nsiz * 2){
2562 uint32_t psiz = tchdbpadsize(hdb, rec->off + nsiz);
2563 uint64_t noff = rec->off + nsiz + psiz;
2564 if(jsiz >= (noff - rec->off) * 2){
2565 TCDODEBUG(hdb->cnt_dividefbp++);
2566 nsiz = off - noff;
2567 if(!tchdbwritefb(hdb, noff, nsiz)) return false;
2568 rec->rsiz = noff - rec->off;
2569 tchdbfbpinsert(hdb, noff, nsiz);
2570 }
2571 }
2572 return true;
2573 }
2574
2575
2576 /* Remove blocks of a region from the free block pool.
2577 `hdb' specifies the hash database object.
2578 `base' specifies the base offset of the region.
2579 `next' specifies the offset of the next region.
2580 `off' specifies the offset of the block.
2581 `rsiz' specifies the size of the block. */
tchdbfbptrim(TCHDB * hdb,uint64_t base,uint64_t next,uint64_t off,uint32_t rsiz)2582 static void tchdbfbptrim(TCHDB *hdb, uint64_t base, uint64_t next, uint64_t off, uint32_t rsiz){
2583 assert(hdb && base > 0 && next > 0);
2584 if(hdb->fpow < 1) return;
2585 if(hdb->fbpnum < 1){
2586 if(off > 0){
2587 HDBFB *fbpool = hdb->fbpool;
2588 fbpool->off = off;
2589 fbpool->rsiz = rsiz;
2590 hdb->fbpnum = 1;
2591 }
2592 return;
2593 }
2594 HDBFB *wp = hdb->fbpool;
2595 HDBFB *cur = wp;
2596 HDBFB *end = wp + hdb->fbpnum;
2597 if(hdb->fbpnum >= hdb->fbpmax * HDBFBPALWRAT) cur++;
2598 while(cur < end){
2599 if(cur->rsiz >= rsiz && off > 0){
2600 TCDODEBUG(hdb->cnt_insertfbp++);
2601 wp->off = off;
2602 wp->rsiz = rsiz;
2603 wp++;
2604 off = 0;
2605 } else if(cur->off < base || cur->off >= next){
2606 *(wp++) = *cur;
2607 }
2608 cur++;
2609 }
2610 if(off > 0){
2611 TCDODEBUG(hdb->cnt_insertfbp++);
2612 wp->off = off;
2613 wp->rsiz = rsiz;
2614 wp++;
2615 off = 0;
2616 }
2617 hdb->fbpnum = wp - (HDBFB *)hdb->fbpool;
2618 }
2619
2620
2621 /* Write a free block into the file.
2622 `hdb' specifies the hash database object.
2623 `off' specifies the offset of the block.
2624 `rsiz' specifies the size of the block.
2625 The return value is true if successful, else, it is false. */
tchdbwritefb(TCHDB * hdb,uint64_t off,uint32_t rsiz)2626 static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz){
2627 assert(hdb && off > 0 && rsiz > 0);
2628 char rbuf[HDBMAXHSIZ];
2629 char *wp = rbuf;
2630 *(uint8_t *)(wp++) = HDBMAGICFB;
2631 uint32_t lnum = TCHTOIL(rsiz);
2632 memcpy(wp, &lnum, sizeof(lnum));
2633 wp += sizeof(lnum);
2634 if(!tchdbseekwrite(hdb, off, rbuf, wp - rbuf)) return false;
2635 return true;
2636 }
2637
2638
2639 /* Write a record into the file.
2640 `hdb' specifies the hash database object.
2641 `rec' specifies the record object.
2642 `bidx' specifies the index of the bucket.
2643 `entoff' specifies the offset of the tree entry.
2644 The return value is true if successful, else, it is false. */
tchdbwriterec(TCHDB * hdb,TCHREC * rec,uint64_t bidx,off_t entoff)2645 static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff){
2646 assert(hdb && rec);
2647 TCDODEBUG(hdb->cnt_writerec++);
2648 char stack[HDBIOBUFSIZ];
2649 int bsiz = (rec->rsiz > 0) ? rec->rsiz : HDBMAXHSIZ + rec->ksiz + rec->vsiz + hdb->align;
2650 char *rbuf;
2651 if(bsiz <= HDBIOBUFSIZ){
2652 rbuf = stack;
2653 } else {
2654 TCMALLOC(rbuf, bsiz);
2655 }
2656 char *wp = rbuf;
2657 *(uint8_t *)(wp++) = HDBMAGICREC;
2658 *(uint8_t *)(wp++) = rec->hash;
2659 if(hdb->ba64){
2660 uint64_t llnum;
2661 llnum = rec->left >> hdb->apow;
2662 llnum = TCHTOILL(llnum);
2663 memcpy(wp, &llnum, sizeof(llnum));
2664 wp += sizeof(llnum);
2665 llnum = rec->right >> hdb->apow;
2666 llnum = TCHTOILL(llnum);
2667 memcpy(wp, &llnum, sizeof(llnum));
2668 wp += sizeof(llnum);
2669 } else {
2670 uint32_t lnum;
2671 lnum = rec->left >> hdb->apow;
2672 lnum = TCHTOIL(lnum);
2673 memcpy(wp, &lnum, sizeof(lnum));
2674 wp += sizeof(lnum);
2675 lnum = rec->right >> hdb->apow;
2676 lnum = TCHTOIL(lnum);
2677 memcpy(wp, &lnum, sizeof(lnum));
2678 wp += sizeof(lnum);
2679 }
2680 uint16_t snum;
2681 char *pwp = wp;
2682 wp += sizeof(snum);
2683 int step;
2684 TCSETVNUMBUF(step, wp, rec->ksiz);
2685 wp += step;
2686 TCSETVNUMBUF(step, wp, rec->vsiz);
2687 wp += step;
2688 int32_t hsiz = wp - rbuf;
2689 int32_t rsiz = hsiz + rec->ksiz + rec->vsiz;
2690 int32_t finc = 0;
2691 if(rec->rsiz < 1){
2692 uint16_t psiz = tchdbpadsize(hdb, hdb->fsiz + rsiz);
2693 rec->rsiz = rsiz + psiz;
2694 rec->psiz = psiz;
2695 finc = rec->rsiz;
2696 } else if(rsiz > rec->rsiz){
2697 if(rbuf != stack) TCFREE(rbuf);
2698 if(!HDBLOCKDB(hdb)) return false;
2699 if(tchdbfbpsplice(hdb, rec, rsiz)){
2700 TCDODEBUG(hdb->cnt_splicefbp++);
2701 bool rv = tchdbwriterec(hdb, rec, bidx, entoff);
2702 HDBUNLOCKDB(hdb);
2703 return rv;
2704 }
2705 TCDODEBUG(hdb->cnt_moverec++);
2706 if(!tchdbwritefb(hdb, rec->off, rec->rsiz)){
2707 HDBUNLOCKDB(hdb);
2708 return false;
2709 }
2710 tchdbfbpinsert(hdb, rec->off, rec->rsiz);
2711 rec->rsiz = rsiz;
2712 if(!tchdbfbpsearch(hdb, rec)){
2713 HDBUNLOCKDB(hdb);
2714 return false;
2715 }
2716 bool rv = tchdbwriterec(hdb, rec, bidx, entoff);
2717 HDBUNLOCKDB(hdb);
2718 return rv;
2719 } else {
2720 TCDODEBUG(hdb->cnt_reuserec++);
2721 uint32_t psiz = rec->rsiz - rsiz;
2722 if(psiz > UINT16_MAX){
2723 TCDODEBUG(hdb->cnt_dividefbp++);
2724 psiz = tchdbpadsize(hdb, rec->off + rsiz);
2725 uint64_t noff = rec->off + rsiz + psiz;
2726 uint32_t nsiz = rec->rsiz - rsiz - psiz;
2727 rec->rsiz = noff - rec->off;
2728 rec->psiz = psiz;
2729 if(!tchdbwritefb(hdb, noff, nsiz)){
2730 if(rbuf != stack) TCFREE(rbuf);
2731 return false;
2732 }
2733 if(!HDBLOCKDB(hdb)){
2734 if(rbuf != stack) TCFREE(rbuf);
2735 return false;
2736 }
2737 tchdbfbpinsert(hdb, noff, nsiz);
2738 HDBUNLOCKDB(hdb);
2739 }
2740 rec->psiz = psiz;
2741 }
2742 snum = rec->psiz;
2743 snum = TCHTOIS(snum);
2744 memcpy(pwp, &snum, sizeof(snum));
2745 rsiz = rec->rsiz;
2746 rsiz -= hsiz;
2747 memcpy(wp, rec->kbuf, rec->ksiz);
2748 wp += rec->ksiz;
2749 rsiz -= rec->ksiz;
2750 memcpy(wp, rec->vbuf, rec->vsiz);
2751 wp += rec->vsiz;
2752 rsiz -= rec->vsiz;
2753 memset(wp, 0, rsiz);
2754 if(!tchdbseekwrite(hdb, rec->off, rbuf, rec->rsiz)){
2755 if(rbuf != stack) TCFREE(rbuf);
2756 return false;
2757 }
2758 if(finc != 0){
2759 hdb->fsiz += finc;
2760 uint64_t llnum = hdb->fsiz;
2761 llnum = TCHTOILL(llnum);
2762 memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum));
2763 }
2764 if(rbuf != stack) TCFREE(rbuf);
2765 if(entoff > 0){
2766 if(hdb->ba64){
2767 uint64_t llnum = rec->off >> hdb->apow;
2768 llnum = TCHTOILL(llnum);
2769 if(!tchdbseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
2770 } else {
2771 uint32_t lnum = rec->off >> hdb->apow;
2772 lnum = TCHTOIL(lnum);
2773 if(!tchdbseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
2774 }
2775 } else {
2776 tchdbsetbucket(hdb, bidx, rec->off);
2777 }
2778 return true;
2779 }
2780
2781
2782 /* Read a record from the file.
2783 `hdb' specifies the hash database object.
2784 `rec' specifies the record object.
2785 `rbuf' specifies the buffer for reading.
2786 The return value is true if successful, else, it is false. */
tchdbreadrec(TCHDB * hdb,TCHREC * rec,char * rbuf)2787 static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf){
2788 assert(hdb && rec && rbuf);
2789 TCDODEBUG(hdb->cnt_readrec++);
2790 int rsiz = hdb->runit;
2791 if(!tchdbseekreadtry(hdb, rec->off, rbuf, rsiz)){
2792 if(!HDBLOCKDB(hdb)) return false;
2793 rsiz = hdb->fsiz - rec->off;
2794 if(rsiz > hdb->runit){
2795 rsiz = hdb->runit;
2796 } else if(rsiz < (int)(sizeof(uint8_t) + sizeof(uint32_t))){
2797 tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__);
2798 HDBUNLOCKDB(hdb);
2799 return false;
2800 }
2801 if(!tchdbseekread(hdb, rec->off, rbuf, rsiz)){
2802 HDBUNLOCKDB(hdb);
2803 return false;
2804 }
2805 HDBUNLOCKDB(hdb);
2806 }
2807 const char *rp = rbuf;
2808 rec->magic = *(uint8_t *)(rp++);
2809 if(rec->magic == HDBMAGICFB){
2810 uint32_t lnum;
2811 memcpy(&lnum, rp, sizeof(lnum));
2812 rec->rsiz = TCITOHL(lnum);
2813 return true;
2814 } else if(rec->magic != HDBMAGICREC){
2815 tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__);
2816 return false;
2817 }
2818 rec->hash = *(uint8_t *)(rp++);
2819 if(hdb->ba64){
2820 uint64_t llnum;
2821 memcpy(&llnum, rp, sizeof(llnum));
2822 rec->left = TCITOHLL(llnum) << hdb->apow;
2823 rp += sizeof(llnum);
2824 memcpy(&llnum, rp, sizeof(llnum));
2825 rec->right = TCITOHLL(llnum) << hdb->apow;
2826 rp += sizeof(llnum);
2827 } else {
2828 uint32_t lnum;
2829 memcpy(&lnum, rp, sizeof(lnum));
2830 rec->left = (uint64_t)TCITOHL(lnum) << hdb->apow;
2831 rp += sizeof(lnum);
2832 memcpy(&lnum, rp, sizeof(lnum));
2833 rec->right = (uint64_t)TCITOHL(lnum) << hdb->apow;
2834 rp += sizeof(lnum);
2835 }
2836 uint16_t snum;
2837 memcpy(&snum, rp, sizeof(snum));
2838 rec->psiz = TCITOHS(snum);
2839 rp += sizeof(snum);
2840 uint32_t lnum;
2841 int step;
2842 TCREADVNUMBUF(rp, lnum, step);
2843 rec->ksiz = lnum;
2844 rp += step;
2845 TCREADVNUMBUF(rp, lnum, step);
2846 rec->vsiz = lnum;
2847 rp += step;
2848 int32_t hsiz = rp - rbuf;
2849 rec->rsiz = hsiz + rec->ksiz + rec->vsiz + rec->psiz;
2850 rec->kbuf = NULL;
2851 rec->vbuf = NULL;
2852 rec->boff = rec->off + hsiz;
2853 rec->bbuf = NULL;
2854 rsiz -= hsiz;
2855 if(rsiz >= rec->ksiz){
2856 rec->kbuf = rp;
2857 rsiz -= rec->ksiz;
2858 rp += rec->ksiz;
2859 if(rsiz >= rec->vsiz) rec->vbuf = rp;
2860 }
2861 return true;
2862 }
2863
2864
2865 /* Read the body of a record from the file.
2866 `hdb' specifies the hash database object.
2867 `rec' specifies the record object.
2868 The return value is true if successful, else, it is false. */
tchdbreadrecbody(TCHDB * hdb,TCHREC * rec)2869 static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec){
2870 assert(hdb && rec);
2871 int32_t bsiz = rec->ksiz + rec->vsiz;
2872 TCMALLOC(rec->bbuf, bsiz + 1);
2873 if(!tchdbseekread(hdb, rec->boff, rec->bbuf, bsiz)) return false;
2874 rec->kbuf = rec->bbuf;
2875 rec->vbuf = rec->bbuf + rec->ksiz;
2876 return true;
2877 }
2878
2879
2880 /* Remove a record from the file.
2881 `hdb' specifies the hash database object.
2882 `rec' specifies the record object.
2883 `rbuf' specifies the buffer for reading.
2884 `bidx' specifies the index of the bucket.
2885 `entoff' specifies the offset of the tree entry.
2886 The return value is true if successful, else, it is false. */
tchdbremoverec(TCHDB * hdb,TCHREC * rec,char * rbuf,uint64_t bidx,off_t entoff)2887 static bool tchdbremoverec(TCHDB *hdb, TCHREC *rec, char *rbuf, uint64_t bidx, off_t entoff){
2888 assert(hdb && rec);
2889 if(!tchdbwritefb(hdb, rec->off, rec->rsiz)) return false;
2890 if(!HDBLOCKDB(hdb)) return false;
2891 tchdbfbpinsert(hdb, rec->off, rec->rsiz);
2892 HDBUNLOCKDB(hdb);
2893 uint64_t child;
2894 if(rec->left > 0 && rec->right < 1){
2895 child = rec->left;
2896 } else if(rec->left < 1 && rec->right > 0){
2897 child = rec->right;
2898 } else if(rec->left < 1){
2899 child = 0;
2900 } else {
2901 child = rec->left;
2902 uint64_t right = rec->right;
2903 rec->right = child;
2904 while(rec->right > 0){
2905 rec->off = rec->right;
2906 if(!tchdbreadrec(hdb, rec, rbuf)) return false;
2907 }
2908 if(hdb->ba64){
2909 off_t toff = rec->off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint64_t));
2910 uint64_t llnum = right >> hdb->apow;
2911 llnum = TCHTOILL(llnum);
2912 if(!tchdbseekwrite(hdb, toff, &llnum, sizeof(uint64_t))) return false;
2913 } else {
2914 off_t toff = rec->off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
2915 uint32_t lnum = right >> hdb->apow;
2916 lnum = TCHTOIL(lnum);
2917 if(!tchdbseekwrite(hdb, toff, &lnum, sizeof(uint32_t))) return false;
2918 }
2919 }
2920 if(entoff > 0){
2921 if(hdb->ba64){
2922 uint64_t llnum = child >> hdb->apow;
2923 llnum = TCHTOILL(llnum);
2924 if(!tchdbseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
2925 } else {
2926 uint32_t lnum = child >> hdb->apow;
2927 lnum = TCHTOIL(lnum);
2928 if(!tchdbseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
2929 }
2930 } else {
2931 tchdbsetbucket(hdb, bidx, child);
2932 }
2933 if(!HDBLOCKDB(hdb)) return false;
2934 hdb->rnum--;
2935 uint64_t llnum = hdb->rnum;
2936 llnum = TCHTOILL(llnum);
2937 memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
2938 HDBUNLOCKDB(hdb);
2939 return true;
2940 }
2941
2942
2943 /* Remove a record from the file.
2944 `hdb' specifies the hash database object.
2945 `rec' specifies the record object.
2946 `rbuf' specifies the buffer for reading.
2947 `destoff' specifies the offset of the destination.
2948 The return value is true if successful, else, it is false. */
tchdbshiftrec(TCHDB * hdb,TCHREC * rec,char * rbuf,off_t destoff)2949 static bool tchdbshiftrec(TCHDB *hdb, TCHREC *rec, char *rbuf, off_t destoff){
2950 assert(hdb && rec && rbuf && destoff >= 0);
2951 TCDODEBUG(hdb->cnt_shiftrec++);
2952 if(!rec->vbuf && !tchdbreadrecbody(hdb, rec)) return false;
2953 uint8_t hash;
2954 uint64_t bidx = tchdbbidx(hdb, rec->kbuf, rec->ksiz, &hash);
2955 off_t off = tchdbgetbucket(hdb, bidx);
2956 if(rec->off == off){
2957 bool err = false;
2958 rec->off = destoff;
2959 if(!tchdbwriterec(hdb, rec, bidx, 0)) err = true;
2960 TCFREE(rec->bbuf);
2961 rec->kbuf = NULL;
2962 rec->vbuf = NULL;
2963 rec->bbuf = NULL;
2964 return !err;
2965 }
2966 TCHREC trec;
2967 char tbuf[HDBIOBUFSIZ];
2968 char *bbuf = rec->bbuf;
2969 const char *kbuf = rec->kbuf;
2970 int ksiz = rec->ksiz;
2971 const char *vbuf = rec->vbuf;
2972 int vsiz = rec->vsiz;
2973 rec->kbuf = NULL;
2974 rec->vbuf = NULL;
2975 rec->bbuf = NULL;
2976 off_t entoff = 0;
2977 while(off > 0){
2978 trec.off = off;
2979 if(!tchdbreadrec(hdb, &trec, tbuf)){
2980 TCFREE(bbuf);
2981 return false;
2982 }
2983 if(hash > trec.hash){
2984 off = trec.left;
2985 entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t));
2986 } else if(hash < trec.hash){
2987 off = trec.right;
2988 entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
2989 (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
2990 } else {
2991 if(!trec.kbuf && !tchdbreadrecbody(hdb, &trec)){
2992 TCFREE(bbuf);
2993 return false;
2994 }
2995 int kcmp = tcreckeycmp(kbuf, ksiz, trec.kbuf, trec.ksiz);
2996 if(kcmp > 0){
2997 off = trec.left;
2998 TCFREE(trec.bbuf);
2999 trec.kbuf = NULL;
3000 trec.bbuf = NULL;
3001 entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t));
3002 } else if(kcmp < 0){
3003 off = trec.right;
3004 TCFREE(trec.bbuf);
3005 trec.kbuf = NULL;
3006 trec.bbuf = NULL;
3007 entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
3008 (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
3009 } else {
3010 TCFREE(trec.bbuf);
3011 trec.bbuf = NULL;
3012 bool err = false;
3013 rec->off = destoff;
3014 rec->kbuf = kbuf;
3015 rec->ksiz = ksiz;
3016 rec->vbuf = vbuf;
3017 rec->vsiz = vsiz;
3018 if(!tchdbwriterec(hdb, rec, bidx, entoff)) err = true;
3019 TCFREE(bbuf);
3020 return !err;
3021 }
3022 }
3023 }
3024 TCFREE(bbuf);
3025 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
3026 return false;
3027 }
3028
3029
3030 /* Compare keys of two records.
3031 `abuf' specifies the pointer to the region of the former.
3032 `asiz' specifies the size of the region.
3033 `bbuf' specifies the pointer to the region of the latter.
3034 `bsiz' specifies the size of the region.
3035 The return value is 0 if two equals, positive if the formar is big, else, negative. */
tcreckeycmp(const char * abuf,int asiz,const char * bbuf,int bsiz)3036 static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz){
3037 assert(abuf && asiz >= 0 && bbuf && bsiz >= 0);
3038 if(asiz > bsiz) return 1;
3039 if(asiz < bsiz) return -1;
3040 return memcmp(abuf, bbuf, asiz);
3041 }
3042
3043
3044 /* Flush the delayed record pool.
3045 `hdb' specifies the hash database object.
3046 The return value is true if successful, else, it is false. */
tchdbflushdrp(TCHDB * hdb)3047 static bool tchdbflushdrp(TCHDB *hdb){
3048 assert(hdb);
3049 if(!HDBLOCKDB(hdb)) return false;
3050 if(!hdb->drpool){
3051 HDBUNLOCKDB(hdb);
3052 return true;
3053 }
3054 TCDODEBUG(hdb->cnt_flushdrp++);
3055 if(!tchdbseekwrite(hdb, hdb->drpoff, TCXSTRPTR(hdb->drpool), TCXSTRSIZE(hdb->drpool))){
3056 HDBUNLOCKDB(hdb);
3057 return false;
3058 }
3059 const char *rp = TCXSTRPTR(hdb->drpdef);
3060 int size = TCXSTRSIZE(hdb->drpdef);
3061 while(size > 0){
3062 int ksiz, vsiz;
3063 memcpy(&ksiz, rp, sizeof(int));
3064 rp += sizeof(int);
3065 memcpy(&vsiz, rp, sizeof(int));
3066 rp += sizeof(int);
3067 const char *kbuf = rp;
3068 rp += ksiz;
3069 const char *vbuf = rp;
3070 rp += vsiz;
3071 uint8_t hash;
3072 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
3073 if(!tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDOVER)){
3074 tcxstrdel(hdb->drpdef);
3075 tcxstrdel(hdb->drpool);
3076 hdb->drpool = NULL;
3077 hdb->drpdef = NULL;
3078 hdb->drpoff = 0;
3079 HDBUNLOCKDB(hdb);
3080 return false;
3081 }
3082 size -= sizeof(int) * 2 + ksiz + vsiz;
3083 }
3084 tcxstrdel(hdb->drpdef);
3085 tcxstrdel(hdb->drpool);
3086 hdb->drpool = NULL;
3087 hdb->drpdef = NULL;
3088 hdb->drpoff = 0;
3089 uint64_t llnum;
3090 llnum = hdb->rnum;
3091 llnum = TCHTOILL(llnum);
3092 memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
3093 llnum = hdb->fsiz;
3094 llnum = TCHTOILL(llnum);
3095 memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum));
3096 HDBUNLOCKDB(hdb);
3097 return true;
3098 }
3099
3100
3101 /* Adjust the caches for leaves and nodes.
3102 `hdb' specifies the hash tree database object. */
tchdbcacheadjust(TCHDB * hdb)3103 static void tchdbcacheadjust(TCHDB *hdb){
3104 assert(hdb);
3105 TCDODEBUG(hdb->cnt_adjrecc++);
3106 tcmdbcutfront(hdb->recc, HDBCACHEOUT);
3107 }
3108
3109
3110 /* Initialize the write ahead logging file.
3111 `hdb' specifies the hash database object.
3112 If successful, the return value is true, else, it is false. */
tchdbwalinit(TCHDB * hdb)3113 static bool tchdbwalinit(TCHDB *hdb){
3114 assert(hdb);
3115 if(lseek(hdb->walfd, 0, SEEK_SET) == -1){
3116 tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__);
3117 return false;
3118 }
3119 if(ftruncate(hdb->walfd, 0) == -1){
3120 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
3121 return false;
3122 }
3123 uint64_t llnum = hdb->fsiz;
3124 llnum = TCHTOILL(llnum);
3125 if(!tcwrite(hdb->walfd, &llnum, sizeof(llnum))){
3126 tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
3127 return false;
3128 }
3129 hdb->walend = hdb->fsiz;
3130 if(!tchdbwalwrite(hdb, 0, HDBHEADSIZ)) return false;
3131 return true;
3132 }
3133
3134
3135 /* Write an event into the write ahead logging file.
3136 `hdb' specifies the hash database object.
3137 `off' specifies the offset of the region to be updated.
3138 `size' specifies the size of the region.
3139 If successful, the return value is true, else, it is false. */
tchdbwalwrite(TCHDB * hdb,uint64_t off,int64_t size)3140 static bool tchdbwalwrite(TCHDB *hdb, uint64_t off, int64_t size){
3141 assert(hdb && off >= 0 && size >= 0);
3142 if(off + size > hdb->walend) size = hdb->walend - off;
3143 if(size < 1) return true;
3144 char stack[HDBIOBUFSIZ];
3145 char *buf;
3146 if(size + sizeof(off) + sizeof(size) <= HDBIOBUFSIZ){
3147 buf = stack;
3148 } else {
3149 TCMALLOC(buf, size + sizeof(off) + sizeof(size));
3150 }
3151 char *wp = buf;
3152 uint64_t llnum = TCHTOILL(off);
3153 memcpy(wp, &llnum, sizeof(llnum));
3154 wp += sizeof(llnum);
3155 uint32_t lnum = TCHTOIL(size);
3156 memcpy(wp, &lnum, sizeof(lnum));
3157 wp += sizeof(lnum);
3158 if(!tchdbseekread(hdb, off, wp, size)){
3159 if(buf != stack) TCFREE(buf);
3160 return false;
3161 }
3162 wp += size;
3163 if(!HDBLOCKWAL(hdb)) return false;
3164 if(!tcwrite(hdb->walfd, buf, wp - buf)){
3165 tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
3166 if(buf != stack) TCFREE(buf);
3167 HDBUNLOCKWAL(hdb);
3168 return false;
3169 }
3170 if(buf != stack) TCFREE(buf);
3171 if((hdb->omode & HDBOTSYNC) && fsync(hdb->walfd) == -1){
3172 tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__);
3173 HDBUNLOCKWAL(hdb);
3174 return false;
3175 }
3176 HDBUNLOCKWAL(hdb);
3177 return true;
3178 }
3179
3180
3181 /* Restore the database from the write ahead logging file.
3182 `hdb' specifies the hash database object.
3183 `path' specifies the path of the database file.
3184 If successful, the return value is true, else, it is false. */
tchdbwalrestore(TCHDB * hdb,const char * path)3185 static int tchdbwalrestore(TCHDB *hdb, const char *path){
3186 assert(hdb && path);
3187 char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, HDBWALSUFFIX);
3188 int walfd = open(tpath, O_RDONLY, HDBFILEMODE);
3189 TCFREE(tpath);
3190 if(walfd < 0) return false;
3191 bool err = false;
3192 uint64_t walsiz = 0;
3193 struct stat sbuf;
3194 if(fstat(walfd, &sbuf) == 0){
3195 walsiz = sbuf.st_size;
3196 } else {
3197 tchdbsetecode(hdb, TCESTAT, __FILE__, __LINE__, __func__);
3198 err = true;
3199 }
3200 if(walsiz >= sizeof(walsiz) + HDBHEADSIZ){
3201 int dbfd = hdb->fd;
3202 int tfd = -1;
3203 if(!(hdb->omode & HDBOWRITER)){
3204 tfd = open(path, O_WRONLY, HDBFILEMODE);
3205 if(tfd >= 0){
3206 dbfd = tfd;
3207 } else {
3208 int ecode = TCEOPEN;
3209 switch(errno){
3210 case EACCES: ecode = TCENOPERM; break;
3211 case ENOENT: ecode = TCENOFILE; break;
3212 case ENOTDIR: ecode = TCENOFILE; break;
3213 }
3214 tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__);
3215 err = true;
3216 }
3217 }
3218 uint64_t fsiz = 0;
3219 if(tcread(walfd, &fsiz, sizeof(fsiz))){
3220 fsiz = TCITOHLL(fsiz);
3221 } else {
3222 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
3223 err = true;
3224 }
3225 TCLIST *list = tclistnew();
3226 uint64_t waloff = sizeof(fsiz);
3227 char stack[HDBIOBUFSIZ];
3228 while(waloff < walsiz){
3229 uint64_t off;
3230 uint32_t size;
3231 if(!tcread(walfd, stack, sizeof(off) + sizeof(size))){
3232 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
3233 err = true;
3234 break;
3235 }
3236 memcpy(&off, stack, sizeof(off));
3237 off = TCITOHLL(off);
3238 memcpy(&size, stack + sizeof(off), sizeof(size));
3239 size = TCITOHL(size);
3240 char *buf;
3241 if(sizeof(off) + size <= HDBIOBUFSIZ){
3242 buf = stack;
3243 } else {
3244 TCMALLOC(buf, sizeof(off) + size);
3245 }
3246 *(uint64_t *)buf = off;
3247 if(!tcread(walfd, buf + sizeof(off), size)){
3248 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
3249 err = true;
3250 if(buf != stack) TCFREE(buf);
3251 break;
3252 }
3253 TCLISTPUSH(list, buf, sizeof(off) + size);
3254 if(buf != stack) TCFREE(buf);
3255 waloff += sizeof(off) + sizeof(size) + size;
3256 }
3257 size_t xmsiz = 0;
3258 if(hdb->fd >= 0 && hdb->map) xmsiz = (hdb->xmsiz > hdb->msiz) ? hdb->xmsiz : hdb->msiz;
3259 for(int i = TCLISTNUM(list) - 1; i >= 0; i--){
3260 const char *rec;
3261 int size;
3262 TCLISTVAL(rec, list, i, size);
3263 uint64_t off = *(uint64_t *)rec;
3264 rec += sizeof(off);
3265 size -= sizeof(off);
3266 if(lseek(dbfd, off, SEEK_SET) == -1){
3267 tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__);
3268 err = true;
3269 break;
3270 }
3271 if(!tcwrite(dbfd, rec, size)){
3272 tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
3273 err = true;
3274 break;
3275 }
3276 if(!TCUBCACHE && off < xmsiz){
3277 size = (size <= xmsiz - off) ? size : xmsiz - off;
3278 memcpy(hdb->map + off, rec, size);
3279 }
3280 }
3281 tclistdel(list);
3282 if(ftruncate(dbfd, fsiz) == -1){
3283 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
3284 err = true;
3285 }
3286 if((hdb->omode & HDBOTSYNC) && fsync(dbfd) == -1){
3287 tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__);
3288 err = true;
3289 }
3290 if(tfd >= 0 && close(tfd) == -1){
3291 tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__);
3292 err = true;
3293 }
3294 } else {
3295 err = true;
3296 }
3297 if(close(walfd) == -1){
3298 tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__);
3299 err = true;
3300 }
3301 return !err;
3302 }
3303
3304
3305 /* Remove the write ahead logging file.
3306 `hdb' specifies the hash database object.
3307 `path' specifies the path of the database file.
3308 If successful, the return value is true, else, it is false. */
tchdbwalremove(TCHDB * hdb,const char * path)3309 static bool tchdbwalremove(TCHDB *hdb, const char *path){
3310 assert(hdb && path);
3311 char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, HDBWALSUFFIX);
3312 bool err = false;
3313 if(unlink(tpath) == -1 && errno != ENOENT){
3314 tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__);
3315 err = true;
3316 }
3317 TCFREE(tpath);
3318 return !err;
3319 }
3320
3321
3322 /* Open a database file and connect a hash database object.
3323 `hdb' specifies the hash database object.
3324 `path' specifies the path of the database file.
3325 `omode' specifies the connection mode.
3326 If successful, the return value is true, else, it is false. */
tchdbopenimpl(TCHDB * hdb,const char * path,int omode)3327 static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode){
3328 assert(hdb && path);
3329 int mode = O_RDONLY;
3330 if(omode & HDBOWRITER){
3331 mode = O_RDWR;
3332 if(omode & HDBOCREAT) mode |= O_CREAT;
3333 }
3334 int fd = open(path, mode, HDBFILEMODE);
3335 if(fd < 0){
3336 int ecode = TCEOPEN;
3337 switch(errno){
3338 case EACCES: ecode = TCENOPERM; break;
3339 case ENOENT: ecode = TCENOFILE; break;
3340 case ENOTDIR: ecode = TCENOFILE; break;
3341 }
3342 tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__);
3343 return false;
3344 }
3345 if(!(omode & HDBONOLCK)){
3346 if(!tclock(fd, omode & HDBOWRITER, omode & HDBOLCKNB)){
3347 tchdbsetecode(hdb, TCELOCK, __FILE__, __LINE__, __func__);
3348 close(fd);
3349 return false;
3350 }
3351 }
3352 if((omode & HDBOWRITER) && (omode & HDBOTRUNC)){
3353 if(ftruncate(fd, 0) == -1){
3354 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
3355 close(fd);
3356 return false;
3357 }
3358 if(!tchdbwalremove(hdb, path)){
3359 close(fd);
3360 return false;
3361 }
3362 }
3363 struct stat sbuf;
3364 if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
3365 tchdbsetecode(hdb, TCESTAT, __FILE__, __LINE__, __func__);
3366 close(fd);
3367 return false;
3368 }
3369 char hbuf[HDBHEADSIZ];
3370 if((omode & HDBOWRITER) && sbuf.st_size < 1){
3371 hdb->flags = 0;
3372 hdb->rnum = 0;
3373 uint32_t fbpmax = 1 << hdb->fpow;
3374 uint32_t fbpsiz = HDBFBPBSIZ + fbpmax * HDBFBPESIZ;
3375 int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t);
3376 hdb->align = 1 << hdb->apow;
3377 hdb->fsiz = HDBHEADSIZ + besiz * hdb->bnum + fbpsiz;
3378 hdb->fsiz += tchdbpadsize(hdb, hdb->fsiz);
3379 hdb->frec = hdb->fsiz;
3380 tchdbdumpmeta(hdb, hbuf);
3381 bool err = false;
3382 if(!tcwrite(fd, hbuf, HDBHEADSIZ)) err = true;
3383 char pbuf[HDBIOBUFSIZ];
3384 memset(pbuf, 0, HDBIOBUFSIZ);
3385 uint64_t psiz = hdb->fsiz - HDBHEADSIZ;
3386 while(psiz > 0){
3387 if(psiz > HDBIOBUFSIZ){
3388 if(!tcwrite(fd, pbuf, HDBIOBUFSIZ)) err = true;
3389 psiz -= HDBIOBUFSIZ;
3390 } else {
3391 if(!tcwrite(fd, pbuf, psiz)) err = true;
3392 psiz = 0;
3393 }
3394 }
3395 if(err){
3396 tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
3397 close(fd);
3398 return false;
3399 }
3400 sbuf.st_size = hdb->fsiz;
3401 }
3402 if(lseek(fd, 0, SEEK_SET) == -1){
3403 tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__);
3404 close(fd);
3405 return false;
3406 }
3407 if(!tcread(fd, hbuf, HDBHEADSIZ)){
3408 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
3409 close(fd);
3410 return false;
3411 }
3412 int type = hdb->type;
3413 tchdbloadmeta(hdb, hbuf);
3414 if((hdb->flags & HDBFOPEN) && tchdbwalrestore(hdb, path)){
3415 if(lseek(fd, 0, SEEK_SET) == -1){
3416 tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__);
3417 close(fd);
3418 return false;
3419 }
3420 if(!tcread(fd, hbuf, HDBHEADSIZ)){
3421 tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
3422 close(fd);
3423 return false;
3424 }
3425 tchdbloadmeta(hdb, hbuf);
3426 if(!tchdbwalremove(hdb, path)){
3427 close(fd);
3428 return false;
3429 }
3430 }
3431 int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t);
3432 size_t msiz = HDBHEADSIZ + hdb->bnum * besiz;
3433 if(!(omode & HDBONOLCK)){
3434 if(memcmp(hbuf, HDBMAGICDATA, strlen(HDBMAGICDATA)) || hdb->type != type ||
3435 hdb->frec < msiz + HDBFBPBSIZ || hdb->frec > hdb->fsiz || sbuf.st_size < hdb->fsiz){
3436 tchdbsetecode(hdb, TCEMETA, __FILE__, __LINE__, __func__);
3437 close(fd);
3438 return false;
3439 }
3440 }
3441 if(((hdb->opts & HDBTDEFLATE) && !_tc_deflate) ||
3442 ((hdb->opts & HDBTBZIP) && !_tc_bzcompress) || ((hdb->opts & HDBTEXCODEC) && !hdb->enc)){
3443 tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
3444 close(fd);
3445 return false;
3446 }
3447 size_t xmsiz = (hdb->xmsiz > msiz) ? hdb->xmsiz : msiz;
3448 if(!(omode & HDBOWRITER) && xmsiz > hdb->fsiz) xmsiz = hdb->fsiz;
3449 void *map = mmap(0, xmsiz, PROT_READ | ((omode & HDBOWRITER) ? PROT_WRITE : 0),
3450 MAP_SHARED, fd, 0);
3451 if(map == MAP_FAILED){
3452 tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
3453 close(fd);
3454 return false;
3455 }
3456 hdb->fbpmax = 1 << hdb->fpow;
3457 if(omode & HDBOWRITER){
3458 TCMALLOC(hdb->fbpool, hdb->fbpmax * HDBFBPALWRAT * sizeof(HDBFB));
3459 } else {
3460 hdb->fbpool = NULL;
3461 }
3462 hdb->fbpnum = 0;
3463 hdb->fbpmis = 0;
3464 hdb->async = false;
3465 hdb->drpool = NULL;
3466 hdb->drpdef = NULL;
3467 hdb->drpoff = 0;
3468 hdb->recc = (hdb->rcnum > 0) ? tcmdbnew2(hdb->rcnum * 2 + 1) : NULL;
3469 hdb->path = tcstrdup(path);
3470 hdb->fd = fd;
3471 hdb->omode = omode;
3472 hdb->dfcur = hdb->frec;
3473 hdb->iter = 0;
3474 hdb->map = map;
3475 hdb->msiz = msiz;
3476 hdb->xfsiz = 0;
3477 if(hdb->opts & HDBTLARGE){
3478 hdb->ba32 = NULL;
3479 hdb->ba64 = (uint64_t *)((char *)map + HDBHEADSIZ);
3480 } else {
3481 hdb->ba32 = (uint32_t *)((char *)map + HDBHEADSIZ);
3482 hdb->ba64 = NULL;
3483 }
3484 hdb->align = 1 << hdb->apow;
3485 hdb->runit = tclmin(tclmax(hdb->align, HDBMINRUNIT), HDBIOBUFSIZ);
3486 hdb->zmode = (hdb->opts & HDBTDEFLATE) || (hdb->opts & HDBTBZIP) ||
3487 (hdb->opts & HDBTTCBS) || (hdb->opts & HDBTEXCODEC);
3488 hdb->ecode = TCESUCCESS;
3489 hdb->fatal = false;
3490 hdb->inode = (uint64_t)sbuf.st_ino;
3491 hdb->mtime = sbuf.st_mtime;
3492 hdb->dfcnt = 0;
3493 hdb->tran = false;
3494 hdb->walfd = -1;
3495 hdb->walend = 0;
3496 if(hdb->omode & HDBOWRITER){
3497 bool err = false;
3498 if(!(hdb->flags & HDBFOPEN) && !tchdbloadfbp(hdb)) err = true;
3499 memset(hbuf, 0, 2);
3500 if(!tchdbseekwrite(hdb, hdb->msiz, hbuf, 2)) err = true;
3501 if(err){
3502 TCFREE(hdb->path);
3503 TCFREE(hdb->fbpool);
3504 munmap(hdb->map, xmsiz);
3505 close(fd);
3506 hdb->fd = -1;
3507 return false;
3508 }
3509 tchdbsetflag(hdb, HDBFOPEN, true);
3510 }
3511 return true;
3512 }
3513
3514
3515 /* Close a hash database object.
3516 `hdb' specifies the hash database object.
3517 If successful, the return value is true, else, it is false. */
tchdbcloseimpl(TCHDB * hdb)3518 static bool tchdbcloseimpl(TCHDB *hdb){
3519 assert(hdb);
3520 bool err = false;
3521 if(hdb->recc){
3522 tcmdbdel(hdb->recc);
3523 hdb->recc = NULL;
3524 }
3525 if(hdb->omode & HDBOWRITER){
3526 if(!tchdbflushdrp(hdb)) err = true;
3527 if(hdb->tran) hdb->fbpnum = 0;
3528 if(!tchdbsavefbp(hdb)) err = true;
3529 TCFREE(hdb->fbpool);
3530 tchdbsetflag(hdb, HDBFOPEN, false);
3531 }
3532 if((hdb->omode & HDBOWRITER) && !tchdbmemsync(hdb, false)) err = true;
3533 size_t xmsiz = (hdb->xmsiz > hdb->msiz) ? hdb->xmsiz : hdb->msiz;
3534 if(!(hdb->omode & HDBOWRITER) && xmsiz > hdb->fsiz) xmsiz = hdb->fsiz;
3535 if(munmap(hdb->map, xmsiz) == -1){
3536 tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
3537 err = true;
3538 }
3539 hdb->map = NULL;
3540 if((hdb->omode & HDBOWRITER) && ftruncate(hdb->fd, hdb->fsiz) == -1){
3541 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
3542 err = true;
3543 }
3544 if(hdb->tran){
3545 if(!tchdbwalrestore(hdb, hdb->path)) err = true;
3546 hdb->tran = false;
3547 }
3548 if(hdb->walfd >= 0){
3549 if(close(hdb->walfd) == -1){
3550 tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__);
3551 err = true;
3552 }
3553 if(!hdb->fatal && !tchdbwalremove(hdb, hdb->path)) err = true;
3554 }
3555 if(close(hdb->fd) == -1){
3556 tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__);
3557 err = true;
3558 }
3559 TCFREE(hdb->path);
3560 hdb->path = NULL;
3561 hdb->fd = -1;
3562 return !err;
3563 }
3564
3565
3566 /* Store a record.
3567 `hdb' specifies the hash database object.
3568 `kbuf' specifies the pointer to the region of the key.
3569 `ksiz' specifies the size of the region of the key.
3570 `bidx' specifies the index of the bucket array.
3571 `hash' specifies the hash value for the collision tree.
3572 `vbuf' specifies the pointer to the region of the value.
3573 `vsiz' specifies the size of the region of the value.
3574 `dmode' specifies behavior when the key overlaps.
3575 If successful, the return value is true, else, it is false. */
tchdbputimpl(TCHDB * hdb,const char * kbuf,int ksiz,uint64_t bidx,uint8_t hash,const char * vbuf,int vsiz,int dmode)3576 static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash,
3577 const char *vbuf, int vsiz, int dmode){
3578 assert(hdb && kbuf && ksiz >= 0);
3579 if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
3580 off_t off = tchdbgetbucket(hdb, bidx);
3581 off_t entoff = 0;
3582 TCHREC rec;
3583 char rbuf[HDBIOBUFSIZ];
3584 while(off > 0){
3585 rec.off = off;
3586 if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
3587 if(hash > rec.hash){
3588 off = rec.left;
3589 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
3590 } else if(hash < rec.hash){
3591 off = rec.right;
3592 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
3593 (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
3594 } else {
3595 if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false;
3596 int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
3597 if(kcmp > 0){
3598 off = rec.left;
3599 TCFREE(rec.bbuf);
3600 rec.kbuf = NULL;
3601 rec.bbuf = NULL;
3602 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
3603 } else if(kcmp < 0){
3604 off = rec.right;
3605 TCFREE(rec.bbuf);
3606 rec.kbuf = NULL;
3607 rec.bbuf = NULL;
3608 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
3609 (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
3610 } else {
3611 bool rv;
3612 int nvsiz;
3613 char *nvbuf;
3614 HDBPDPROCOP *procptr;
3615 switch(dmode){
3616 case HDBPDKEEP:
3617 tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
3618 TCFREE(rec.bbuf);
3619 return false;
3620 case HDBPDCAT:
3621 if(vsiz < 1){
3622 TCFREE(rec.bbuf);
3623 return true;
3624 }
3625 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){
3626 TCFREE(rec.bbuf);
3627 return false;
3628 }
3629 nvsiz = rec.vsiz + vsiz;
3630 if(rec.bbuf){
3631 TCREALLOC(rec.bbuf, rec.bbuf, rec.ksiz + nvsiz);
3632 memcpy(rec.bbuf + rec.ksiz + rec.vsiz, vbuf, vsiz);
3633 rec.kbuf = rec.bbuf;
3634 rec.vbuf = rec.kbuf + rec.ksiz;
3635 rec.vsiz = nvsiz;
3636 } else {
3637 TCMALLOC(rec.bbuf, nvsiz + 1);
3638 memcpy(rec.bbuf, rec.vbuf, rec.vsiz);
3639 memcpy(rec.bbuf + rec.vsiz, vbuf, vsiz);
3640 rec.vbuf = rec.bbuf;
3641 rec.vsiz = nvsiz;
3642 }
3643 rv = tchdbwriterec(hdb, &rec, bidx, entoff);
3644 TCFREE(rec.bbuf);
3645 return rv;
3646 case HDBPDADDINT:
3647 if(rec.vsiz != sizeof(int)){
3648 tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
3649 TCFREE(rec.bbuf);
3650 return false;
3651 }
3652 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){
3653 TCFREE(rec.bbuf);
3654 return false;
3655 }
3656 int lnum;
3657 memcpy(&lnum, rec.vbuf, sizeof(lnum));
3658 if(*(int *)vbuf == 0){
3659 TCFREE(rec.bbuf);
3660 *(int *)vbuf = lnum;
3661 return true;
3662 }
3663 lnum += *(int *)vbuf;
3664 rec.vbuf = (char *)&lnum;
3665 *(int *)vbuf = lnum;
3666 rv = tchdbwriterec(hdb, &rec, bidx, entoff);
3667 TCFREE(rec.bbuf);
3668 return rv;
3669 case HDBPDADDDBL:
3670 if(rec.vsiz != sizeof(double)){
3671 tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
3672 TCFREE(rec.bbuf);
3673 return false;
3674 }
3675 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){
3676 TCFREE(rec.bbuf);
3677 return false;
3678 }
3679 double dnum;
3680 memcpy(&dnum, rec.vbuf, sizeof(dnum));
3681 if(*(double *)vbuf == 0.0){
3682 TCFREE(rec.bbuf);
3683 *(double *)vbuf = dnum;
3684 return true;
3685 }
3686 dnum += *(double *)vbuf;
3687 rec.vbuf = (char *)&dnum;
3688 *(double *)vbuf = dnum;
3689 rv = tchdbwriterec(hdb, &rec, bidx, entoff);
3690 TCFREE(rec.bbuf);
3691 return rv;
3692 case HDBPDPROC:
3693 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){
3694 TCFREE(rec.bbuf);
3695 return false;
3696 }
3697 procptr = *(HDBPDPROCOP **)((char *)kbuf - sizeof(procptr));
3698 nvbuf = procptr->proc(rec.vbuf, rec.vsiz, &nvsiz, procptr->op);
3699 TCFREE(rec.bbuf);
3700 if(nvbuf == (void *)-1){
3701 return tchdbremoverec(hdb, &rec, rbuf, bidx, entoff);
3702 } else if(nvbuf){
3703 rec.kbuf = kbuf;
3704 rec.ksiz = ksiz;
3705 rec.vbuf = nvbuf;
3706 rec.vsiz = nvsiz;
3707 rv = tchdbwriterec(hdb, &rec, bidx, entoff);
3708 TCFREE(nvbuf);
3709 return rv;
3710 }
3711 tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
3712 return false;
3713 default:
3714 break;
3715 }
3716 TCFREE(rec.bbuf);
3717 rec.ksiz = ksiz;
3718 rec.vsiz = vsiz;
3719 rec.kbuf = kbuf;
3720 rec.vbuf = vbuf;
3721 return tchdbwriterec(hdb, &rec, bidx, entoff);
3722 }
3723 }
3724 }
3725 if(!vbuf){
3726 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
3727 return false;
3728 }
3729 if(!HDBLOCKDB(hdb)) return false;
3730 rec.rsiz = hdb->ba64 ? sizeof(uint8_t) * 2 + sizeof(uint64_t) * 2 + sizeof(uint16_t) :
3731 sizeof(uint8_t) * 2 + sizeof(uint32_t) * 2 + sizeof(uint16_t);
3732 if(ksiz < (1U << 7)){
3733 rec.rsiz += 1;
3734 } else if(ksiz < (1U << 14)){
3735 rec.rsiz += 2;
3736 } else if(ksiz < (1U << 21)){
3737 rec.rsiz += 3;
3738 } else if(ksiz < (1U << 28)){
3739 rec.rsiz += 4;
3740 } else {
3741 rec.rsiz += 5;
3742 }
3743 if(vsiz < (1U << 7)){
3744 rec.rsiz += 1;
3745 } else if(vsiz < (1U << 14)){
3746 rec.rsiz += 2;
3747 } else if(vsiz < (1U << 21)){
3748 rec.rsiz += 3;
3749 } else if(vsiz < (1U << 28)){
3750 rec.rsiz += 4;
3751 } else {
3752 rec.rsiz += 5;
3753 }
3754 if(!tchdbfbpsearch(hdb, &rec)){
3755 HDBUNLOCKDB(hdb);
3756 return false;
3757 }
3758 rec.hash = hash;
3759 rec.left = 0;
3760 rec.right = 0;
3761 rec.ksiz = ksiz;
3762 rec.vsiz = vsiz;
3763 rec.psiz = 0;
3764 rec.kbuf = kbuf;
3765 rec.vbuf = vbuf;
3766 if(!tchdbwriterec(hdb, &rec, bidx, entoff)){
3767 HDBUNLOCKDB(hdb);
3768 return false;
3769 }
3770 hdb->rnum++;
3771 uint64_t llnum = hdb->rnum;
3772 llnum = TCHTOILL(llnum);
3773 memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
3774 HDBUNLOCKDB(hdb);
3775 return true;
3776 }
3777
3778
3779 /* Append a record to the delayed record pool.
3780 `hdb' specifies the hash database object.
3781 `kbuf' specifies the pointer to the region of the key.
3782 `ksiz' specifies the size of the region of the key.
3783 `vbuf' specifies the pointer to the region of the value.
3784 `vsiz' specifies the size of the region of the value.
3785 `hash' specifies the second hash value. */
tchdbdrpappend(TCHDB * hdb,const char * kbuf,int ksiz,const char * vbuf,int vsiz,uint8_t hash)3786 static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
3787 uint8_t hash){
3788 assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3789 TCDODEBUG(hdb->cnt_appenddrp++);
3790 char rbuf[HDBIOBUFSIZ];
3791 char *wp = rbuf;
3792 *(uint8_t *)(wp++) = HDBMAGICREC;
3793 *(uint8_t *)(wp++) = hash;
3794 if(hdb->ba64){
3795 memset(wp, 0, sizeof(uint64_t) * 2);
3796 wp += sizeof(uint64_t) * 2;
3797 } else {
3798 memset(wp, 0, sizeof(uint32_t) * 2);
3799 wp += sizeof(uint32_t) * 2;
3800 }
3801 uint16_t snum;
3802 char *pwp = wp;
3803 wp += sizeof(snum);
3804 int step;
3805 TCSETVNUMBUF(step, wp, ksiz);
3806 wp += step;
3807 TCSETVNUMBUF(step, wp, vsiz);
3808 wp += step;
3809 int32_t hsiz = wp - rbuf;
3810 int32_t rsiz = hsiz + ksiz + vsiz;
3811 uint16_t psiz = tchdbpadsize(hdb, hdb->fsiz + rsiz);
3812 hdb->fsiz += rsiz + psiz;
3813 snum = TCHTOIS(psiz);
3814 memcpy(pwp, &snum, sizeof(snum));
3815 TCXSTR *drpool = hdb->drpool;
3816 TCXSTRCAT(drpool, rbuf, hsiz);
3817 TCXSTRCAT(drpool, kbuf, ksiz);
3818 TCXSTRCAT(drpool, vbuf, vsiz);
3819 if(psiz > 0){
3820 char pbuf[psiz];
3821 memset(pbuf, 0, psiz);
3822 TCXSTRCAT(drpool, pbuf, psiz);
3823 }
3824 }
3825
3826
3827 /* Store a record in asynchronus fashion.
3828 `hdb' specifies the hash database object.
3829 `kbuf' specifies the pointer to the region of the key.
3830 `ksiz' specifies the size of the region of the key.
3831 `bidx' specifies the index of the bucket array.
3832 `hash' specifies the hash value for the collision tree.
3833 `vbuf' specifies the pointer to the region of the value.
3834 `vsiz' specifies the size of the region of the value.
3835 If successful, the return value is true, else, it is false. */
tchdbputasyncimpl(TCHDB * hdb,const char * kbuf,int ksiz,uint64_t bidx,uint8_t hash,const char * vbuf,int vsiz)3836 static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx,
3837 uint8_t hash, const char *vbuf, int vsiz){
3838 assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3839 if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
3840 if(!hdb->drpool){
3841 hdb->drpool = tcxstrnew3(HDBDRPUNIT + HDBDRPLAT);
3842 hdb->drpdef = tcxstrnew3(HDBDRPUNIT);
3843 hdb->drpoff = hdb->fsiz;
3844 }
3845 off_t off = tchdbgetbucket(hdb, bidx);
3846 off_t entoff = 0;
3847 TCHREC rec;
3848 char rbuf[HDBIOBUFSIZ];
3849 while(off > 0){
3850 if(off >= hdb->drpoff - hdb->runit){
3851 TCDODEBUG(hdb->cnt_deferdrp++);
3852 TCXSTR *drpdef = hdb->drpdef;
3853 TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz));
3854 TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz));
3855 TCXSTRCAT(drpdef, kbuf, ksiz);
3856 TCXSTRCAT(drpdef, vbuf, vsiz);
3857 if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
3858 return true;
3859 }
3860 rec.off = off;
3861 if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
3862 if(hash > rec.hash){
3863 off = rec.left;
3864 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
3865 } else if(hash < rec.hash){
3866 off = rec.right;
3867 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
3868 (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
3869 } else {
3870 TCDODEBUG(hdb->cnt_deferdrp++);
3871 TCXSTR *drpdef = hdb->drpdef;
3872 TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz));
3873 TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz));
3874 TCXSTRCAT(drpdef, kbuf, ksiz);
3875 TCXSTRCAT(drpdef, vbuf, vsiz);
3876 if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
3877 return true;
3878 }
3879 }
3880 if(entoff > 0){
3881 if(hdb->ba64){
3882 uint64_t llnum = hdb->fsiz >> hdb->apow;
3883 llnum = TCHTOILL(llnum);
3884 if(!tchdbseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
3885 } else {
3886 uint32_t lnum = hdb->fsiz >> hdb->apow;
3887 lnum = TCHTOIL(lnum);
3888 if(!tchdbseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
3889 }
3890 } else {
3891 tchdbsetbucket(hdb, bidx, hdb->fsiz);
3892 }
3893 tchdbdrpappend(hdb, kbuf, ksiz, vbuf, vsiz, hash);
3894 hdb->rnum++;
3895 if(TCXSTRSIZE(hdb->drpool) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
3896 return true;
3897 }
3898
3899
3900 /* Remove a record of a hash database object.
3901 `hdb' specifies the hash database object.
3902 `kbuf' specifies the pointer to the region of the key.
3903 `ksiz' specifies the size of the region of the key.
3904 `bidx' specifies the index of the bucket array.
3905 `hash' specifies the hash value for the collision tree.
3906 If successful, the return value is true, else, it is false. */
tchdboutimpl(TCHDB * hdb,const char * kbuf,int ksiz,uint64_t bidx,uint8_t hash)3907 static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash){
3908 assert(hdb && kbuf && ksiz >= 0);
3909 if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
3910 off_t off = tchdbgetbucket(hdb, bidx);
3911 off_t entoff = 0;
3912 TCHREC rec;
3913 char rbuf[HDBIOBUFSIZ];
3914 while(off > 0){
3915 rec.off = off;
3916 if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
3917 if(hash > rec.hash){
3918 off = rec.left;
3919 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
3920 } else if(hash < rec.hash){
3921 off = rec.right;
3922 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
3923 (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
3924 } else {
3925 if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false;
3926 int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
3927 if(kcmp > 0){
3928 off = rec.left;
3929 TCFREE(rec.bbuf);
3930 rec.kbuf = NULL;
3931 rec.bbuf = NULL;
3932 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
3933 } else if(kcmp < 0){
3934 off = rec.right;
3935 TCFREE(rec.bbuf);
3936 rec.kbuf = NULL;
3937 rec.bbuf = NULL;
3938 entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
3939 (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
3940 } else {
3941 TCFREE(rec.bbuf);
3942 rec.bbuf = NULL;
3943 return tchdbremoverec(hdb, &rec, rbuf, bidx, entoff);
3944 }
3945 }
3946 }
3947 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
3948 return false;
3949 }
3950
3951
3952 /* Retrieve a record in a hash database object.
3953 `hdb' specifies the hash database object.
3954 `kbuf' specifies the pointer to the region of the key.
3955 `ksiz' specifies the size of the region of the key.
3956 `bidx' specifies the index of the bucket array.
3957 `hash' specifies the hash value for the collision tree.
3958 `sp' specifies the pointer to the variable into which the size of the region of the return
3959 value is assigned.
3960 If successful, the return value is the pointer to the region of the value of the corresponding
3961 record. */
tchdbgetimpl(TCHDB * hdb,const char * kbuf,int ksiz,uint64_t bidx,uint8_t hash,int * sp)3962 static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash,
3963 int *sp){
3964 assert(hdb && kbuf && ksiz >= 0 && sp);
3965 if(hdb->recc){
3966 int tvsiz;
3967 char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
3968 if(tvbuf){
3969 if(*tvbuf == '*'){
3970 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
3971 TCFREE(tvbuf);
3972 return NULL;
3973 }
3974 *sp = tvsiz - 1;
3975 memmove(tvbuf, tvbuf + 1, tvsiz);
3976 return tvbuf;
3977 }
3978 }
3979 off_t off = tchdbgetbucket(hdb, bidx);
3980 TCHREC rec;
3981 char rbuf[HDBIOBUFSIZ];
3982 while(off > 0){
3983 rec.off = off;
3984 if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
3985 if(hash > rec.hash){
3986 off = rec.left;
3987 } else if(hash < rec.hash){
3988 off = rec.right;
3989 } else {
3990 if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return NULL;
3991 int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
3992 if(kcmp > 0){
3993 off = rec.left;
3994 TCFREE(rec.bbuf);
3995 rec.kbuf = NULL;
3996 rec.bbuf = NULL;
3997 } else if(kcmp < 0){
3998 off = rec.right;
3999 TCFREE(rec.bbuf);
4000 rec.kbuf = NULL;
4001 rec.bbuf = NULL;
4002 } else {
4003 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return NULL;
4004 if(hdb->zmode){
4005 int zsiz;
4006 char *zbuf;
4007 if(hdb->opts & HDBTDEFLATE){
4008 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4009 } else if(hdb->opts & HDBTBZIP){
4010 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4011 } else if(hdb->opts & HDBTTCBS){
4012 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4013 } else {
4014 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4015 }
4016 TCFREE(rec.bbuf);
4017 if(!zbuf){
4018 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4019 return NULL;
4020 }
4021 if(hdb->recc){
4022 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4023 tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
4024 }
4025 *sp = zsiz;
4026 return zbuf;
4027 }
4028 if(hdb->recc){
4029 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4030 tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
4031 }
4032 if(rec.bbuf){
4033 memmove(rec.bbuf, rec.vbuf, rec.vsiz);
4034 rec.bbuf[rec.vsiz] = '\0';
4035 *sp = rec.vsiz;
4036 return rec.bbuf;
4037 }
4038 *sp = rec.vsiz;
4039 char *rv;
4040 TCMEMDUP(rv, rec.vbuf, rec.vsiz);
4041 return rv;
4042 }
4043 }
4044 }
4045 if(hdb->recc){
4046 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4047 tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
4048 }
4049 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4050 return NULL;
4051 }
4052
4053
4054 /* Retrieve a record in a hash database object and write the value into a buffer.
4055 `hdb' specifies the hash database object.
4056 `kbuf' specifies the pointer to the region of the key.
4057 `ksiz' specifies the size of the region of the key.
4058 `bidx' specifies the index of the bucket array.
4059 `hash' specifies the hash value for the collision tree.
4060 `vbuf' specifies the pointer to the buffer into which the value of the corresponding record is
4061 written.
4062 `max' specifies the size of the buffer.
4063 If successful, the return value is the size of the written data, else, it is -1. */
tchdbgetintobuf(TCHDB * hdb,const char * kbuf,int ksiz,uint64_t bidx,uint8_t hash,char * vbuf,int max)4064 static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash,
4065 char *vbuf, int max){
4066 assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0);
4067 if(hdb->recc){
4068 int tvsiz;
4069 char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
4070 if(tvbuf){
4071 if(*tvbuf == '*'){
4072 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4073 TCFREE(tvbuf);
4074 return -1;
4075 }
4076 tvsiz = tclmin(tvsiz - 1, max);
4077 memcpy(vbuf, tvbuf + 1, tvsiz);
4078 TCFREE(tvbuf);
4079 return tvsiz;
4080 }
4081 }
4082 off_t off = tchdbgetbucket(hdb, bidx);
4083 TCHREC rec;
4084 char rbuf[HDBIOBUFSIZ];
4085 while(off > 0){
4086 rec.off = off;
4087 if(!tchdbreadrec(hdb, &rec, rbuf)) return -1;
4088 if(hash > rec.hash){
4089 off = rec.left;
4090 } else if(hash < rec.hash){
4091 off = rec.right;
4092 } else {
4093 if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
4094 int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
4095 if(kcmp > 0){
4096 off = rec.left;
4097 TCFREE(rec.bbuf);
4098 rec.kbuf = NULL;
4099 rec.bbuf = NULL;
4100 } else if(kcmp < 0){
4101 off = rec.right;
4102 TCFREE(rec.bbuf);
4103 rec.kbuf = NULL;
4104 rec.bbuf = NULL;
4105 } else {
4106 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
4107 if(hdb->zmode){
4108 int zsiz;
4109 char *zbuf;
4110 if(hdb->opts & HDBTDEFLATE){
4111 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4112 } else if(hdb->opts & HDBTBZIP){
4113 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4114 } else if(hdb->opts & HDBTTCBS){
4115 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4116 } else {
4117 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4118 }
4119 TCFREE(rec.bbuf);
4120 if(!zbuf){
4121 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4122 return -1;
4123 }
4124 if(hdb->recc){
4125 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4126 tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
4127 }
4128 zsiz = tclmin(zsiz, max);
4129 memcpy(vbuf, zbuf, zsiz);
4130 TCFREE(zbuf);
4131 return zsiz;
4132 }
4133 if(hdb->recc){
4134 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4135 tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
4136 }
4137 int vsiz = tclmin(rec.vsiz, max);
4138 memcpy(vbuf, rec.vbuf, vsiz);
4139 TCFREE(rec.bbuf);
4140 return vsiz;
4141 }
4142 }
4143 }
4144 if(hdb->recc){
4145 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4146 tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
4147 }
4148 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4149 return -1;
4150 }
4151
4152
4153 /* Retrieve the next record of a record in a hash database object.
4154 `hdb' specifies the hash database object.
4155 `kbuf' specifies the pointer to the region of the key.
4156 `ksiz' specifies the size of the region of the key.
4157 `sp' specifies the pointer to the variable into which the size of the region of the return
4158 value is assigned.
4159 `vbp' specifies the pointer to the variable into which the pointer to the value is assigned.
4160 `vsp' specifies the pointer to the variable into which the size of the value is assigned.
4161 If successful, the return value is the pointer to the region of the value of the next
4162 record. */
tchdbgetnextimpl(TCHDB * hdb,const char * kbuf,int ksiz,int * sp,const char ** vbp,int * vsp)4163 static char *tchdbgetnextimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp,
4164 const char **vbp, int *vsp){
4165 assert(hdb && sp);
4166 if(!kbuf){
4167 uint64_t iter = hdb->frec;
4168 TCHREC rec;
4169 char rbuf[HDBIOBUFSIZ];
4170 while(iter < hdb->fsiz){
4171 rec.off = iter;
4172 if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
4173 iter += rec.rsiz;
4174 if(rec.magic == HDBMAGICREC){
4175 if(vbp){
4176 if(hdb->zmode){
4177 if(!tchdbreadrecbody(hdb, &rec)) return NULL;
4178 int zsiz;
4179 char *zbuf;
4180 if(hdb->opts & HDBTDEFLATE){
4181 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4182 } else if(hdb->opts & HDBTBZIP){
4183 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4184 } else if(hdb->opts & HDBTTCBS){
4185 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4186 } else {
4187 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4188 }
4189 if(!zbuf){
4190 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4191 TCFREE(rec.bbuf);
4192 return NULL;
4193 }
4194 char *rv;
4195 TCMALLOC(rv, rec.ksiz + zsiz + 1);
4196 memcpy(rv, rec.kbuf, rec.ksiz);
4197 memcpy(rv + rec.ksiz, zbuf, zsiz);
4198 *sp = rec.ksiz;
4199 *vbp = rv + rec.ksiz;
4200 *vsp = zsiz;
4201 TCFREE(zbuf);
4202 TCFREE(rec.bbuf);
4203 return rv;
4204 }
4205 if(rec.vbuf){
4206 char *rv;
4207 TCMALLOC(rv, rec.ksiz + rec.vsiz + 1);
4208 memcpy(rv, rec.kbuf, rec.ksiz);
4209 memcpy(rv + rec.ksiz, rec.vbuf, rec.vsiz);
4210 *sp = rec.ksiz;
4211 *vbp = rv + rec.ksiz;
4212 *vsp = rec.vsiz;
4213 return rv;
4214 }
4215 if(!tchdbreadrecbody(hdb, &rec)) return NULL;
4216 *sp = rec.ksiz;
4217 *vbp = rec.vbuf;
4218 *vsp = rec.vsiz;
4219 return rec.bbuf;
4220 }
4221 if(rec.kbuf){
4222 *sp = rec.ksiz;
4223 char *rv;
4224 TCMEMDUP(rv, rec.kbuf, rec.ksiz);
4225 return rv;
4226 }
4227 if(!tchdbreadrecbody(hdb, &rec)) return NULL;
4228 rec.bbuf[rec.ksiz] = '\0';
4229 *sp = rec.ksiz;
4230 return rec.bbuf;
4231 }
4232 }
4233 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4234 return NULL;
4235 }
4236 uint8_t hash;
4237 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
4238 off_t off = tchdbgetbucket(hdb, bidx);
4239 TCHREC rec;
4240 char rbuf[HDBIOBUFSIZ];
4241 while(off > 0){
4242 rec.off = off;
4243 if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
4244 if(hash > rec.hash){
4245 off = rec.left;
4246 } else if(hash < rec.hash){
4247 off = rec.right;
4248 } else {
4249 if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return NULL;
4250 int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
4251 if(kcmp > 0){
4252 off = rec.left;
4253 TCFREE(rec.bbuf);
4254 rec.kbuf = NULL;
4255 rec.bbuf = NULL;
4256 } else if(kcmp < 0){
4257 off = rec.right;
4258 TCFREE(rec.bbuf);
4259 rec.kbuf = NULL;
4260 rec.bbuf = NULL;
4261 } else {
4262 uint64_t iter = rec.off + rec.rsiz;
4263 TCFREE(rec.bbuf);
4264 rec.kbuf = NULL;
4265 rec.bbuf = NULL;
4266 while(iter < hdb->fsiz){
4267 rec.off = iter;
4268 if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
4269 iter += rec.rsiz;
4270 if(rec.magic == HDBMAGICREC){
4271 if(vbp){
4272 if(hdb->zmode){
4273 if(!tchdbreadrecbody(hdb, &rec)) return NULL;
4274 int zsiz;
4275 char *zbuf;
4276 if(hdb->opts & HDBTDEFLATE){
4277 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4278 } else if(hdb->opts & HDBTBZIP){
4279 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4280 } else if(hdb->opts & HDBTTCBS){
4281 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4282 } else {
4283 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4284 }
4285 if(!zbuf){
4286 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4287 TCFREE(rec.bbuf);
4288 return NULL;
4289 }
4290 char *rv;
4291 TCMALLOC(rv, rec.ksiz + zsiz + 1);
4292 memcpy(rv, rec.kbuf, rec.ksiz);
4293 memcpy(rv + rec.ksiz, zbuf, zsiz);
4294 *sp = rec.ksiz;
4295 *vbp = rv + rec.ksiz;
4296 *vsp = zsiz;
4297 TCFREE(zbuf);
4298 TCFREE(rec.bbuf);
4299 return rv;
4300 }
4301 if(rec.vbuf){
4302 char *rv;
4303 TCMALLOC(rv, rec.ksiz + rec.vsiz + 1);
4304 memcpy(rv, rec.kbuf, rec.ksiz);
4305 memcpy(rv + rec.ksiz, rec.vbuf, rec.vsiz);
4306 *sp = rec.ksiz;
4307 *vbp = rv + rec.ksiz;
4308 *vsp = rec.vsiz;
4309 return rv;
4310 }
4311 if(!tchdbreadrecbody(hdb, &rec)) return NULL;
4312 *sp = rec.ksiz;
4313 *vbp = rec.vbuf;
4314 *vsp = rec.vsiz;
4315 return rec.bbuf;
4316 }
4317 if(rec.kbuf){
4318 *sp = rec.ksiz;
4319 char *rv;
4320 TCMEMDUP(rv, rec.kbuf, rec.ksiz);
4321 return rv;
4322 }
4323 if(!tchdbreadrecbody(hdb, &rec)) return NULL;
4324 rec.bbuf[rec.ksiz] = '\0';
4325 *sp = rec.ksiz;
4326 return rec.bbuf;
4327 }
4328 }
4329 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4330 return NULL;
4331 }
4332 }
4333 }
4334 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4335 return NULL;
4336 }
4337
4338
4339 /* Get the size of the value of a record in a hash database object.
4340 `hdb' specifies the hash database object.
4341 `kbuf' specifies the pointer to the region of the key.
4342 `ksiz' specifies the size of the region of the key.
4343 `bidx' specifies the index of the bucket array.
4344 `hash' specifies the hash value for the collision tree.
4345 If successful, the return value is the size of the value of the corresponding record, else,
4346 it is -1. */
tchdbvsizimpl(TCHDB * hdb,const char * kbuf,int ksiz,uint64_t bidx,uint8_t hash)4347 static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash){
4348 assert(hdb && kbuf && ksiz >= 0);
4349 if(hdb->recc){
4350 int tvsiz;
4351 char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
4352 if(tvbuf){
4353 if(*tvbuf == '*'){
4354 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4355 TCFREE(tvbuf);
4356 return -1;
4357 }
4358 TCFREE(tvbuf);
4359 return tvsiz - 1;
4360 }
4361 }
4362 off_t off = tchdbgetbucket(hdb, bidx);
4363 TCHREC rec;
4364 char rbuf[HDBIOBUFSIZ];
4365 while(off > 0){
4366 rec.off = off;
4367 if(!tchdbreadrec(hdb, &rec, rbuf)) return -1;
4368 if(hash > rec.hash){
4369 off = rec.left;
4370 } else if(hash < rec.hash){
4371 off = rec.right;
4372 } else {
4373 if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
4374 int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
4375 if(kcmp > 0){
4376 off = rec.left;
4377 TCFREE(rec.bbuf);
4378 rec.kbuf = NULL;
4379 rec.bbuf = NULL;
4380 } else if(kcmp < 0){
4381 off = rec.right;
4382 TCFREE(rec.bbuf);
4383 rec.kbuf = NULL;
4384 rec.bbuf = NULL;
4385 } else {
4386 if(hdb->zmode){
4387 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
4388 int zsiz;
4389 char *zbuf;
4390 if(hdb->opts & HDBTDEFLATE){
4391 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4392 } else if(hdb->opts & HDBTBZIP){
4393 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4394 } else if(hdb->opts & HDBTTCBS){
4395 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4396 } else {
4397 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4398 }
4399 TCFREE(rec.bbuf);
4400 if(!zbuf){
4401 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4402 return -1;
4403 }
4404 if(hdb->recc){
4405 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4406 tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
4407 }
4408 TCFREE(zbuf);
4409 return zsiz;
4410 }
4411 if(hdb->recc && rec.vbuf){
4412 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4413 tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
4414 }
4415 TCFREE(rec.bbuf);
4416 return rec.vsiz;
4417 }
4418 }
4419 }
4420 if(hdb->recc){
4421 if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
4422 tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
4423 }
4424 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4425 return -1;
4426 }
4427
4428
4429 /* Initialize the iterator of a hash database object.
4430 `hdb' specifies the hash database object.
4431 If successful, the return value is true, else, it is false. */
tchdbiterinitimpl(TCHDB * hdb)4432 static bool tchdbiterinitimpl(TCHDB *hdb){
4433 assert(hdb);
4434 hdb->iter = hdb->frec;
4435 return true;
4436 }
4437
4438
4439 /* Get the next key of the iterator of a hash database object.
4440 `hdb' specifies the hash database object.
4441 `sp' specifies the pointer to the variable into which the size of the region of the return
4442 value is assigned.
4443 If successful, the return value is the pointer to the region of the next key, else, it is
4444 `NULL'. */
tchdbiternextimpl(TCHDB * hdb,int * sp)4445 static char *tchdbiternextimpl(TCHDB *hdb, int *sp){
4446 assert(hdb && sp);
4447 TCHREC rec;
4448 char rbuf[HDBIOBUFSIZ];
4449 while(hdb->iter < hdb->fsiz){
4450 rec.off = hdb->iter;
4451 if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
4452 hdb->iter += rec.rsiz;
4453 if(rec.magic == HDBMAGICREC){
4454 if(rec.kbuf){
4455 *sp = rec.ksiz;
4456 char *rv;
4457 TCMEMDUP(rv, rec.kbuf, rec.ksiz);
4458 return rv;
4459 }
4460 if(!tchdbreadrecbody(hdb, &rec)) return NULL;
4461 rec.bbuf[rec.ksiz] = '\0';
4462 *sp = rec.ksiz;
4463 return rec.bbuf;
4464 }
4465 }
4466 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4467 return NULL;
4468 }
4469
4470
4471 /* Get the next extensible objects of the iterator of a hash database object. */
tchdbiternextintoxstr(TCHDB * hdb,TCXSTR * kxstr,TCXSTR * vxstr)4472 static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){
4473 assert(hdb && kxstr && vxstr);
4474 TCHREC rec;
4475 char rbuf[HDBIOBUFSIZ];
4476 while(hdb->iter < hdb->fsiz){
4477 rec.off = hdb->iter;
4478 if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
4479 hdb->iter += rec.rsiz;
4480 if(rec.magic == HDBMAGICREC){
4481 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return false;
4482 tcxstrclear(kxstr);
4483 TCXSTRCAT(kxstr, rec.kbuf, rec.ksiz);
4484 tcxstrclear(vxstr);
4485 if(hdb->zmode){
4486 int zsiz;
4487 char *zbuf;
4488 if(hdb->opts & HDBTDEFLATE){
4489 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4490 } else if(hdb->opts & HDBTBZIP){
4491 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4492 } else if(hdb->opts & HDBTTCBS){
4493 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4494 } else {
4495 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4496 }
4497 if(!zbuf){
4498 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4499 TCFREE(rec.bbuf);
4500 return false;
4501 }
4502 TCXSTRCAT(vxstr, zbuf, zsiz);
4503 TCFREE(zbuf);
4504 } else {
4505 TCXSTRCAT(vxstr, rec.vbuf, rec.vsiz);
4506 }
4507 TCFREE(rec.bbuf);
4508 return true;
4509 }
4510 }
4511 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4512 return false;
4513 }
4514
4515
4516 /* Optimize the file of a hash database object.
4517 `hdb' specifies the hash database object.
4518 `bnum' specifies the number of elements of the bucket array.
4519 `apow' specifies the size of record alignment by power of 2.
4520 `fpow' specifies the maximum number of elements of the free block pool by power of 2.
4521 `opts' specifies options by bitwise-or.
4522 If successful, the return value is true, else, it is false. */
tchdboptimizeimpl(TCHDB * hdb,int64_t bnum,int8_t apow,int8_t fpow,uint8_t opts)4523 static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
4524 assert(hdb);
4525 char *tpath = tcsprintf("%s%ctmp%c%llu", hdb->path, MYEXTCHR, MYEXTCHR, hdb->inode);
4526 TCHDB *thdb = tchdbnew();
4527 thdb->dbgfd = hdb->dbgfd;
4528 thdb->enc = hdb->enc;
4529 thdb->encop = hdb->encop;
4530 thdb->dec = hdb->dec;
4531 thdb->decop = hdb->decop;
4532 if(bnum < 1){
4533 bnum = hdb->rnum * 2 + 1;
4534 if(bnum < HDBDEFBNUM) bnum = HDBDEFBNUM;
4535 }
4536 if(apow < 0) apow = hdb->apow;
4537 if(fpow < 0) fpow = hdb->fpow;
4538 if(opts == UINT8_MAX) opts = hdb->opts;
4539 tchdbtune(thdb, bnum, apow, fpow, opts);
4540 if(!tchdbopen(thdb, tpath, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){
4541 tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
4542 tchdbdel(thdb);
4543 TCFREE(tpath);
4544 return false;
4545 }
4546 memcpy(tchdbopaque(thdb), tchdbopaque(hdb), HDBHEADSIZ - HDBOPAQUEOFF);
4547 bool err = false;
4548 uint64_t off = hdb->frec;
4549 TCHREC rec;
4550 char rbuf[HDBIOBUFSIZ];
4551 while(off < hdb->fsiz){
4552 rec.off = off;
4553 if(!tchdbreadrec(hdb, &rec, rbuf)){
4554 err = true;
4555 break;
4556 }
4557 off += rec.rsiz;
4558 if(rec.magic == HDBMAGICREC){
4559 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){
4560 TCFREE(rec.bbuf);
4561 err = true;
4562 } else {
4563 if(hdb->zmode){
4564 int zsiz;
4565 char *zbuf;
4566 if(hdb->opts & HDBTDEFLATE){
4567 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4568 } else if(hdb->opts & HDBTBZIP){
4569 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4570 } else if(hdb->opts & HDBTTCBS){
4571 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4572 } else {
4573 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4574 }
4575 if(zbuf){
4576 if(!tchdbput(thdb, rec.kbuf, rec.ksiz, zbuf, zsiz)){
4577 tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
4578 err = true;
4579 }
4580 TCFREE(zbuf);
4581 } else {
4582 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4583 err = true;
4584 }
4585 } else {
4586 if(!tchdbput(thdb, rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz)){
4587 tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
4588 err = true;
4589 }
4590 }
4591 }
4592 TCFREE(rec.bbuf);
4593 }
4594 }
4595 if(!tchdbclose(thdb)){
4596 tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
4597 err = true;
4598 }
4599 bool esc = false;
4600 if(err && (hdb->omode & HDBONOLCK) && !thdb->fatal){
4601 err = false;
4602 esc = true;
4603 }
4604 tchdbdel(thdb);
4605 if(err){
4606 TCFREE(tpath);
4607 return false;
4608 }
4609 if(esc){
4610 char *bpath = tcsprintf("%s%cbroken", tpath, MYEXTCHR);
4611 if(rename(hdb->path, bpath) == -1){
4612 tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__);
4613 err = true;
4614 }
4615 TCFREE(bpath);
4616 } else {
4617 if(unlink(hdb->path) == -1){
4618 tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__);
4619 err = true;
4620 }
4621 }
4622 if(rename(tpath, hdb->path) == -1){
4623 tchdbsetecode(hdb, TCERENAME, __FILE__, __LINE__, __func__);
4624 err = true;
4625 }
4626 TCFREE(tpath);
4627 if(err) return false;
4628 tpath = tcstrdup(hdb->path);
4629 int omode = (hdb->omode & ~HDBOCREAT) & ~HDBOTRUNC;
4630 if(!tchdbcloseimpl(hdb)){
4631 TCFREE(tpath);
4632 return false;
4633 }
4634 bool rv = tchdbopenimpl(hdb, tpath, omode);
4635 TCFREE(tpath);
4636 return rv;
4637 }
4638
4639
4640 /* Remove all records of a hash database object.
4641 `hdb' specifies the hash database object.
4642 If successful, the return value is true, else, it is false. */
tchdbvanishimpl(TCHDB * hdb)4643 static bool tchdbvanishimpl(TCHDB *hdb){
4644 assert(hdb);
4645 char *path = tcstrdup(hdb->path);
4646 int omode = hdb->omode;
4647 bool err = false;
4648 if(!tchdbcloseimpl(hdb)) err = true;
4649 if(!tchdbopenimpl(hdb, path, HDBOTRUNC | omode)){
4650 tcpathunlock(hdb->rpath);
4651 TCFREE(hdb->rpath);
4652 err = true;
4653 }
4654 TCFREE(path);
4655 return !err;
4656 }
4657
4658
4659 /* Copy the database file of a hash database object.
4660 `hdb' specifies the hash database object.
4661 `path' specifies the path of the destination file.
4662 If successful, the return value is true, else, it is false. */
tchdbcopyimpl(TCHDB * hdb,const char * path)4663 static bool tchdbcopyimpl(TCHDB *hdb, const char *path){
4664 assert(hdb && path);
4665 bool err = false;
4666 if(hdb->omode & HDBOWRITER){
4667 if(!tchdbsavefbp(hdb)) err = true;
4668 if(!tchdbmemsync(hdb, false)) err = true;
4669 tchdbsetflag(hdb, HDBFOPEN, false);
4670 }
4671 if(*path == '@'){
4672 char tsbuf[TCNUMBUFSIZ];
4673 sprintf(tsbuf, "%llu", (unsigned long long)(tctime() * 1000000));
4674 const char *args[3];
4675 args[0] = path + 1;
4676 args[1] = hdb->path;
4677 args[2] = tsbuf;
4678 if(tcsystem(args, sizeof(args) / sizeof(*args)) != 0) err = true;
4679 } else {
4680 if(!tccopyfile(hdb->path, path)){
4681 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4682 err = true;
4683 }
4684 }
4685 if(hdb->omode & HDBOWRITER) tchdbsetflag(hdb, HDBFOPEN, true);
4686 return !err;
4687 }
4688
4689
4690 /* Perform dynamic defragmentation of a hash database object.
4691 `hdb' specifies the hash database object connected.
4692 `step' specifie the number of steps.
4693 If successful, the return value is true, else, it is false. */
tchdbdefragimpl(TCHDB * hdb,int64_t step)4694 static bool tchdbdefragimpl(TCHDB *hdb, int64_t step){
4695 assert(hdb && step >= 0);
4696 TCDODEBUG(hdb->cnt_defrag++);
4697 hdb->dfcnt = 0;
4698 TCHREC rec;
4699 char rbuf[HDBIOBUFSIZ];
4700 while(true){
4701 if(hdb->dfcur >= hdb->fsiz){
4702 hdb->dfcur = hdb->frec;
4703 return true;
4704 }
4705 if(step-- < 1) return true;
4706 rec.off = hdb->dfcur;
4707 if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
4708 if(rec.magic == HDBMAGICFB) break;
4709 hdb->dfcur += rec.rsiz;
4710 }
4711 uint32_t align = hdb->align;
4712 uint64_t base = hdb->dfcur;
4713 uint64_t dest = base;
4714 uint64_t cur = base;
4715 if(hdb->iter == cur) hdb->iter += rec.rsiz;
4716 cur += rec.rsiz;
4717 uint64_t fbsiz = cur - dest;
4718 step++;
4719 while(step > 0 && cur < hdb->fsiz){
4720 rec.off = cur;
4721 if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
4722 uint32_t rsiz = rec.rsiz;
4723 if(rec.magic == HDBMAGICREC){
4724 if(rec.psiz >= align){
4725 int diff = rec.psiz - rec.psiz % align;
4726 rec.psiz -= diff;
4727 rec.rsiz -= diff;
4728 fbsiz += diff;
4729 }
4730 if(!tchdbshiftrec(hdb, &rec, rbuf, dest)) return false;
4731 if(hdb->iter == cur) hdb->iter = dest;
4732 dest += rec.rsiz;
4733 step--;
4734 } else {
4735 if(hdb->iter == cur) hdb->iter += rec.rsiz;
4736 fbsiz += rec.rsiz;
4737 }
4738 cur += rsiz;
4739 }
4740 if(cur < hdb->fsiz){
4741 if(fbsiz > HDBFBMAXSIZ){
4742 tchdbfbptrim(hdb, base, cur, 0, 0);
4743 uint64_t off = dest;
4744 uint64_t size = fbsiz;
4745 while(size > 0){
4746 uint32_t rsiz = (size > HDBFBMAXSIZ) ? HDBFBMAXSIZ : size;
4747 if(size - rsiz < HDBMINRUNIT) rsiz = size;
4748 tchdbfbpinsert(hdb, off, rsiz);
4749 if(!tchdbwritefb(hdb, off, rsiz)) return false;
4750 off += rsiz;
4751 size -= rsiz;
4752 }
4753 } else {
4754 tchdbfbptrim(hdb, base, cur, dest, fbsiz);
4755 if(!tchdbwritefb(hdb, dest, fbsiz)) return false;
4756 }
4757 hdb->dfcur = cur - fbsiz;
4758 } else {
4759 TCDODEBUG(hdb->cnt_trunc++);
4760 if(hdb->tran && !tchdbwalwrite(hdb, dest, fbsiz)) return false;
4761 tchdbfbptrim(hdb, base, cur, 0, 0);
4762 hdb->dfcur = hdb->frec;
4763 hdb->fsiz = dest;
4764 uint64_t llnum = hdb->fsiz;
4765 llnum = TCHTOILL(llnum);
4766 memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum));
4767 if(hdb->iter >= hdb->fsiz) hdb->iter = UINT64_MAX;
4768 if(!hdb->tran){
4769 if(ftruncate(hdb->fd, hdb->fsiz) == -1){
4770 tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
4771 return false;
4772 }
4773 hdb->xfsiz = 0;
4774 }
4775 }
4776 return true;
4777 }
4778
4779
4780 /* Move the iterator to the record corresponding a key of a hash database object.
4781 `hdb' specifies the hash database object.
4782 `kbuf' specifies the pointer to the region of the key.
4783 `ksiz' specifies the size of the region of the key.
4784 If successful, the return value is true, else, it is false. */
tchdbiterjumpimpl(TCHDB * hdb,const char * kbuf,int ksiz)4785 static bool tchdbiterjumpimpl(TCHDB *hdb, const char *kbuf, int ksiz){
4786 assert(hdb && kbuf && ksiz);
4787 uint8_t hash;
4788 uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
4789 off_t off = tchdbgetbucket(hdb, bidx);
4790 TCHREC rec;
4791 char rbuf[HDBIOBUFSIZ];
4792 while(off > 0){
4793 rec.off = off;
4794 if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
4795 if(hash > rec.hash){
4796 off = rec.left;
4797 } else if(hash < rec.hash){
4798 off = rec.right;
4799 } else {
4800 if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false;
4801 int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
4802 if(kcmp > 0){
4803 off = rec.left;
4804 TCFREE(rec.bbuf);
4805 rec.kbuf = NULL;
4806 rec.bbuf = NULL;
4807 } else if(kcmp < 0){
4808 off = rec.right;
4809 TCFREE(rec.bbuf);
4810 rec.kbuf = NULL;
4811 rec.bbuf = NULL;
4812 } else {
4813 hdb->iter = off;
4814 return true;
4815 }
4816 }
4817 }
4818 tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
4819 return false;
4820 }
4821
4822
4823 /* Process each record atomically of a hash database object.
4824 `hdb' specifies the hash database object.
4825 `func' specifies the pointer to the iterator function called for each record.
4826 `op' specifies an arbitrary pointer to be given as a parameter of the iterator function.
4827 If successful, the return value is true, else, it is false. */
tchdbforeachimpl(TCHDB * hdb,TCITER iter,void * op)4828 static bool tchdbforeachimpl(TCHDB *hdb, TCITER iter, void *op){
4829 assert(hdb && iter);
4830 bool err = false;
4831 uint64_t off = hdb->frec;
4832 TCHREC rec;
4833 char rbuf[HDBIOBUFSIZ];
4834 bool cont = true;
4835 while(cont && off < hdb->fsiz){
4836 rec.off = off;
4837 if(!tchdbreadrec(hdb, &rec, rbuf)){
4838 err = true;
4839 break;
4840 }
4841 off += rec.rsiz;
4842 if(rec.magic == HDBMAGICREC){
4843 if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){
4844 TCFREE(rec.bbuf);
4845 err = true;
4846 } else {
4847 if(hdb->zmode){
4848 int zsiz;
4849 char *zbuf;
4850 if(hdb->opts & HDBTDEFLATE){
4851 zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
4852 } else if(hdb->opts & HDBTBZIP){
4853 zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz);
4854 } else if(hdb->opts & HDBTTCBS){
4855 zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
4856 } else {
4857 zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop);
4858 }
4859 if(zbuf){
4860 cont = iter(rec.kbuf, rec.ksiz, zbuf, zsiz, op);
4861 TCFREE(zbuf);
4862 } else {
4863 tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
4864 err = true;
4865 }
4866 } else {
4867 cont = iter(rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz, op);
4868 }
4869 }
4870 TCFREE(rec.bbuf);
4871 }
4872 }
4873 return !err;
4874 }
4875
4876
4877 /* Lock a method of the hash database object.
4878 `hdb' specifies the hash database object.
4879 `wr' specifies whether the lock is writer or not.
4880 If successful, the return value is true, else, it is false. */
tchdblockmethod(TCHDB * hdb,bool wr)4881 static bool tchdblockmethod(TCHDB *hdb, bool wr){
4882 assert(hdb);
4883 if(wr ? pthread_rwlock_wrlock(hdb->mmtx) != 0 : pthread_rwlock_rdlock(hdb->mmtx) != 0){
4884 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4885 return false;
4886 }
4887 TCTESTYIELD();
4888 return true;
4889 }
4890
4891
4892 /* Unlock a method of the hash database object.
4893 `hdb' specifies the hash database object.
4894 If successful, the return value is true, else, it is false. */
tchdbunlockmethod(TCHDB * hdb)4895 static bool tchdbunlockmethod(TCHDB *hdb){
4896 assert(hdb);
4897 if(pthread_rwlock_unlock(hdb->mmtx) != 0){
4898 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4899 return false;
4900 }
4901 TCTESTYIELD();
4902 return true;
4903 }
4904
4905
4906 /* Lock a record of the hash database object.
4907 `hdb' specifies the hash database object.
4908 `bidx' specifies the bucket index of the record.
4909 `wr' specifies whether the lock is writer or not.
4910 If successful, the return value is true, else, it is false. */
tchdblockrecord(TCHDB * hdb,uint8_t bidx,bool wr)4911 static bool tchdblockrecord(TCHDB *hdb, uint8_t bidx, bool wr){
4912 assert(hdb);
4913 if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)hdb->rmtxs + bidx) != 0 :
4914 pthread_rwlock_rdlock((pthread_rwlock_t *)hdb->rmtxs + bidx) != 0){
4915 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4916 return false;
4917 }
4918 TCTESTYIELD();
4919 return true;
4920 }
4921
4922
4923 /* Unlock a record of the hash database object.
4924 `hdb' specifies the hash database object.
4925 `bidx' specifies the bucket index of the record.
4926 If successful, the return value is true, else, it is false. */
tchdbunlockrecord(TCHDB * hdb,uint8_t bidx)4927 static bool tchdbunlockrecord(TCHDB *hdb, uint8_t bidx){
4928 assert(hdb);
4929 if(pthread_rwlock_unlock((pthread_rwlock_t *)hdb->rmtxs + bidx) != 0){
4930 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4931 return false;
4932 }
4933 TCTESTYIELD();
4934 return true;
4935 }
4936
4937
4938 /* Lock all records of the hash database object.
4939 `hdb' specifies the hash database object.
4940 `wr' specifies whether the lock is writer or not.
4941 If successful, the return value is true, else, it is false. */
tchdblockallrecords(TCHDB * hdb,bool wr)4942 static bool tchdblockallrecords(TCHDB *hdb, bool wr){
4943 assert(hdb);
4944 for(int i = 0; i <= UINT8_MAX; i++){
4945 if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)hdb->rmtxs + i) != 0 :
4946 pthread_rwlock_rdlock((pthread_rwlock_t *)hdb->rmtxs + i) != 0){
4947 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4948 while(--i >= 0){
4949 pthread_rwlock_unlock((pthread_rwlock_t *)hdb->rmtxs + i);
4950 }
4951 return false;
4952 }
4953 }
4954 TCTESTYIELD();
4955 return true;
4956 }
4957
4958
4959 /* Unlock all records of the hash database object.
4960 `hdb' specifies the hash database object.
4961 If successful, the return value is true, else, it is false. */
tchdbunlockallrecords(TCHDB * hdb)4962 static bool tchdbunlockallrecords(TCHDB *hdb){
4963 assert(hdb);
4964 bool err = false;
4965 for(int i = UINT8_MAX; i >= 0; i--){
4966 if(pthread_rwlock_unlock((pthread_rwlock_t *)hdb->rmtxs + i)) err = true;
4967 }
4968 TCTESTYIELD();
4969 if(err){
4970 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4971 return false;
4972 }
4973 return true;
4974 }
4975
4976
4977 /* Lock the whole database of the hash database object.
4978 `hdb' specifies the hash database object.
4979 If successful, the return value is true, else, it is false. */
tchdblockdb(TCHDB * hdb)4980 static bool tchdblockdb(TCHDB *hdb){
4981 assert(hdb);
4982 if(pthread_mutex_lock(hdb->dmtx) != 0){
4983 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4984 return false;
4985 }
4986 TCTESTYIELD();
4987 return true;
4988 }
4989
4990
4991 /* Unlock the whole database of the hash database object.
4992 `hdb' specifies the hash database object.
4993 If successful, the return value is true, else, it is false. */
tchdbunlockdb(TCHDB * hdb)4994 static bool tchdbunlockdb(TCHDB *hdb){
4995 assert(hdb);
4996 if(pthread_mutex_unlock(hdb->dmtx) != 0){
4997 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
4998 return false;
4999 }
5000 TCTESTYIELD();
5001 return true;
5002 }
5003
5004
5005 /* Lock the write ahead logging file of the hash database object.
5006 `hdb' specifies the hash database object.
5007 If successful, the return value is true, else, it is false. */
tchdblockwal(TCHDB * hdb)5008 static bool tchdblockwal(TCHDB *hdb){
5009 assert(hdb);
5010 if(pthread_mutex_lock(hdb->wmtx) != 0){
5011 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
5012 return false;
5013 }
5014 TCTESTYIELD();
5015 return true;
5016 }
5017
5018
5019 /* Unlock the write ahead logging file of the hash database object.
5020 `hdb' specifies the hash database object.
5021 If successful, the return value is true, else, it is false. */
tchdbunlockwal(TCHDB * hdb)5022 static bool tchdbunlockwal(TCHDB *hdb){
5023 assert(hdb);
5024 if(pthread_mutex_unlock(hdb->wmtx) != 0){
5025 tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
5026 return false;
5027 }
5028 TCTESTYIELD();
5029 return true;
5030 }
5031
5032
5033
5034 /*************************************************************************************************
5035 * debugging functions
5036 *************************************************************************************************/
5037
5038
5039 /* Print meta data of the header into the debugging output.
5040 `hdb' specifies the hash database object. */
tchdbprintmeta(TCHDB * hdb)5041 void tchdbprintmeta(TCHDB *hdb){
5042 assert(hdb);
5043 if(hdb->dbgfd < 0) return;
5044 int dbgfd = (hdb->dbgfd == UINT16_MAX) ? 1 : hdb->dbgfd;
5045 char buf[HDBIOBUFSIZ];
5046 char *wp = buf;
5047 wp += sprintf(wp, "META:");
5048 wp += sprintf(wp, " mmtx=%p", (void *)hdb->mmtx);
5049 wp += sprintf(wp, " rmtxs=%p", (void *)hdb->rmtxs);
5050 wp += sprintf(wp, " dmtx=%p", (void *)hdb->dmtx);
5051 wp += sprintf(wp, " wmtx=%p", (void *)hdb->wmtx);
5052 wp += sprintf(wp, " eckey=%p", (void *)hdb->eckey);
5053 wp += sprintf(wp, " rpath=%s", hdb->rpath ? hdb->rpath : "-");
5054 wp += sprintf(wp, " type=%02X", hdb->type);
5055 wp += sprintf(wp, " flags=%02X", hdb->flags);
5056 wp += sprintf(wp, " bnum=%llu", (unsigned long long)hdb->bnum);
5057 wp += sprintf(wp, " apow=%u", hdb->apow);
5058 wp += sprintf(wp, " fpow=%u", hdb->fpow);
5059 wp += sprintf(wp, " opts=%u", hdb->opts);
5060 wp += sprintf(wp, " path=%s", hdb->path ? hdb->path : "-");
5061 wp += sprintf(wp, " fd=%d", hdb->fd);
5062 wp += sprintf(wp, " omode=%u", hdb->omode);
5063 wp += sprintf(wp, " rnum=%llu", (unsigned long long)hdb->rnum);
5064 wp += sprintf(wp, " fsiz=%llu", (unsigned long long)hdb->fsiz);
5065 wp += sprintf(wp, " frec=%llu", (unsigned long long)hdb->frec);
5066 wp += sprintf(wp, " dfcur=%llu", (unsigned long long)hdb->dfcur);
5067 wp += sprintf(wp, " iter=%llu", (unsigned long long)hdb->iter);
5068 wp += sprintf(wp, " map=%p", (void *)hdb->map);
5069 wp += sprintf(wp, " msiz=%llu", (unsigned long long)hdb->msiz);
5070 wp += sprintf(wp, " ba32=%p", (void *)hdb->ba32);
5071 wp += sprintf(wp, " ba64=%p", (void *)hdb->ba64);
5072 wp += sprintf(wp, " align=%u", hdb->align);
5073 wp += sprintf(wp, " runit=%u", hdb->runit);
5074 wp += sprintf(wp, " zmode=%u", hdb->zmode);
5075 wp += sprintf(wp, " fbpmax=%d", hdb->fbpmax);
5076 wp += sprintf(wp, " fbpool=%p", (void *)hdb->fbpool);
5077 wp += sprintf(wp, " fbpnum=%d", hdb->fbpnum);
5078 wp += sprintf(wp, " fbpmis=%d", hdb->fbpmis);
5079 wp += sprintf(wp, " drpool=%p", (void *)hdb->drpool);
5080 wp += sprintf(wp, " drpdef=%p", (void *)hdb->drpdef);
5081 wp += sprintf(wp, " drpoff=%llu", (unsigned long long)hdb->drpoff);
5082 wp += sprintf(wp, " recc=%p", (void *)hdb->recc);
5083 wp += sprintf(wp, " rcnum=%u", hdb->rcnum);
5084 wp += sprintf(wp, " ecode=%d", hdb->ecode);
5085 wp += sprintf(wp, " fatal=%u", hdb->fatal);
5086 wp += sprintf(wp, " inode=%llu", (unsigned long long)(uint64_t)hdb->inode);
5087 wp += sprintf(wp, " mtime=%llu", (unsigned long long)(uint64_t)hdb->mtime);
5088 wp += sprintf(wp, " dfunit=%u", hdb->dfunit);
5089 wp += sprintf(wp, " dfcnt=%u", hdb->dfcnt);
5090 wp += sprintf(wp, " tran=%d", hdb->tran);
5091 wp += sprintf(wp, " walfd=%d", hdb->walfd);
5092 wp += sprintf(wp, " walend=%llu", (unsigned long long)hdb->walend);
5093 wp += sprintf(wp, " dbgfd=%d", hdb->dbgfd);
5094 wp += sprintf(wp, " cnt_writerec=%lld", (long long)hdb->cnt_writerec);
5095 wp += sprintf(wp, " cnt_reuserec=%lld", (long long)hdb->cnt_reuserec);
5096 wp += sprintf(wp, " cnt_moverec=%lld", (long long)hdb->cnt_moverec);
5097 wp += sprintf(wp, " cnt_readrec=%lld", (long long)hdb->cnt_readrec);
5098 wp += sprintf(wp, " cnt_searchfbp=%lld", (long long)hdb->cnt_searchfbp);
5099 wp += sprintf(wp, " cnt_insertfbp=%lld", (long long)hdb->cnt_insertfbp);
5100 wp += sprintf(wp, " cnt_splicefbp=%lld", (long long)hdb->cnt_splicefbp);
5101 wp += sprintf(wp, " cnt_dividefbp=%lld", (long long)hdb->cnt_dividefbp);
5102 wp += sprintf(wp, " cnt_mergefbp=%lld", (long long)hdb->cnt_mergefbp);
5103 wp += sprintf(wp, " cnt_reducefbp=%lld", (long long)hdb->cnt_reducefbp);
5104 wp += sprintf(wp, " cnt_appenddrp=%lld", (long long)hdb->cnt_appenddrp);
5105 wp += sprintf(wp, " cnt_deferdrp=%lld", (long long)hdb->cnt_deferdrp);
5106 wp += sprintf(wp, " cnt_flushdrp=%lld", (long long)hdb->cnt_flushdrp);
5107 wp += sprintf(wp, " cnt_adjrecc=%lld", (long long)hdb->cnt_adjrecc);
5108 wp += sprintf(wp, " cnt_defrag=%lld", (long long)hdb->cnt_defrag);
5109 wp += sprintf(wp, " cnt_shiftrec=%lld", (long long)hdb->cnt_shiftrec);
5110 wp += sprintf(wp, " cnt_trunc=%lld", (long long)hdb->cnt_trunc);
5111 *(wp++) = '\n';
5112 tcwrite(dbgfd, buf, wp - buf);
5113 }
5114
5115
5116 /* Print a record information into the debugging output.
5117 `hdb' specifies the hash database object.
5118 `rec' specifies the record. */
tchdbprintrec(TCHDB * hdb,TCHREC * rec)5119 void tchdbprintrec(TCHDB *hdb, TCHREC *rec){
5120 assert(hdb && rec);
5121 if(hdb->dbgfd < 0) return;
5122 int dbgfd = (hdb->dbgfd == UINT16_MAX) ? 1 : hdb->dbgfd;
5123 char buf[HDBIOBUFSIZ];
5124 char *wp = buf;
5125 wp += sprintf(wp, "REC:");
5126 wp += sprintf(wp, " off=%llu", (unsigned long long)rec->off);
5127 wp += sprintf(wp, " rsiz=%u", rec->rsiz);
5128 wp += sprintf(wp, " magic=%02X", rec->magic);
5129 wp += sprintf(wp, " hash=%02X", rec->hash);
5130 wp += sprintf(wp, " left=%llu", (unsigned long long)rec->left);
5131 wp += sprintf(wp, " right=%llu", (unsigned long long)rec->right);
5132 wp += sprintf(wp, " ksiz=%u", rec->ksiz);
5133 wp += sprintf(wp, " vsiz=%u", rec->vsiz);
5134 wp += sprintf(wp, " psiz=%u", rec->psiz);
5135 wp += sprintf(wp, " kbuf=%p", (void *)rec->kbuf);
5136 wp += sprintf(wp, " vbuf=%p", (void *)rec->vbuf);
5137 wp += sprintf(wp, " boff=%llu", (unsigned long long)rec->boff);
5138 wp += sprintf(wp, " bbuf=%p", (void *)rec->bbuf);
5139 *(wp++) = '\n';
5140 tcwrite(dbgfd, buf, wp - buf);
5141 }
5142
5143
5144
5145 // END OF FILE
5146