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