1 /*************************************************************************************************
2  * The abstract 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 "tcfdb.h"
21 #include "tctdb.h"
22 #include "tcadb.h"
23 #include "myconf.h"
24 
25 #define ADBDIRMODE     00755             // permission of created directories
26 #define ADBMULPREFIX   "adbmul-"         // prefix of multiple database files
27 
28 typedef struct {                         // type of structure for multiple database
29   TCADB **adbs;                          // inner database objects
30   int num;                               // number of inner databases
31   int iter;                              // index of the iterator
32   char *path;                            // path of the base directory
33 } ADBMUL;
34 
35 typedef struct {                         // type of structure for mapper to B+ tree database
36   TCADB *adb;                            // source database object
37   TCBDB *bdb;                            // destination database object
38   TCLIST *recs;                          // cached records
39   int64_t rsiz;                          // total size of cached records
40   int64_t csiz;                          // capacity of cached records
41   ADBMAPPROC proc;                       // mapping function
42   void *op;                              // opaque object for the mapping function
43 } ADBMAPBDB;
44 
45 
46 /* private function prototypes */
47 static ADBMUL *tcadbmulnew(int num);
48 static void tcadbmuldel(ADBMUL *mul);
49 static bool tcadbmulopen(ADBMUL *mul, const char *name);
50 static bool tcadbmulclose(ADBMUL *mul);
51 static bool tcadbmulput(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
52 static bool tcadbmulputkeep(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
53 static bool tcadbmulputcat(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
54 static bool tcadbmulout(ADBMUL *mul, const void *kbuf, int ksiz);
55 static void *tcadbmulget(ADBMUL *mul, const void *kbuf, int ksiz, int *sp);
56 static int tcadbmulvsiz(ADBMUL *mul, const void *kbuf, int ksiz);
57 static bool tcadbmuliterinit(ADBMUL *mul);
58 static void *tcadbmuliternext(ADBMUL *mul, int *sp);
59 static TCLIST *tcadbmulfwmkeys(ADBMUL *mul, const void *pbuf, int psiz, int max);
60 static int tcadbmuladdint(ADBMUL *mul, const void *kbuf, int ksiz, int num);
61 static double tcadbmuladddouble(ADBMUL *mul, const void *kbuf, int ksiz, double num);
62 static bool tcadbmulsync(ADBMUL *mul);
63 static bool tcadbmuloptimize(ADBMUL *mul, const char *params);
64 static bool tcadbmulvanish(ADBMUL *mul);
65 static bool tcadbmulcopy(ADBMUL *mul, const char *path);
66 static bool tcadbmultranbegin(ADBMUL *mul);
67 static bool tcadbmultrancommit(ADBMUL *mul);
68 static bool tcadbmultranabort(ADBMUL *mul);
69 static const char *tcadbmulpath(ADBMUL *mul);
70 static uint64_t tcadbmulrnum(ADBMUL *mul);
71 static uint64_t tcadbmulsize(ADBMUL *mul);
72 static TCLIST *tcadbmulmisc(ADBMUL *mul, const char *name, const TCLIST *args);
73 static bool tcadbmulputproc(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
74                             TCPDPROC proc, void *op);
75 static bool tcadbmulforeach(ADBMUL *mul, TCITER iter, void *op);
76 static int tcadbmulidx(ADBMUL *mul, const void *kbuf, int ksiz);
77 static bool tcadbmapbdbiter(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op);
78 static bool tcadbmapbdbdump(ADBMAPBDB *map);
79 static int tcadbmapreccmplexical(const TCLISTDATUM *a, const TCLISTDATUM *b);
80 static int tcadbmapreccmpdecimal(const TCLISTDATUM *a, const TCLISTDATUM *b);
81 static int tcadbmapreccmpint32(const TCLISTDATUM *a, const TCLISTDATUM *b);
82 static int tcadbmapreccmpint64(const TCLISTDATUM *a, const TCLISTDATUM *b);
83 static int tcadbtdbqrygetout(const void *pkbuf, int pksiz, TCMAP *cols, void *op);
84 
85 
86 
87 /*************************************************************************************************
88  * API
89  *************************************************************************************************/
90 
91 
92 /* Create an abstract database object. */
tcadbnew(void)93 TCADB *tcadbnew(void){
94   TCADB *adb;
95   TCMALLOC(adb, sizeof(*adb));
96   adb->omode = ADBOVOID;
97   adb->mdb = NULL;
98   adb->ndb = NULL;
99   adb->hdb = NULL;
100   adb->bdb = NULL;
101   adb->fdb = NULL;
102   adb->tdb = NULL;
103   adb->capnum = -1;
104   adb->capsiz = -1;
105   adb->capcnt = 0;
106   adb->cur = NULL;
107   adb->skel = NULL;
108   return adb;
109 }
110 
111 
112 /* Delete an abstract database object. */
tcadbdel(TCADB * adb)113 void tcadbdel(TCADB *adb){
114   assert(adb);
115   if(adb->omode != ADBOVOID) tcadbclose(adb);
116   if(adb->skel){
117     ADBSKEL *skel = adb->skel;
118     if(skel->del) skel->del(skel->opq);
119     TCFREE(skel);
120   }
121   TCFREE(adb);
122 }
123 
124 
125 /* Open an abstract database. */
tcadbopen(TCADB * adb,const char * name)126 bool tcadbopen(TCADB *adb, const char *name){
127   assert(adb && name);
128   if(adb->omode != ADBOVOID) return false;
129   TCLIST *elems = tcstrsplit(name, "#");
130   char *path = tclistshift2(elems);
131   if(!path){
132     tclistdel(elems);
133     return false;
134   }
135   int dbgfd = -1;
136   int64_t bnum = -1;
137   int64_t capnum = -1;
138   int64_t capsiz = -1;
139   bool owmode = true;
140   bool ocmode = true;
141   bool otmode = false;
142   bool onlmode = false;
143   bool onbmode = false;
144   int8_t apow = -1;
145   int8_t fpow = -1;
146   bool tlmode = false;
147   bool tdmode = false;
148   bool tbmode = false;
149   bool ttmode = false;
150   int32_t rcnum = -1;
151   int64_t xmsiz = -1;
152   int32_t dfunit = -1;
153   int32_t lmemb = -1;
154   int32_t nmemb = -1;
155   int32_t lcnum = -1;
156   int32_t ncnum = -1;
157   int32_t width = -1;
158   int64_t limsiz = -1;
159   TCLIST *idxs = NULL;
160   int ln = TCLISTNUM(elems);
161   for(int i = 0; i < ln; i++){
162     const char *elem = TCLISTVALPTR(elems, i);
163     char *pv = strchr(elem, '=');
164     if(!pv) continue;
165     *(pv++) = '\0';
166     if(!tcstricmp(elem, "dbgfd")){
167       dbgfd = tcatoi(pv);
168     } else if(!tcstricmp(elem, "bnum")){
169       bnum = tcatoix(pv);
170     } else if(!tcstricmp(elem, "capnum")){
171       capnum = tcatoix(pv);
172     } else if(!tcstricmp(elem, "capsiz")){
173       capsiz = tcatoix(pv);
174     } else if(!tcstricmp(elem, "mode")){
175       owmode = strchr(pv, 'w') || strchr(pv, 'W');
176       ocmode = strchr(pv, 'c') || strchr(pv, 'C');
177       otmode = strchr(pv, 't') || strchr(pv, 'T');
178       onlmode = strchr(pv, 'e') || strchr(pv, 'E');
179       onbmode = strchr(pv, 'f') || strchr(pv, 'F');
180     } else if(!tcstricmp(elem, "apow")){
181       apow = tcatoix(pv);
182     } else if(!tcstricmp(elem, "fpow")){
183       fpow = tcatoix(pv);
184     } else if(!tcstricmp(elem, "opts")){
185       if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true;
186       if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true;
187       if(strchr(pv, 'b') || strchr(pv, 'B')) tbmode = true;
188       if(strchr(pv, 't') || strchr(pv, 'T')) ttmode = true;
189     } else if(!tcstricmp(elem, "rcnum")){
190       rcnum = tcatoix(pv);
191     } else if(!tcstricmp(elem, "xmsiz")){
192       xmsiz = tcatoix(pv);
193     } else if(!tcstricmp(elem, "dfunit")){
194       dfunit = tcatoix(pv);
195     } else if(!tcstricmp(elem, "lmemb")){
196       lmemb = tcatoix(pv);
197     } else if(!tcstricmp(elem, "nmemb")){
198       nmemb = tcatoix(pv);
199     } else if(!tcstricmp(elem, "lcnum")){
200       lcnum = tcatoix(pv);
201     } else if(!tcstricmp(elem, "ncnum")){
202       ncnum = tcatoix(pv);
203     } else if(!tcstricmp(elem, "width")){
204       width = tcatoix(pv);
205     } else if(!tcstricmp(elem, "limsiz")){
206       limsiz = tcatoix(pv);
207     } else if(!tcstricmp(elem, "idx")){
208       if(!idxs) idxs = tclistnew();
209       TCLISTPUSH(idxs, pv, strlen(pv));
210     }
211   }
212   tclistdel(elems);
213   adb->omode = ADBOVOID;
214   if(adb->skel){
215     ADBSKEL *skel = adb->skel;
216     if(!skel->open || !skel->open(skel->opq, name)){
217       if(idxs) tclistdel(idxs);
218       TCFREE(path);
219       return false;
220     }
221     adb->omode = ADBOSKEL;
222   } else if(!tcstricmp(path, "*")){
223     adb->mdb = bnum > 0 ? tcmdbnew2(bnum) : tcmdbnew();
224     adb->capnum = capnum;
225     adb->capsiz = capsiz;
226     adb->capcnt = 0;
227     adb->omode = ADBOMDB;
228   } else if(!tcstricmp(path, "+")){
229     adb->ndb = tcndbnew();
230     adb->capnum = capnum;
231     adb->capsiz = capsiz;
232     adb->capcnt = 0;
233     adb->omode = ADBONDB;
234   } else if(tcstribwm(path, ".tch") || tcstribwm(path, ".hdb")){
235     TCHDB *hdb = tchdbnew();
236     if(dbgfd >= 0) tchdbsetdbgfd(hdb, dbgfd);
237     tchdbsetmutex(hdb);
238     int opts = 0;
239     if(tlmode) opts |= HDBTLARGE;
240     if(tdmode) opts |= HDBTDEFLATE;
241     if(tbmode) opts |= HDBTBZIP;
242     if(ttmode) opts |= HDBTTCBS;
243     tchdbtune(hdb, bnum, apow, fpow, opts);
244     tchdbsetcache(hdb, rcnum);
245     if(xmsiz >= 0) tchdbsetxmsiz(hdb, xmsiz);
246     if(dfunit >= 0) tchdbsetdfunit(hdb, dfunit);
247     int omode = owmode ? HDBOWRITER : HDBOREADER;
248     if(ocmode) omode |= HDBOCREAT;
249     if(otmode) omode |= HDBOTRUNC;
250     if(onlmode) omode |= HDBONOLCK;
251     if(onbmode) omode |= HDBOLCKNB;
252     if(!tchdbopen(hdb, path, omode)){
253       tchdbdel(hdb);
254       if(idxs) tclistdel(idxs);
255       TCFREE(path);
256       return false;
257     }
258     adb->hdb = hdb;
259     adb->omode = ADBOHDB;
260   } else if(tcstribwm(path, ".tcb") || tcstribwm(path, ".bdb")){
261     TCBDB *bdb = tcbdbnew();
262     if(dbgfd >= 0) tcbdbsetdbgfd(bdb, dbgfd);
263     tcbdbsetmutex(bdb);
264     int opts = 0;
265     if(tlmode) opts |= BDBTLARGE;
266     if(tdmode) opts |= BDBTDEFLATE;
267     if(tbmode) opts |= BDBTBZIP;
268     if(ttmode) opts |= BDBTTCBS;
269     tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
270     tcbdbsetcache(bdb, lcnum, ncnum);
271     if(xmsiz >= 0) tcbdbsetxmsiz(bdb, xmsiz);
272     if(dfunit >= 0) tcbdbsetdfunit(bdb, dfunit);
273     if(capnum > 0) tcbdbsetcapnum(bdb, capnum);
274     int omode = owmode ? BDBOWRITER : BDBOREADER;
275     if(ocmode) omode |= BDBOCREAT;
276     if(otmode) omode |= BDBOTRUNC;
277     if(onlmode) omode |= BDBONOLCK;
278     if(onbmode) omode |= BDBOLCKNB;
279     if(!tcbdbopen(bdb, path, omode)){
280       tcbdbdel(bdb);
281       if(idxs) tclistdel(idxs);
282       TCFREE(path);
283       return false;
284     }
285     adb->bdb = bdb;
286     adb->cur = tcbdbcurnew(bdb);
287     adb->omode = ADBOBDB;
288   } else if(tcstribwm(path, ".tcf") || tcstribwm(path, ".fdb")){
289     TCFDB *fdb = tcfdbnew();
290     if(dbgfd >= 0) tcfdbsetdbgfd(fdb, dbgfd);
291     tcfdbsetmutex(fdb);
292     tcfdbtune(fdb, width, limsiz);
293     int omode = owmode ? FDBOWRITER : FDBOREADER;
294     if(ocmode) omode |= FDBOCREAT;
295     if(otmode) omode |= FDBOTRUNC;
296     if(onlmode) omode |= FDBONOLCK;
297     if(onbmode) omode |= FDBOLCKNB;
298     if(!tcfdbopen(fdb, path, omode)){
299       tcfdbdel(fdb);
300       if(idxs) tclistdel(idxs);
301       TCFREE(path);
302       return false;
303     }
304     adb->fdb = fdb;
305     adb->omode = ADBOFDB;
306   } else if(tcstribwm(path, ".tct") || tcstribwm(path, ".tdb")){
307     TCTDB *tdb = tctdbnew();
308     if(dbgfd >= 0) tctdbsetdbgfd(tdb, dbgfd);
309     tctdbsetmutex(tdb);
310     int opts = 0;
311     if(tlmode) opts |= TDBTLARGE;
312     if(tdmode) opts |= TDBTDEFLATE;
313     if(tbmode) opts |= TDBTBZIP;
314     if(ttmode) opts |= TDBTTCBS;
315     tctdbtune(tdb, bnum, apow, fpow, opts);
316     tctdbsetcache(tdb, rcnum, lcnum, ncnum);
317     if(xmsiz >= 0) tctdbsetxmsiz(tdb, xmsiz);
318     if(dfunit >= 0) tctdbsetdfunit(tdb, dfunit);
319     int omode = owmode ? TDBOWRITER : TDBOREADER;
320     if(ocmode) omode |= TDBOCREAT;
321     if(otmode) omode |= TDBOTRUNC;
322     if(onlmode) omode |= TDBONOLCK;
323     if(onbmode) omode |= TDBOLCKNB;
324     if(!tctdbopen(tdb, path, omode)){
325       tctdbdel(tdb);
326       if(idxs) tclistdel(idxs);
327       TCFREE(path);
328       return false;
329     }
330     if(idxs){
331       int xnum = TCLISTNUM(idxs);
332       for(int i = 0; i < xnum; i++){
333         const char *expr = TCLISTVALPTR(idxs, i);
334         int type = TDBITLEXICAL;
335         char *pv = strchr(expr, ':');
336         if(pv){
337           *(pv++) = '\0';
338           type = tctdbstrtoindextype(pv);
339         }
340         if(type >= 0) tctdbsetindex(tdb, expr, type | TDBITKEEP);
341       }
342     }
343     adb->tdb = tdb;
344     adb->omode = ADBOTDB;
345   }
346   if(idxs) tclistdel(idxs);
347   TCFREE(path);
348   if(adb->omode == ADBOVOID) return false;
349   return true;
350 }
351 
352 
353 /* Close an abstract database object. */
tcadbclose(TCADB * adb)354 bool tcadbclose(TCADB *adb){
355   assert(adb);
356   int err = false;
357   ADBSKEL *skel;
358   switch(adb->omode){
359     case ADBOMDB:
360       tcmdbdel(adb->mdb);
361       adb->mdb = NULL;
362       break;
363     case ADBONDB:
364       tcndbdel(adb->ndb);
365       adb->ndb = NULL;
366       break;
367     case ADBOHDB:
368       if(!tchdbclose(adb->hdb)) err = true;
369       tchdbdel(adb->hdb);
370       adb->hdb = NULL;
371       break;
372     case ADBOBDB:
373       tcbdbcurdel(adb->cur);
374       if(!tcbdbclose(adb->bdb)) err = true;
375       tcbdbdel(adb->bdb);
376       adb->bdb = NULL;
377       break;
378     case ADBOFDB:
379       if(!tcfdbclose(adb->fdb)) err = true;
380       tcfdbdel(adb->fdb);
381       adb->fdb = NULL;
382       break;
383     case ADBOTDB:
384       if(!tctdbclose(adb->tdb)) err = true;
385       tctdbdel(adb->tdb);
386       adb->tdb = NULL;
387       break;
388     case ADBOSKEL:
389       skel = adb->skel;
390       if(skel->close){
391         if(!skel->close(skel->opq)) err = true;
392       } else {
393         err = true;
394       }
395       break;
396     default:
397       err = true;
398       break;
399   }
400   adb->omode = ADBOVOID;
401   return !err;
402 }
403 
404 
405 /* Store a record into an abstract database object. */
tcadbput(TCADB * adb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)406 bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
407   assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
408   bool err = false;
409   char numbuf[TCNUMBUFSIZ];
410   ADBSKEL *skel;
411   switch(adb->omode){
412     case ADBOMDB:
413       if(adb->capnum > 0 || adb->capsiz > 0){
414         tcmdbput3(adb->mdb, kbuf, ksiz, vbuf, vsiz);
415         adb->capcnt++;
416         if((adb->capcnt & 0xff) == 0){
417           if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100)
418             tcmdbcutfront(adb->mdb, 0x100);
419           if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz)
420             tcmdbcutfront(adb->mdb, 0x200);
421         }
422       } else {
423         tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz);
424       }
425       break;
426     case ADBONDB:
427       tcndbput(adb->ndb, kbuf, ksiz, vbuf, vsiz);
428       if(adb->capnum > 0 || adb->capsiz > 0){
429         adb->capcnt++;
430         if((adb->capcnt & 0xff) == 0){
431           if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100)
432             tcndbcutfringe(adb->ndb, 0x100);
433           if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz)
434             tcndbcutfringe(adb->ndb, 0x200);
435         }
436       }
437       break;
438     case ADBOHDB:
439       if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
440       break;
441     case ADBOBDB:
442       if(!tcbdbput(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
443       break;
444     case ADBOFDB:
445       if(!tcfdbput2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true;
446       break;
447     case ADBOTDB:
448       if(ksiz < 1){
449         ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb));
450         kbuf = numbuf;
451       }
452       if(!tctdbput2(adb->tdb, kbuf, ksiz, vbuf, vsiz)) err = true;
453       break;
454     case ADBOSKEL:
455       skel = adb->skel;
456       if(skel->put){
457         if(!skel->put(skel->opq, kbuf, ksiz, vbuf, vsiz)) err = true;
458       } else {
459         err = true;
460       }
461       break;
462     default:
463       err = true;
464       break;
465   }
466   return !err;
467 }
468 
469 
470 /* Store a string record into an abstract object. */
tcadbput2(TCADB * adb,const char * kstr,const char * vstr)471 bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr){
472   assert(adb && kstr && vstr);
473   return tcadbput(adb, kstr, strlen(kstr), vstr, strlen(vstr));
474 }
475 
476 
477 /* Store a new record into an abstract database object. */
tcadbputkeep(TCADB * adb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)478 bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
479   assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
480   bool err = false;
481   char numbuf[TCNUMBUFSIZ];
482   ADBSKEL *skel;
483   switch(adb->omode){
484     case ADBOMDB:
485       if(tcmdbputkeep(adb->mdb, kbuf, ksiz, vbuf, vsiz)){
486         if(adb->capnum > 0 || adb->capsiz > 0){
487           adb->capcnt++;
488           if((adb->capcnt & 0xff) == 0){
489             if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100)
490               tcmdbcutfront(adb->mdb, 0x100);
491             if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz)
492               tcmdbcutfront(adb->mdb, 0x200);
493           }
494         }
495       } else {
496         err = true;
497       }
498       break;
499     case ADBONDB:
500       if(tcndbputkeep(adb->ndb, kbuf, ksiz, vbuf, vsiz)){
501         if(adb->capnum > 0 || adb->capsiz > 0){
502           adb->capcnt++;
503           if((adb->capcnt & 0xff) == 0){
504             if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100)
505               tcndbcutfringe(adb->ndb, 0x100);
506             if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz)
507               tcndbcutfringe(adb->ndb, 0x200);
508           }
509         }
510       } else {
511         err = true;
512       }
513       break;
514     case ADBOHDB:
515       if(!tchdbputkeep(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
516       break;
517     case ADBOBDB:
518       if(!tcbdbputkeep(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
519       break;
520     case ADBOFDB:
521       if(!tcfdbputkeep2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true;
522       break;
523     case ADBOTDB:
524       if(ksiz < 1){
525         ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb));
526         kbuf = numbuf;
527       }
528       if(!tctdbputkeep2(adb->tdb, kbuf, ksiz, vbuf, vsiz)) err = true;
529       break;
530     case ADBOSKEL:
531       skel = adb->skel;
532       if(skel->putkeep){
533         if(!skel->putkeep(skel->opq, kbuf, ksiz, vbuf, vsiz)) err = true;
534       } else {
535         err = true;
536       }
537       break;
538     default:
539       err = true;
540       break;
541   }
542   return !err;
543 }
544 
545 
546 /* Store a new string record into an abstract database object. */
tcadbputkeep2(TCADB * adb,const char * kstr,const char * vstr)547 bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr){
548   assert(adb && kstr && vstr);
549   return tcadbputkeep(adb, kstr, strlen(kstr), vstr, strlen(vstr));
550 }
551 
552 
553 /* Concatenate a value at the end of the existing record in an abstract database object. */
tcadbputcat(TCADB * adb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)554 bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
555   assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
556   bool err = false;
557   char numbuf[TCNUMBUFSIZ];
558   ADBSKEL *skel;
559   switch(adb->omode){
560     case ADBOMDB:
561       if(adb->capnum > 0 || adb->capsiz > 0){
562         tcmdbputcat3(adb->mdb, kbuf, ksiz, vbuf, vsiz);
563         adb->capcnt++;
564         if((adb->capcnt & 0xff) == 0){
565           if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100)
566             tcmdbcutfront(adb->mdb, 0x100);
567           if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz)
568             tcmdbcutfront(adb->mdb, 0x200);
569         }
570       } else {
571         tcmdbputcat(adb->mdb, kbuf, ksiz, vbuf, vsiz);
572       }
573       break;
574     case ADBONDB:
575       tcndbputcat(adb->ndb, kbuf, ksiz, vbuf, vsiz);
576       if(adb->capnum > 0 || adb->capsiz > 0){
577         adb->capcnt++;
578         if((adb->capcnt & 0xff) == 0){
579           if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100)
580             tcndbcutfringe(adb->ndb, 0x100);
581           if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz)
582             tcndbcutfringe(adb->ndb, 0x200);
583         }
584       }
585       break;
586     case ADBOHDB:
587       if(!tchdbputcat(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
588       break;
589     case ADBOBDB:
590       if(!tcbdbputcat(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
591       break;
592     case ADBOFDB:
593       if(!tcfdbputcat2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true;
594       break;
595     case ADBOTDB:
596       if(ksiz < 1){
597         ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb));
598         kbuf = numbuf;
599       }
600       if(!tctdbputcat2(adb->tdb, kbuf, ksiz, vbuf, vsiz)) err = true;
601       break;
602     case ADBOSKEL:
603       skel = adb->skel;
604       if(skel->putcat){
605         if(!skel->putcat(skel->opq, kbuf, ksiz, vbuf, vsiz)) err = true;
606       } else {
607         err = true;
608       }
609       break;
610     default:
611       err = true;
612       break;
613   }
614   return !err;
615 }
616 
617 
618 /* Concatenate a string value at the end of the existing record in an abstract database object. */
tcadbputcat2(TCADB * adb,const char * kstr,const char * vstr)619 bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr){
620   assert(adb && kstr && vstr);
621   return tcadbputcat(adb, kstr, strlen(kstr), vstr, strlen(vstr));
622 }
623 
624 
625 /* Remove a record of an abstract database object. */
tcadbout(TCADB * adb,const void * kbuf,int ksiz)626 bool tcadbout(TCADB *adb, const void *kbuf, int ksiz){
627   assert(adb && kbuf && ksiz >= 0);
628   bool err = false;
629   ADBSKEL *skel;
630   switch(adb->omode){
631     case ADBOMDB:
632       if(!tcmdbout(adb->mdb, kbuf, ksiz)) err = true;
633       break;
634     case ADBONDB:
635       if(!tcndbout(adb->ndb, kbuf, ksiz)) err = true;
636       break;
637     case ADBOHDB:
638       if(!tchdbout(adb->hdb, kbuf, ksiz)) err = true;
639       break;
640     case ADBOBDB:
641       if(!tcbdbout(adb->bdb, kbuf, ksiz)) err = true;
642       break;
643     case ADBOFDB:
644       if(!tcfdbout2(adb->fdb, kbuf, ksiz)) err = true;
645       break;
646     case ADBOTDB:
647       if(!tctdbout(adb->tdb, kbuf, ksiz)) err = true;
648       break;
649     case ADBOSKEL:
650       skel = adb->skel;
651       if(skel->out){
652         if(!skel->out(skel->opq, kbuf, ksiz)) err = true;
653       } else {
654         err = true;
655       }
656       break;
657     default:
658       err = true;
659       break;
660   }
661   return !err;
662 }
663 
664 
665 /* Remove a string record of an abstract database object. */
tcadbout2(TCADB * adb,const char * kstr)666 bool tcadbout2(TCADB *adb, const char *kstr){
667   assert(adb && kstr);
668   return tcadbout(adb, kstr, strlen(kstr));
669 }
670 
671 
672 /* Retrieve a record in an abstract database object. */
tcadbget(TCADB * adb,const void * kbuf,int ksiz,int * sp)673 void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp){
674   assert(adb && kbuf && ksiz >= 0 && sp);
675   char *rv;
676   ADBSKEL *skel;
677   switch(adb->omode){
678     case ADBOMDB:
679       rv = tcmdbget(adb->mdb, kbuf, ksiz, sp);
680       break;
681     case ADBONDB:
682       rv = tcndbget(adb->ndb, kbuf, ksiz, sp);
683       break;
684     case ADBOHDB:
685       rv = tchdbget(adb->hdb, kbuf, ksiz, sp);
686       break;
687     case ADBOBDB:
688       rv = tcbdbget(adb->bdb, kbuf, ksiz, sp);
689       break;
690     case ADBOFDB:
691       rv = tcfdbget2(adb->fdb, kbuf, ksiz, sp);
692       break;
693     case ADBOTDB:
694       rv = tctdbget2(adb->tdb, kbuf, ksiz, sp);
695       break;
696     case ADBOSKEL:
697       skel = adb->skel;
698       if(skel->get){
699         rv = skel->get(skel->opq, kbuf, ksiz, sp);
700       } else {
701         rv = NULL;
702       }
703       break;
704     default:
705       rv = NULL;
706       break;
707   }
708   return rv;
709 }
710 
711 
712 /* Retrieve a string record in an abstract database object. */
tcadbget2(TCADB * adb,const char * kstr)713 char *tcadbget2(TCADB *adb, const char *kstr){
714   assert(adb && kstr);
715   int vsiz;
716   return tcadbget(adb, kstr, strlen(kstr), &vsiz);
717 }
718 
719 
720 /* Get the size of the value of a record in an abstract database object. */
tcadbvsiz(TCADB * adb,const void * kbuf,int ksiz)721 int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz){
722   assert(adb && kbuf && ksiz >= 0);
723   int rv;
724   ADBSKEL *skel;
725   switch(adb->omode){
726     case ADBOMDB:
727       rv = tcmdbvsiz(adb->mdb, kbuf, ksiz);
728       break;
729     case ADBONDB:
730       rv = tcndbvsiz(adb->ndb, kbuf, ksiz);
731       break;
732     case ADBOHDB:
733       rv = tchdbvsiz(adb->hdb, kbuf, ksiz);
734       break;
735     case ADBOBDB:
736       rv = tcbdbvsiz(adb->bdb, kbuf, ksiz);
737       break;
738     case ADBOFDB:
739       rv = tcfdbvsiz2(adb->fdb, kbuf, ksiz);
740       break;
741     case ADBOTDB:
742       rv = tctdbvsiz(adb->tdb, kbuf, ksiz);
743       break;
744     case ADBOSKEL:
745       skel = adb->skel;
746       if(skel->vsiz){
747         rv = skel->vsiz(skel->opq, kbuf, ksiz);
748       } else {
749         rv = -1;
750       }
751       break;
752     default:
753       rv = -1;
754       break;
755   }
756   return rv;
757 }
758 
759 
760 /* Get the size of the value of a string record in an abstract database object. */
tcadbvsiz2(TCADB * adb,const char * kstr)761 int tcadbvsiz2(TCADB *adb, const char *kstr){
762   assert(adb && kstr);
763   return tcadbvsiz(adb, kstr, strlen(kstr));
764 }
765 
766 
767 /* Initialize the iterator of an abstract database object. */
tcadbiterinit(TCADB * adb)768 bool tcadbiterinit(TCADB *adb){
769   assert(adb);
770   bool err = false;
771   ADBSKEL *skel;
772   switch(adb->omode){
773     case ADBOMDB:
774       tcmdbiterinit(adb->mdb);
775       break;
776     case ADBONDB:
777       tcndbiterinit(adb->ndb);
778       break;
779     case ADBOHDB:
780       if(!tchdbiterinit(adb->hdb)) err = true;
781       break;
782     case ADBOBDB:
783       if(!tcbdbcurfirst(adb->cur)){
784         int ecode = tcbdbecode(adb->bdb);
785         if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC)
786           err = true;
787       }
788       break;
789     case ADBOFDB:
790       if(!tcfdbiterinit(adb->fdb)) err = true;
791       break;
792     case ADBOTDB:
793       if(!tctdbiterinit(adb->tdb)) err = true;
794       break;
795     case ADBOSKEL:
796       skel = adb->skel;
797       if(skel->iterinit){
798         if(!skel->iterinit(skel->opq)) err = true;
799       } else {
800         err = true;
801       }
802       break;
803     default:
804       err = true;
805       break;
806   }
807   return !err;
808 }
809 
810 
811 /* Get the next key of the iterator of an abstract database object. */
tcadbiternext(TCADB * adb,int * sp)812 void *tcadbiternext(TCADB *adb, int *sp){
813   assert(adb && sp);
814   char *rv;
815   ADBSKEL *skel;
816   switch(adb->omode){
817     case ADBOMDB:
818       rv = tcmdbiternext(adb->mdb, sp);
819       break;
820     case ADBONDB:
821       rv = tcndbiternext(adb->ndb, sp);
822       break;
823     case ADBOHDB:
824       rv = tchdbiternext(adb->hdb, sp);
825       break;
826     case ADBOBDB:
827       rv = tcbdbcurkey(adb->cur, sp);
828       tcbdbcurnext(adb->cur);
829       break;
830     case ADBOFDB:
831       rv = tcfdbiternext2(adb->fdb, sp);
832       break;
833     case ADBOTDB:
834       rv = tctdbiternext(adb->tdb, sp);
835       break;
836     case ADBOSKEL:
837       skel = adb->skel;
838       if(skel->iternext){
839         rv = skel->iternext(skel->opq, sp);
840       } else {
841         rv = NULL;
842       }
843       break;
844     default:
845       rv = NULL;
846       break;
847   }
848   return rv;
849 }
850 
851 
852 /* Get the next key string of the iterator of an abstract database object. */
tcadbiternext2(TCADB * adb)853 char *tcadbiternext2(TCADB *adb){
854   assert(adb);
855   int vsiz;
856   return tcadbiternext(adb, &vsiz);
857 }
858 
859 
860 /* Get forward matching keys in an abstract database object. */
tcadbfwmkeys(TCADB * adb,const void * pbuf,int psiz,int max)861 TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max){
862   assert(adb && pbuf && psiz >= 0);
863   TCLIST *rv;
864   ADBSKEL *skel;
865   switch(adb->omode){
866     case ADBOMDB:
867       rv = tcmdbfwmkeys(adb->mdb, pbuf, psiz, max);
868       break;
869     case ADBONDB:
870       rv = tcndbfwmkeys(adb->ndb, pbuf, psiz, max);
871       break;
872     case ADBOHDB:
873       rv = tchdbfwmkeys(adb->hdb, pbuf, psiz, max);
874       break;
875     case ADBOBDB:
876       rv = tcbdbfwmkeys(adb->bdb, pbuf, psiz, max);
877       break;
878     case ADBOFDB:
879       rv = tcfdbrange4(adb->fdb, pbuf, psiz, max);
880       break;
881     case ADBOTDB:
882       rv = tctdbfwmkeys(adb->tdb, pbuf, psiz, max);
883       break;
884     case ADBOSKEL:
885       skel = adb->skel;
886       if(skel->fwmkeys){
887         rv = skel->fwmkeys(skel->opq, pbuf, psiz, max);
888       } else {
889         rv = NULL;
890       }
891       break;
892     default:
893       rv = tclistnew();
894       break;
895   }
896   return rv;
897 }
898 
899 
900 /* Get forward matching string keys in an abstract database object. */
tcadbfwmkeys2(TCADB * adb,const char * pstr,int max)901 TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max){
902   assert(adb && pstr);
903   return tcadbfwmkeys(adb, pstr, strlen(pstr), max);
904 }
905 
906 
907 /* Add an integer to a record in an abstract database object. */
tcadbaddint(TCADB * adb,const void * kbuf,int ksiz,int num)908 int tcadbaddint(TCADB *adb, const void *kbuf, int ksiz, int num){
909   assert(adb && kbuf && ksiz >= 0);
910   int rv;
911   char numbuf[TCNUMBUFSIZ];
912   ADBSKEL *skel;
913   switch(adb->omode){
914     case ADBOMDB:
915       rv = tcmdbaddint(adb->mdb, kbuf, ksiz, num);
916       if(adb->capnum > 0 || adb->capsiz > 0){
917         adb->capcnt++;
918         if((adb->capcnt & 0xff) == 0){
919           if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100)
920             tcmdbcutfront(adb->mdb, 0x100);
921           if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz)
922             tcmdbcutfront(adb->mdb, 0x200);
923         }
924       }
925       break;
926     case ADBONDB:
927       rv = tcndbaddint(adb->ndb, kbuf, ksiz, num);
928       if(adb->capnum > 0 || adb->capsiz > 0){
929         adb->capcnt++;
930         if((adb->capcnt & 0xff) == 0){
931           if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100)
932             tcndbcutfringe(adb->ndb, 0x100);
933           if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz)
934             tcndbcutfringe(adb->ndb, 0x200);
935         }
936       }
937       break;
938     case ADBOHDB:
939       rv = tchdbaddint(adb->hdb, kbuf, ksiz, num);
940       break;
941     case ADBOBDB:
942       rv = tcbdbaddint(adb->bdb, kbuf, ksiz, num);
943       break;
944     case ADBOFDB:
945       rv = tcfdbaddint(adb->fdb, tcfdbkeytoid(kbuf, ksiz), num);
946       break;
947     case ADBOTDB:
948       if(ksiz < 1){
949         ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb));
950         kbuf = numbuf;
951       }
952       rv = tctdbaddint(adb->tdb, kbuf, ksiz, num);
953       break;
954     case ADBOSKEL:
955       skel = adb->skel;
956       if(skel->addint){
957         rv = skel->addint(skel->opq, kbuf, ksiz, num);
958       } else {
959         rv = INT_MIN;
960       }
961       break;
962     default:
963       rv = INT_MIN;
964       break;
965   }
966   return rv;
967 }
968 
969 
970 /* Add a real number to a record in an abstract database object. */
tcadbadddouble(TCADB * adb,const void * kbuf,int ksiz,double num)971 double tcadbadddouble(TCADB *adb, const void *kbuf, int ksiz, double num){
972   assert(adb && kbuf && ksiz >= 0);
973   double rv;
974   char numbuf[TCNUMBUFSIZ];
975   ADBSKEL *skel;
976   switch(adb->omode){
977     case ADBOMDB:
978       rv = tcmdbadddouble(adb->mdb, kbuf, ksiz, num);
979       if(adb->capnum > 0 || adb->capsiz > 0){
980         adb->capcnt++;
981         if((adb->capcnt & 0xff) == 0){
982           if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100)
983             tcmdbcutfront(adb->mdb, 0x100);
984           if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz)
985             tcmdbcutfront(adb->mdb, 0x200);
986         }
987       }
988       break;
989     case ADBONDB:
990       rv = tcndbadddouble(adb->ndb, kbuf, ksiz, num);
991       if(adb->capnum > 0 || adb->capsiz > 0){
992         adb->capcnt++;
993         if((adb->capcnt & 0xff) == 0){
994           if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100)
995             tcndbcutfringe(adb->ndb, 0x100);
996           if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz)
997             tcndbcutfringe(adb->ndb, 0x200);
998         }
999       }
1000       break;
1001     case ADBOHDB:
1002       rv = tchdbadddouble(adb->hdb, kbuf, ksiz, num);
1003       break;
1004     case ADBOBDB:
1005       rv = tcbdbadddouble(adb->bdb, kbuf, ksiz, num);
1006       break;
1007     case ADBOFDB:
1008       rv = tcfdbadddouble(adb->fdb, tcfdbkeytoid(kbuf, ksiz), num);
1009       break;
1010     case ADBOTDB:
1011       if(ksiz < 1){
1012         ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb));
1013         kbuf = numbuf;
1014       }
1015       rv = tctdbadddouble(adb->tdb, kbuf, ksiz, num);
1016       break;
1017     case ADBOSKEL:
1018       skel = adb->skel;
1019       if(skel->adddouble){
1020         rv = skel->adddouble(skel->opq, kbuf, ksiz, num);
1021       } else {
1022         rv = nan("");
1023       }
1024       break;
1025     default:
1026       rv = nan("");
1027       break;
1028   }
1029   return rv;
1030 }
1031 
1032 
1033 /* Synchronize updated contents of an abstract database object with the file and the device. */
tcadbsync(TCADB * adb)1034 bool tcadbsync(TCADB *adb){
1035   assert(adb);
1036   bool err = false;
1037   ADBSKEL *skel;
1038   switch(adb->omode){
1039     case ADBOMDB:
1040       if(adb->capnum > 0){
1041         while(tcmdbrnum(adb->mdb) > adb->capnum){
1042           tcmdbcutfront(adb->mdb, 1);
1043         }
1044       }
1045       if(adb->capsiz > 0){
1046         while(tcmdbmsiz(adb->mdb) > adb->capsiz && tcmdbrnum(adb->mdb) > 0){
1047           tcmdbcutfront(adb->mdb, 1);
1048         }
1049       }
1050       adb->capcnt = 0;
1051       break;
1052     case ADBONDB:
1053       if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum)
1054         tcndbcutfringe(adb->ndb, tcndbrnum(adb->ndb) - adb->capnum);
1055       if(adb->capsiz > 0){
1056         while(tcndbmsiz(adb->ndb) > adb->capsiz && tcndbrnum(adb->ndb) > 0){
1057           tcndbcutfringe(adb->ndb, 0x100);
1058         }
1059       }
1060       adb->capcnt = 0;
1061       break;
1062     case ADBOHDB:
1063       if(!tchdbsync(adb->hdb)) err = true;
1064       break;
1065     case ADBOBDB:
1066       if(!tcbdbsync(adb->bdb)) err = true;
1067       break;
1068     case ADBOFDB:
1069       if(!tcfdbsync(adb->fdb)) err = true;
1070       break;
1071     case ADBOTDB:
1072       if(!tctdbsync(adb->tdb)) err = true;
1073       break;
1074     case ADBOSKEL:
1075       skel = adb->skel;
1076       if(skel->sync){
1077         if(!skel->sync(skel->opq)) err = true;
1078       } else {
1079         err = true;
1080       }
1081       break;
1082     default:
1083       err = true;
1084       break;
1085   }
1086   return !err;
1087 }
1088 
1089 
1090 /* Optimize the storage of an abstract database object. */
tcadboptimize(TCADB * adb,const char * params)1091 bool tcadboptimize(TCADB *adb, const char *params){
1092   assert(adb);
1093   TCLIST *elems = params ? tcstrsplit(params, "#") : tclistnew();
1094   int64_t bnum = -1;
1095   int64_t capnum = -1;
1096   int64_t capsiz = -1;
1097   int8_t apow = -1;
1098   int8_t fpow = -1;
1099   bool tdefault = true;
1100   bool tlmode = false;
1101   bool tdmode = false;
1102   bool tbmode = false;
1103   bool ttmode = false;
1104   int32_t lmemb = -1;
1105   int32_t nmemb = -1;
1106   int32_t width = -1;
1107   int64_t limsiz = -1;
1108   int ln = TCLISTNUM(elems);
1109   for(int i = 0; i < ln; i++){
1110     const char *elem = TCLISTVALPTR(elems, i);
1111     char *pv = strchr(elem, '=');
1112     if(!pv) continue;
1113     *(pv++) = '\0';
1114     if(!tcstricmp(elem, "bnum")){
1115       bnum = tcatoix(pv);
1116     } else if(!tcstricmp(elem, "capnum")){
1117       capnum = tcatoix(pv);
1118     } else if(!tcstricmp(elem, "capsiz")){
1119       capsiz = tcatoix(pv);
1120     } else if(!tcstricmp(elem, "apow")){
1121       apow = tcatoix(pv);
1122     } else if(!tcstricmp(elem, "fpow")){
1123       fpow = tcatoix(pv);
1124     } else if(!tcstricmp(elem, "opts")){
1125       tdefault = false;
1126       if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true;
1127       if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true;
1128       if(strchr(pv, 'b') || strchr(pv, 'B')) tbmode = true;
1129       if(strchr(pv, 't') || strchr(pv, 'T')) ttmode = true;
1130     } else if(!tcstricmp(elem, "lmemb")){
1131       lmemb = tcatoix(pv);
1132     } else if(!tcstricmp(elem, "nmemb")){
1133       nmemb = tcatoix(pv);
1134     } else if(!tcstricmp(elem, "width")){
1135       width = tcatoix(pv);
1136     } else if(!tcstricmp(elem, "limsiz")){
1137       limsiz = tcatoix(pv);
1138     }
1139   }
1140   tclistdel(elems);
1141   bool err = false;
1142   int opts;
1143   ADBSKEL *skel;
1144   switch(adb->omode){
1145     case ADBOMDB:
1146       adb->capnum = capnum;
1147       adb->capsiz = capsiz;
1148       tcadbsync(adb);
1149       break;
1150     case ADBONDB:
1151       adb->capnum = capnum;
1152       adb->capsiz = capsiz;
1153       tcadbsync(adb);
1154       break;
1155     case ADBOHDB:
1156       opts = 0;
1157       if(tdefault){
1158         opts = UINT8_MAX;
1159       } else {
1160         if(tlmode) opts |= HDBTLARGE;
1161         if(tdmode) opts |= HDBTDEFLATE;
1162         if(tbmode) opts |= HDBTBZIP;
1163         if(ttmode) opts |= HDBTTCBS;
1164       }
1165       if(!tchdboptimize(adb->hdb, bnum, apow, fpow, opts)) err = true;
1166       break;
1167     case ADBOBDB:
1168       opts = 0;
1169       if(tdefault){
1170         opts = UINT8_MAX;
1171       } else {
1172         if(tlmode) opts |= BDBTLARGE;
1173         if(tdmode) opts |= BDBTDEFLATE;
1174         if(tbmode) opts |= BDBTBZIP;
1175         if(ttmode) opts |= BDBTTCBS;
1176       }
1177       if(!tcbdboptimize(adb->bdb, lmemb, nmemb, bnum, apow, fpow, opts)) err = true;
1178       break;
1179     case ADBOFDB:
1180       if(!tcfdboptimize(adb->fdb, width, limsiz)) err = true;
1181       break;
1182     case ADBOTDB:
1183       opts = 0;
1184       if(tdefault){
1185         opts = UINT8_MAX;
1186       } else {
1187         if(tlmode) opts |= TDBTLARGE;
1188         if(tdmode) opts |= TDBTDEFLATE;
1189         if(tbmode) opts |= TDBTBZIP;
1190         if(ttmode) opts |= TDBTTCBS;
1191       }
1192       if(!tctdboptimize(adb->tdb, bnum, apow, fpow, opts)) err = true;
1193       break;
1194     case ADBOSKEL:
1195       skel = adb->skel;
1196       if(skel->optimize){
1197         if(!skel->optimize(skel->opq, params)) err = true;
1198       } else {
1199         err = true;
1200       }
1201       break;
1202     default:
1203       err = true;
1204       break;
1205   }
1206   return !err;
1207 }
1208 
1209 
1210 /* Remove all records of an abstract database object. */
tcadbvanish(TCADB * adb)1211 bool tcadbvanish(TCADB *adb){
1212   assert(adb);
1213   bool err = false;
1214   ADBSKEL *skel;
1215   switch(adb->omode){
1216     case ADBOMDB:
1217       tcmdbvanish(adb->mdb);
1218       break;
1219     case ADBONDB:
1220       tcndbvanish(adb->ndb);
1221       break;
1222     case ADBOHDB:
1223       if(!tchdbvanish(adb->hdb)) err = true;
1224       break;
1225     case ADBOBDB:
1226       if(!tcbdbvanish(adb->bdb)) err = true;
1227       break;
1228     case ADBOFDB:
1229       if(!tcfdbvanish(adb->fdb)) err = true;
1230       break;
1231     case ADBOTDB:
1232       if(!tctdbvanish(adb->tdb)) err = true;
1233       break;
1234     case ADBOSKEL:
1235       skel = adb->skel;
1236       if(skel->vanish){
1237         if(!skel->vanish(skel->opq)) err = true;
1238       } else {
1239         err = true;
1240       }
1241       break;
1242     default:
1243       err = true;
1244       break;
1245   }
1246   return !err;
1247 }
1248 
1249 
1250 /* Copy the database file of an abstract database object. */
tcadbcopy(TCADB * adb,const char * path)1251 bool tcadbcopy(TCADB *adb, const char *path){
1252   assert(adb && path);
1253   bool err = false;
1254   ADBSKEL *skel;
1255   switch(adb->omode){
1256     case ADBOMDB:
1257     case ADBONDB:
1258       if(*path == '@'){
1259         char tsbuf[TCNUMBUFSIZ];
1260         sprintf(tsbuf, "%llu", (unsigned long long)(tctime() * 1000000));
1261         const char *args[2];
1262         args[0] = path + 1;
1263         args[1] = tsbuf;
1264         if(tcsystem(args, sizeof(args) / sizeof(*args)) != 0) err = true;
1265       } else {
1266         TCADB *tadb = tcadbnew();
1267         if(tcadbopen(tadb, path)){
1268           tcadbiterinit(adb);
1269           char *kbuf;
1270           int ksiz;
1271           while((kbuf = tcadbiternext(adb, &ksiz)) != NULL){
1272             int vsiz;
1273             char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
1274             if(vbuf){
1275               if(!tcadbput(tadb, kbuf, ksiz, vbuf, vsiz)) err = true;
1276               TCFREE(vbuf);
1277             }
1278             TCFREE(kbuf);
1279           }
1280           if(!tcadbclose(tadb)) err = true;
1281         } else {
1282           err = true;
1283         }
1284         tcadbdel(tadb);
1285       }
1286       break;
1287     case ADBOHDB:
1288       if(!tchdbcopy(adb->hdb, path)) err = true;
1289       break;
1290     case ADBOBDB:
1291       if(!tcbdbcopy(adb->bdb, path)) err = true;
1292       break;
1293     case ADBOFDB:
1294       if(!tcfdbcopy(adb->fdb, path)) err = true;
1295       break;
1296     case ADBOTDB:
1297       if(!tctdbcopy(adb->tdb, path)) err = true;
1298       break;
1299     case ADBOSKEL:
1300       skel = adb->skel;
1301       if(skel->copy){
1302         if(!skel->copy(skel->opq, path)) err = true;
1303       } else {
1304         err = true;
1305       }
1306       break;
1307     default:
1308       err = true;
1309       break;
1310   }
1311   return !err;
1312 }
1313 
1314 
1315 /* Begin the transaction of an abstract database object. */
tcadbtranbegin(TCADB * adb)1316 bool tcadbtranbegin(TCADB *adb){
1317   assert(adb);
1318   bool err = false;
1319   ADBSKEL *skel;
1320   switch(adb->omode){
1321     case ADBOMDB:
1322       err = true;
1323       break;
1324     case ADBONDB:
1325       err = true;
1326       break;
1327     case ADBOHDB:
1328       if(!tchdbtranbegin(adb->hdb)) err = true;
1329       break;
1330     case ADBOBDB:
1331       if(!tcbdbtranbegin(adb->bdb)) err = true;
1332       break;
1333     case ADBOFDB:
1334       if(!tcfdbtranbegin(adb->fdb)) err = true;
1335       break;
1336     case ADBOTDB:
1337       if(!tctdbtranbegin(adb->tdb)) err = true;
1338       break;
1339     case ADBOSKEL:
1340       skel = adb->skel;
1341       if(skel->tranbegin){
1342         if(!skel->tranbegin(skel->opq)) err = true;
1343       } else {
1344         err = true;
1345       }
1346       break;
1347     default:
1348       err = true;
1349       break;
1350   }
1351   return !err;
1352 }
1353 
1354 
1355 /* Commit the transaction of an abstract database object. */
tcadbtrancommit(TCADB * adb)1356 bool tcadbtrancommit(TCADB *adb){
1357   assert(adb);
1358   bool err = false;
1359   ADBSKEL *skel;
1360   switch(adb->omode){
1361     case ADBOMDB:
1362       err = true;
1363       break;
1364     case ADBONDB:
1365       err = true;
1366       break;
1367     case ADBOHDB:
1368       if(!tchdbtrancommit(adb->hdb)) err = true;
1369       break;
1370     case ADBOBDB:
1371       if(!tcbdbtrancommit(adb->bdb)) err = true;
1372       break;
1373     case ADBOFDB:
1374       if(!tcfdbtrancommit(adb->fdb)) err = true;
1375       break;
1376     case ADBOTDB:
1377       if(!tctdbtrancommit(adb->tdb)) err = true;
1378       break;
1379     case ADBOSKEL:
1380       skel = adb->skel;
1381       if(skel->trancommit){
1382         if(!skel->trancommit(skel->opq)) err = true;
1383       } else {
1384         err = true;
1385       }
1386       break;
1387     default:
1388       err = true;
1389       break;
1390   }
1391   return !err;
1392 }
1393 
1394 
1395 /* Abort the transaction of an abstract database object. */
tcadbtranabort(TCADB * adb)1396 bool tcadbtranabort(TCADB *adb){
1397   assert(adb);
1398   bool err = false;
1399   ADBSKEL *skel;
1400   switch(adb->omode){
1401     case ADBOMDB:
1402       err = true;
1403       break;
1404     case ADBONDB:
1405       err = true;
1406       break;
1407     case ADBOHDB:
1408       if(!tchdbtranabort(adb->hdb)) err = true;
1409       break;
1410     case ADBOBDB:
1411       if(!tcbdbtranabort(adb->bdb)) err = true;
1412       break;
1413     case ADBOFDB:
1414       if(!tcfdbtranabort(adb->fdb)) err = true;
1415       break;
1416     case ADBOTDB:
1417       if(!tctdbtranabort(adb->tdb)) err = true;
1418       break;
1419     case ADBOSKEL:
1420       skel = adb->skel;
1421       if(skel->tranabort){
1422         if(!skel->tranabort(skel->opq)) err = true;
1423       } else {
1424         err = true;
1425       }
1426       break;
1427     default:
1428       err = true;
1429       break;
1430   }
1431   return !err;
1432 }
1433 
1434 
1435 /* Get the file path of an abstract database object. */
tcadbpath(TCADB * adb)1436 const char *tcadbpath(TCADB *adb){
1437   assert(adb);
1438   const char *rv;
1439   ADBSKEL *skel;
1440   switch(adb->omode){
1441     case ADBOMDB:
1442       rv = "*";
1443       break;
1444     case ADBONDB:
1445       rv = "+";
1446       break;
1447     case ADBOHDB:
1448       rv = tchdbpath(adb->hdb);
1449       break;
1450     case ADBOBDB:
1451       rv = tcbdbpath(adb->bdb);
1452       break;
1453     case ADBOFDB:
1454       rv = tcfdbpath(adb->fdb);
1455       break;
1456     case ADBOTDB:
1457       rv = tctdbpath(adb->tdb);
1458       break;
1459     case ADBOSKEL:
1460       skel = adb->skel;
1461       if(skel->path){
1462         rv = skel->path(skel->opq);
1463       } else {
1464         rv = NULL;
1465       }
1466       break;
1467     default:
1468       rv = NULL;
1469       break;
1470   }
1471   return rv;
1472 }
1473 
1474 
1475 /* Get the number of records of an abstract database object. */
tcadbrnum(TCADB * adb)1476 uint64_t tcadbrnum(TCADB *adb){
1477   assert(adb);
1478   uint64_t rv;
1479   ADBSKEL *skel;
1480   switch(adb->omode){
1481     case ADBOMDB:
1482       rv = tcmdbrnum(adb->mdb);
1483       break;
1484     case ADBONDB:
1485       rv = tcndbrnum(adb->ndb);
1486       break;
1487     case ADBOHDB:
1488       rv = tchdbrnum(adb->hdb);
1489       break;
1490     case ADBOBDB:
1491       rv = tcbdbrnum(adb->bdb);
1492       break;
1493     case ADBOFDB:
1494       rv = tcfdbrnum(adb->fdb);
1495       break;
1496     case ADBOTDB:
1497       rv = tctdbrnum(adb->tdb);
1498       break;
1499     case ADBOSKEL:
1500       skel = adb->skel;
1501       if(skel->rnum){
1502         rv = skel->rnum(skel->opq);
1503       } else {
1504         rv = 0;
1505       }
1506       break;
1507     default:
1508       rv = 0;
1509       break;
1510   }
1511   return rv;
1512 }
1513 
1514 
1515 /* Get the size of the database of an abstract database object. */
tcadbsize(TCADB * adb)1516 uint64_t tcadbsize(TCADB *adb){
1517   assert(adb);
1518   uint64_t rv;
1519   ADBSKEL *skel;
1520   switch(adb->omode){
1521     case ADBOMDB:
1522       rv = tcmdbmsiz(adb->mdb);
1523       break;
1524     case ADBONDB:
1525       rv = tcndbmsiz(adb->ndb);
1526       break;
1527     case ADBOHDB:
1528       rv = tchdbfsiz(adb->hdb);
1529       break;
1530     case ADBOBDB:
1531       rv = tcbdbfsiz(adb->bdb);
1532       break;
1533     case ADBOFDB:
1534       rv = tcfdbfsiz(adb->fdb);
1535       break;
1536     case ADBOTDB:
1537       rv = tctdbfsiz(adb->tdb);
1538       break;
1539     case ADBOSKEL:
1540       skel = adb->skel;
1541       if(skel->size){
1542         rv = skel->size(skel->opq);
1543       } else {
1544         rv = 0;
1545       }
1546       break;
1547     default:
1548       rv = 0;
1549       break;
1550   }
1551   return rv;
1552 }
1553 
1554 
1555 /* Call a versatile function for miscellaneous operations of an abstract database object. */
tcadbmisc(TCADB * adb,const char * name,const TCLIST * args)1556 TCLIST *tcadbmisc(TCADB *adb, const char *name, const TCLIST *args){
1557   assert(adb && name && args);
1558   int argc = TCLISTNUM(args);
1559   TCLIST *rv;
1560   ADBSKEL *skel;
1561   switch(adb->omode){
1562     case ADBOMDB:
1563       if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){
1564         if(argc > 1){
1565           rv = tclistnew2(1);
1566           const char *kbuf;
1567           int ksiz;
1568           TCLISTVAL(kbuf, args, 0, ksiz);
1569           const char *vbuf;
1570           int vsiz;
1571           TCLISTVAL(vbuf, args, 1, vsiz);
1572           bool err = false;
1573           if(!strcmp(name, "put")){
1574             tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz);
1575           } else if(!strcmp(name, "putkeep")){
1576             if(!tcmdbputkeep(adb->mdb, kbuf, ksiz, vbuf, vsiz)) err = true;
1577           } else if(!strcmp(name, "putcat")){
1578             tcmdbputcat(adb->mdb, kbuf, ksiz, vbuf, vsiz);
1579           }
1580           if(err){
1581             tclistdel(rv);
1582             rv = NULL;
1583           }
1584         } else {
1585           rv = NULL;
1586         }
1587       } else if(!strcmp(name, "out")){
1588         if(argc > 0){
1589           rv = tclistnew2(1);
1590           const char *kbuf;
1591           int ksiz;
1592           TCLISTVAL(kbuf, args, 0, ksiz);
1593           if(!tcmdbout(adb->mdb, kbuf, ksiz)){
1594             tclistdel(rv);
1595             rv = NULL;
1596           }
1597         } else {
1598           rv = NULL;
1599         }
1600       } else if(!strcmp(name, "get")){
1601         if(argc > 0){
1602           rv = tclistnew2(1);
1603           const char *kbuf;
1604           int ksiz;
1605           TCLISTVAL(kbuf, args, 0, ksiz);
1606           int vsiz;
1607           char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz);
1608           if(vbuf){
1609             TCLISTPUSH(rv, vbuf, vsiz);
1610             TCFREE(vbuf);
1611           } else {
1612             tclistdel(rv);
1613             rv = NULL;
1614           }
1615         } else {
1616           rv = NULL;
1617         }
1618       } else if(!strcmp(name, "putlist")){
1619         rv = tclistnew2(1);
1620         argc--;
1621         for(int i = 0; i < argc; i += 2){
1622           const char *kbuf, *vbuf;
1623           int ksiz, vsiz;
1624           TCLISTVAL(kbuf, args, i, ksiz);
1625           TCLISTVAL(vbuf, args, i + 1, vsiz);
1626           tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz);
1627         }
1628       } else if(!strcmp(name, "outlist")){
1629         rv = tclistnew2(1);
1630         for(int i = 0; i < argc; i++){
1631           const char *kbuf;
1632           int ksiz;
1633           TCLISTVAL(kbuf, args, i, ksiz);
1634           tcmdbout(adb->mdb, kbuf, ksiz);
1635         }
1636       } else if(!strcmp(name, "getlist")){
1637         rv = tclistnew2(argc * 2);
1638         for(int i = 0; i < argc; i++){
1639           const char *kbuf;
1640           int ksiz;
1641           TCLISTVAL(kbuf, args, i, ksiz);
1642           int vsiz;
1643           char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz);
1644           if(vbuf){
1645             TCLISTPUSH(rv, kbuf, ksiz);
1646             TCLISTPUSH(rv, vbuf, vsiz);
1647             TCFREE(vbuf);
1648           }
1649         }
1650       } else if(!strcmp(name, "getpart")){
1651         if(argc > 0){
1652           const char *kbuf;
1653           int ksiz;
1654           TCLISTVAL(kbuf, args, 0, ksiz);
1655           int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
1656           if(off < 0) off = 0;
1657           if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
1658           int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
1659           if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
1660           int vsiz;
1661           char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz);
1662           if(vbuf){
1663             if(off < vsiz){
1664               rv = tclistnew2(1);
1665               vsiz -= off;
1666               if(vsiz > len) vsiz = len;
1667               if(off > 0) memmove(vbuf, vbuf + off, vsiz);
1668               tclistpushmalloc(rv, vbuf, vsiz);
1669             } else {
1670               rv = NULL;
1671               TCFREE(vbuf);
1672             }
1673           } else {
1674             rv = NULL;
1675           }
1676         } else {
1677           rv = NULL;
1678         }
1679       } else if(!strcmp(name, "iterinit")){
1680         rv = tclistnew2(1);
1681         if(argc > 0){
1682           const char *kbuf;
1683           int ksiz;
1684           TCLISTVAL(kbuf, args, 0, ksiz);
1685           tcmdbiterinit2(adb->mdb, kbuf, ksiz);
1686         } else {
1687           tcmdbiterinit(adb->mdb);
1688         }
1689       } else if(!strcmp(name, "iternext")){
1690         rv = tclistnew2(1);
1691         int ksiz;
1692         char *kbuf = tcmdbiternext(adb->mdb, &ksiz);
1693         if(kbuf){
1694           TCLISTPUSH(rv, kbuf, ksiz);
1695           int vsiz;
1696           char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz);
1697           if(vbuf){
1698             TCLISTPUSH(rv, vbuf, vsiz);
1699             TCFREE(vbuf);
1700           }
1701           TCFREE(kbuf);
1702         } else {
1703           tclistdel(rv);
1704           rv = NULL;
1705         }
1706       } else if(!strcmp(name, "sync")){
1707         rv = tclistnew2(1);
1708         if(!tcadbsync(adb)){
1709           tclistdel(rv);
1710           rv = NULL;
1711         }
1712       } else if(!strcmp(name, "optimize")){
1713         rv = tclistnew2(1);
1714         const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
1715         if(!tcadboptimize(adb, params)){
1716           tclistdel(rv);
1717           rv = NULL;
1718         }
1719       } else if(!strcmp(name, "vanish")){
1720         rv = tclistnew2(1);
1721         if(!tcadbvanish(adb)){
1722           tclistdel(rv);
1723           rv = NULL;
1724         }
1725       } else if(!strcmp(name, "regex")){
1726         if(argc > 0){
1727           const char *regex = TCLISTVALPTR(args, 0);
1728           int options = REG_EXTENDED | REG_NOSUB;
1729           if(*regex == '*'){
1730             options |= REG_ICASE;
1731             regex++;
1732           }
1733           regex_t rbuf;
1734           if(regcomp(&rbuf, regex, options) == 0){
1735             rv = tclistnew();
1736             int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
1737             if(max < 1) max = INT_MAX;
1738             tcmdbiterinit(adb->mdb);
1739             char *kbuf;
1740             int ksiz;
1741             while(max > 0 && (kbuf = tcmdbiternext(adb->mdb, &ksiz))){
1742               if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
1743                 int vsiz;
1744                 char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz);
1745                 if(vbuf){
1746                   TCLISTPUSH(rv, kbuf, ksiz);
1747                   TCLISTPUSH(rv, vbuf, vsiz);
1748                   TCFREE(vbuf);
1749                   max--;
1750                 }
1751               }
1752               TCFREE(kbuf);
1753             }
1754             regfree(&rbuf);
1755           } else {
1756             rv = NULL;
1757           }
1758         } else {
1759           rv = NULL;
1760         }
1761       } else {
1762         rv = NULL;
1763       }
1764       break;
1765     case ADBONDB:
1766       if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){
1767         if(argc > 1){
1768           rv = tclistnew2(1);
1769           const char *kbuf;
1770           int ksiz;
1771           TCLISTVAL(kbuf, args, 0, ksiz);
1772           const char *vbuf;
1773           int vsiz;
1774           TCLISTVAL(vbuf, args, 1, vsiz);
1775           bool err = false;
1776           if(!strcmp(name, "put")){
1777             tcndbput(adb->ndb, kbuf, ksiz, vbuf, vsiz);
1778           } else if(!strcmp(name, "putkeep")){
1779             if(!tcndbputkeep(adb->ndb, kbuf, ksiz, vbuf, vsiz)) err = true;
1780           } else if(!strcmp(name, "putcat")){
1781             tcndbputcat(adb->ndb, kbuf, ksiz, vbuf, vsiz);
1782           }
1783           if(err){
1784             tclistdel(rv);
1785             rv = NULL;
1786           }
1787         } else {
1788           rv = NULL;
1789         }
1790       } else if(!strcmp(name, "out")){
1791         if(argc > 0){
1792           rv = tclistnew2(1);
1793           const char *kbuf;
1794           int ksiz;
1795           TCLISTVAL(kbuf, args, 0, ksiz);
1796           if(!tcndbout(adb->ndb, kbuf, ksiz)){
1797             tclistdel(rv);
1798             rv = NULL;
1799           }
1800         } else {
1801           rv = NULL;
1802         }
1803       } else if(!strcmp(name, "get")){
1804         if(argc > 0){
1805           rv = tclistnew2(1);
1806           const char *kbuf;
1807           int ksiz;
1808           TCLISTVAL(kbuf, args, 0, ksiz);
1809           int vsiz;
1810           char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz);
1811           if(vbuf){
1812             TCLISTPUSH(rv, vbuf, vsiz);
1813             TCFREE(vbuf);
1814           } else {
1815             tclistdel(rv);
1816             rv = NULL;
1817           }
1818         } else {
1819           rv = NULL;
1820         }
1821       } else if(!strcmp(name, "putlist")){
1822         rv = tclistnew2(1);
1823         argc--;
1824         for(int i = 0; i < argc; i += 2){
1825           const char *kbuf, *vbuf;
1826           int ksiz, vsiz;
1827           TCLISTVAL(kbuf, args, i, ksiz);
1828           TCLISTVAL(vbuf, args, i + 1, vsiz);
1829           tcndbput(adb->ndb, kbuf, ksiz, vbuf, vsiz);
1830         }
1831       } else if(!strcmp(name, "outlist")){
1832         rv = tclistnew2(1);
1833         for(int i = 0; i < argc; i++){
1834           const char *kbuf;
1835           int ksiz;
1836           TCLISTVAL(kbuf, args, i, ksiz);
1837           tcndbout(adb->ndb, kbuf, ksiz);
1838         }
1839       } else if(!strcmp(name, "getlist")){
1840         rv = tclistnew2(argc * 2);
1841         for(int i = 0; i < argc; i++){
1842           const char *kbuf;
1843           int ksiz;
1844           TCLISTVAL(kbuf, args, i, ksiz);
1845           int vsiz;
1846           char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz);
1847           if(vbuf){
1848             TCLISTPUSH(rv, kbuf, ksiz);
1849             TCLISTPUSH(rv, vbuf, vsiz);
1850             TCFREE(vbuf);
1851           }
1852         }
1853       } else if(!strcmp(name, "getpart")){
1854         if(argc > 0){
1855           const char *kbuf;
1856           int ksiz;
1857           TCLISTVAL(kbuf, args, 0, ksiz);
1858           int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
1859           if(off < 0) off = 0;
1860           if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
1861           int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
1862           if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
1863           int vsiz;
1864           char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz);
1865           if(vbuf){
1866             if(off < vsiz){
1867               rv = tclistnew2(1);
1868               vsiz -= off;
1869               if(vsiz > len) vsiz = len;
1870               if(off > 0) memmove(vbuf, vbuf + off, vsiz);
1871               tclistpushmalloc(rv, vbuf, vsiz);
1872             } else {
1873               rv = NULL;
1874               TCFREE(vbuf);
1875             }
1876           } else {
1877             rv = NULL;
1878           }
1879         } else {
1880           rv = NULL;
1881         }
1882       } else if(!strcmp(name, "iterinit")){
1883         rv = tclistnew2(1);
1884         if(argc > 0){
1885           const char *kbuf;
1886           int ksiz;
1887           TCLISTVAL(kbuf, args, 0, ksiz);
1888           tcndbiterinit2(adb->ndb, kbuf, ksiz);
1889         } else {
1890           tcndbiterinit(adb->ndb);
1891         }
1892       } else if(!strcmp(name, "iternext")){
1893         rv = tclistnew2(1);
1894         int ksiz;
1895         char *kbuf = tcndbiternext(adb->ndb, &ksiz);
1896         if(kbuf){
1897           TCLISTPUSH(rv, kbuf, ksiz);
1898           int vsiz;
1899           char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz);
1900           if(vbuf){
1901             TCLISTPUSH(rv, vbuf, vsiz);
1902             TCFREE(vbuf);
1903           }
1904           TCFREE(kbuf);
1905         } else {
1906           tclistdel(rv);
1907           rv = NULL;
1908         }
1909       } else if(!strcmp(name, "sync")){
1910         rv = tclistnew2(1);
1911         if(!tcadbsync(adb)){
1912           tclistdel(rv);
1913           rv = NULL;
1914         }
1915       } else if(!strcmp(name, "optimize")){
1916         rv = tclistnew2(1);
1917         const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
1918         if(!tcadboptimize(adb, params)){
1919           tclistdel(rv);
1920           rv = NULL;
1921         }
1922       } else if(!strcmp(name, "vanish")){
1923         rv = tclistnew2(1);
1924         if(!tcadbvanish(adb)){
1925           tclistdel(rv);
1926           rv = NULL;
1927         }
1928       } else if(!strcmp(name, "regex")){
1929         if(argc > 0){
1930           const char *regex = TCLISTVALPTR(args, 0);
1931           int options = REG_EXTENDED | REG_NOSUB;
1932           if(*regex == '*'){
1933             options |= REG_ICASE;
1934             regex++;
1935           }
1936           regex_t rbuf;
1937           if(regcomp(&rbuf, regex, options) == 0){
1938             rv = tclistnew();
1939             int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
1940             if(max < 1) max = INT_MAX;
1941             tcndbiterinit(adb->ndb);
1942             char *kbuf;
1943             int ksiz;
1944             while(max > 0 && (kbuf = tcndbiternext(adb->ndb, &ksiz))){
1945               if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
1946                 int vsiz;
1947                 char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz);
1948                 if(vbuf){
1949                   TCLISTPUSH(rv, kbuf, ksiz);
1950                   TCLISTPUSH(rv, vbuf, vsiz);
1951                   TCFREE(vbuf);
1952                   max--;
1953                 }
1954               }
1955               TCFREE(kbuf);
1956             }
1957             regfree(&rbuf);
1958           } else {
1959             rv = NULL;
1960           }
1961         } else {
1962           rv = NULL;
1963         }
1964       } else if(!strcmp(name, "range")){
1965         rv = tclistnew();
1966         int bksiz = 0;
1967         const char *bkbuf = NULL;
1968         if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz);
1969         int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
1970         if(max < 1) max = INT_MAX;
1971         int eksiz = 0;
1972         const char *ekbuf = NULL;
1973         if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz);
1974         if(bkbuf){
1975           tcndbiterinit2(adb->ndb, bkbuf, bksiz);
1976         } else {
1977           tcndbiterinit(adb->ndb);
1978         }
1979         char *kbuf;
1980         int ksiz;
1981         while(max > 0 && (kbuf = tcndbiternext(adb->ndb, &ksiz)) != NULL){
1982           if(ekbuf && tccmplexical(kbuf, ksiz, ekbuf, eksiz, NULL) >= 0){
1983             TCFREE(kbuf);
1984             break;
1985           }
1986           int vsiz;
1987           char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz);
1988           if(vbuf){
1989             TCLISTPUSH(rv, kbuf, ksiz);
1990             TCLISTPUSH(rv, vbuf, vsiz);
1991             TCFREE(vbuf);
1992             max--;
1993           }
1994           TCFREE(kbuf);
1995         }
1996       } else {
1997         rv = NULL;
1998       }
1999       break;
2000     case ADBOHDB:
2001       if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){
2002         if(argc > 1){
2003           rv = tclistnew2(1);
2004           const char *kbuf;
2005           int ksiz;
2006           TCLISTVAL(kbuf, args, 0, ksiz);
2007           const char *vbuf;
2008           int vsiz;
2009           TCLISTVAL(vbuf, args, 1, vsiz);
2010           bool err = false;
2011           if(!strcmp(name, "put")){
2012             if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2013           } else if(!strcmp(name, "putkeep")){
2014             if(!tchdbputkeep(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2015           } else if(!strcmp(name, "putcat")){
2016             if(!tchdbputcat(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2017           }
2018           if(err){
2019             tclistdel(rv);
2020             rv = NULL;
2021           }
2022         } else {
2023           rv = NULL;
2024         }
2025       } else if(!strcmp(name, "out")){
2026         if(argc > 0){
2027           rv = tclistnew2(1);
2028           const char *kbuf;
2029           int ksiz;
2030           TCLISTVAL(kbuf, args, 0, ksiz);
2031           if(!tchdbout(adb->hdb, kbuf, ksiz)){
2032             tclistdel(rv);
2033             rv = NULL;
2034           }
2035         } else {
2036           rv = NULL;
2037         }
2038       } else if(!strcmp(name, "get")){
2039         if(argc > 0){
2040           rv = tclistnew2(1);
2041           const char *kbuf;
2042           int ksiz;
2043           TCLISTVAL(kbuf, args, 0, ksiz);
2044           int vsiz;
2045           char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz);
2046           if(vbuf){
2047             TCLISTPUSH(rv, vbuf, vsiz);
2048             TCFREE(vbuf);
2049           } else {
2050             tclistdel(rv);
2051             rv = NULL;
2052           }
2053         } else {
2054           rv = NULL;
2055         }
2056       } else if(!strcmp(name, "putlist")){
2057         rv = tclistnew2(1);
2058         bool err = false;
2059         argc--;
2060         for(int i = 0; i < argc; i += 2){
2061           const char *kbuf;
2062           int ksiz;
2063           TCLISTVAL(kbuf, args, i, ksiz);
2064           int vsiz;
2065           const char *vbuf = tclistval(args, i + 1, &vsiz);
2066           if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)){
2067             err = true;
2068             break;
2069           }
2070         }
2071         if(err){
2072           tclistdel(rv);
2073           rv = NULL;
2074         }
2075       } else if(!strcmp(name, "outlist")){
2076         rv = tclistnew2(1);
2077         bool err = false;
2078         for(int i = 0; i < argc; i++){
2079           const char *kbuf;
2080           int ksiz;
2081           TCLISTVAL(kbuf, args, i, ksiz);
2082           if(!tchdbout(adb->hdb, kbuf, ksiz) && tchdbecode(adb->hdb) != TCENOREC){
2083             err = true;
2084             break;
2085           }
2086         }
2087         if(err){
2088           tclistdel(rv);
2089           rv = NULL;
2090         }
2091       } else if(!strcmp(name, "getlist")){
2092         rv = tclistnew2(argc * 2);
2093         bool err = false;
2094         for(int i = 0; i < argc; i++){
2095           const char *kbuf;
2096           int ksiz;
2097           TCLISTVAL(kbuf, args, i, ksiz);
2098           int vsiz;
2099           char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz);
2100           if(vbuf){
2101             TCLISTPUSH(rv, kbuf, ksiz);
2102             TCLISTPUSH(rv, vbuf, vsiz);
2103             TCFREE(vbuf);
2104           } else if(tchdbecode(adb->hdb) != TCENOREC){
2105             err = true;
2106           }
2107         }
2108         if(err){
2109           tclistdel(rv);
2110           rv = NULL;
2111         }
2112       } else if(!strcmp(name, "getpart")){
2113         if(argc > 0){
2114           const char *kbuf;
2115           int ksiz;
2116           TCLISTVAL(kbuf, args, 0, ksiz);
2117           int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2118           if(off < 0) off = 0;
2119           if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
2120           int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
2121           if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
2122           int vsiz;
2123           char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz);
2124           if(vbuf){
2125             if(off < vsiz){
2126               rv = tclistnew2(1);
2127               vsiz -= off;
2128               if(vsiz > len) vsiz = len;
2129               if(off > 0) memmove(vbuf, vbuf + off, vsiz);
2130               tclistpushmalloc(rv, vbuf, vsiz);
2131             } else {
2132               rv = NULL;
2133               TCFREE(vbuf);
2134             }
2135           } else {
2136             rv = NULL;
2137           }
2138         } else {
2139           rv = NULL;
2140         }
2141       } else if(!strcmp(name, "iterinit")){
2142         rv = tclistnew2(1);
2143         bool err = false;
2144         if(argc > 0){
2145           const char *kbuf;
2146           int ksiz;
2147           TCLISTVAL(kbuf, args, 0, ksiz);
2148           if(!tchdbiterinit2(adb->hdb, kbuf, ksiz)) err = true;
2149         } else {
2150           if(!tchdbiterinit(adb->hdb)) err = true;
2151         }
2152         if(err){
2153           tclistdel(rv);
2154           rv = NULL;
2155         }
2156       } else if(!strcmp(name, "iternext")){
2157         rv = tclistnew2(1);
2158         int ksiz;
2159         char *kbuf = tchdbiternext(adb->hdb, &ksiz);
2160         if(kbuf){
2161           TCLISTPUSH(rv, kbuf, ksiz);
2162           int vsiz;
2163           char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz);
2164           if(vbuf){
2165             TCLISTPUSH(rv, vbuf, vsiz);
2166             TCFREE(vbuf);
2167           }
2168           TCFREE(kbuf);
2169         } else {
2170           tclistdel(rv);
2171           rv = NULL;
2172         }
2173       } else if(!strcmp(name, "sync")){
2174         rv = tclistnew2(1);
2175         if(!tcadbsync(adb)){
2176           tclistdel(rv);
2177           rv = NULL;
2178         }
2179       } else if(!strcmp(name, "optimize")){
2180         rv = tclistnew2(1);
2181         const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
2182         if(!tcadboptimize(adb, params)){
2183           tclistdel(rv);
2184           rv = NULL;
2185         }
2186       } else if(!strcmp(name, "vanish")){
2187         rv = tclistnew2(1);
2188         if(!tcadbvanish(adb)){
2189           tclistdel(rv);
2190           rv = NULL;
2191         }
2192       } else if(!strcmp(name, "error")){
2193         rv = tclistnew2(1);
2194         int ecode = tchdbecode(adb->hdb);
2195         tclistprintf(rv, "%d: %s", ecode, tchdberrmsg(ecode));
2196         uint8_t flags = tchdbflags(adb->hdb);
2197         if(flags & HDBFFATAL) tclistprintf(rv, "fatal");
2198       } else if(!strcmp(name, "defrag")){
2199         rv = tclistnew2(1);
2200         int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1;
2201         if(!tchdbdefrag(adb->hdb, step)){
2202           tclistdel(rv);
2203           rv = NULL;
2204         }
2205       } else if(!strcmp(name, "cacheclear")){
2206         rv = tclistnew2(1);
2207         if(!tchdbcacheclear(adb->hdb)){
2208           tclistdel(rv);
2209           rv = NULL;
2210         }
2211       } else if(!strcmp(name, "regex")){
2212         if(argc > 0){
2213           const char *regex = TCLISTVALPTR(args, 0);
2214           int options = REG_EXTENDED | REG_NOSUB;
2215           if(*regex == '*'){
2216             options |= REG_ICASE;
2217             regex++;
2218           }
2219           regex_t rbuf;
2220           if(regcomp(&rbuf, regex, options) == 0){
2221             rv = tclistnew();
2222             int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2223             if(max < 1) max = INT_MAX;
2224             tchdbiterinit(adb->hdb);
2225             TCXSTR *kxstr = tcxstrnew();
2226             TCXSTR *vxstr = tcxstrnew();
2227             while(max > 0 && tchdbiternext3(adb->hdb, kxstr, vxstr)){
2228               const char *kbuf = TCXSTRPTR(kxstr);
2229               if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
2230                 TCLISTPUSH(rv, kbuf, TCXSTRSIZE(kxstr));
2231                 TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr));
2232                 max--;
2233               }
2234             }
2235             tcxstrdel(vxstr);
2236             tcxstrdel(kxstr);
2237             regfree(&rbuf);
2238           } else {
2239             rv = NULL;
2240           }
2241         } else {
2242           rv = NULL;
2243         }
2244       } else {
2245         rv = NULL;
2246       }
2247       break;
2248     case ADBOBDB:
2249       if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat") ||
2250          !strcmp(name, "putdup") || !strcmp(name, "putdupback")){
2251         if(argc > 1){
2252           rv = tclistnew2(1);
2253           const char *kbuf;
2254           int ksiz;
2255           TCLISTVAL(kbuf, args, 0, ksiz);
2256           const char *vbuf;
2257           int vsiz;
2258           TCLISTVAL(vbuf, args, 1, vsiz);
2259           bool err = false;
2260           if(!strcmp(name, "put")){
2261             if(!tcbdbput(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2262           } else if(!strcmp(name, "putkeep")){
2263             if(!tcbdbputkeep(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2264           } else if(!strcmp(name, "putcat")){
2265             if(!tcbdbputcat(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2266           } else if(!strcmp(name, "putdup")){
2267             if(!tcbdbputdup(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2268           } else if(!strcmp(name, "putdupback")){
2269             if(!tcbdbputdupback(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2270           }
2271           if(err){
2272             tclistdel(rv);
2273             rv = NULL;
2274           }
2275         } else {
2276           rv = NULL;
2277         }
2278       } else if(!strcmp(name, "out")){
2279         if(argc > 0){
2280           rv = tclistnew2(1);
2281           const char *kbuf;
2282           int ksiz;
2283           TCLISTVAL(kbuf, args, 0, ksiz);
2284           if(!tcbdbout(adb->bdb, kbuf, ksiz)){
2285             tclistdel(rv);
2286             rv = NULL;
2287           }
2288         } else {
2289           rv = NULL;
2290         }
2291       } else if(!strcmp(name, "get")){
2292         if(argc > 0){
2293           rv = tclistnew2(1);
2294           const char *kbuf;
2295           int ksiz;
2296           TCLISTVAL(kbuf, args, 0, ksiz);
2297           TCLIST *vals = tcbdbget4(adb->bdb, kbuf, ksiz);
2298           if(vals){
2299             tclistdel(rv);
2300             rv = vals;
2301           } else {
2302             tclistdel(rv);
2303             rv = NULL;
2304           }
2305         } else {
2306           rv = NULL;
2307         }
2308       } else if(!strcmp(name, "putlist")){
2309         rv = tclistnew2(1);
2310         bool err = false;
2311         argc--;
2312         for(int i = 0; i < argc; i += 2){
2313           const char *kbuf, *vbuf;
2314           int ksiz, vsiz;
2315           TCLISTVAL(kbuf, args, i, ksiz);
2316           TCLISTVAL(vbuf, args, i + 1, vsiz);
2317           if(!tcbdbputdup(adb->bdb, kbuf, ksiz, vbuf, vsiz)){
2318             err = true;
2319             break;
2320           }
2321         }
2322         if(err){
2323           tclistdel(rv);
2324           rv = NULL;
2325         }
2326       } else if(!strcmp(name, "outlist")){
2327         rv = tclistnew2(1);
2328         bool err = false;
2329         for(int i = 0; i < argc; i++){
2330           const char *kbuf;
2331           int ksiz;
2332           TCLISTVAL(kbuf, args, i, ksiz);
2333           if(!tcbdbout3(adb->bdb, kbuf, ksiz) && tcbdbecode(adb->bdb) != TCENOREC){
2334             err = true;
2335             break;
2336           }
2337         }
2338         if(err){
2339           tclistdel(rv);
2340           rv = NULL;
2341         }
2342       } else if(!strcmp(name, "getlist")){
2343         rv = tclistnew2(argc * 2);
2344         bool err = false;
2345         for(int i = 0; i < argc; i++){
2346           const char *kbuf;
2347           int ksiz;
2348           TCLISTVAL(kbuf, args, i, ksiz);
2349           TCLIST *vals = tcbdbget4(adb->bdb, kbuf, ksiz);
2350           if(vals){
2351             int vnum = TCLISTNUM(vals);
2352             for(int j = 0; j < vnum; j++){
2353               TCLISTPUSH(rv, kbuf, ksiz);
2354               const char *vbuf;
2355               int vsiz;
2356               TCLISTVAL(vbuf, vals, j, vsiz);
2357               TCLISTPUSH(rv, vbuf, vsiz);
2358             }
2359             tclistdel(vals);
2360           } else if(tcbdbecode(adb->bdb) != TCENOREC){
2361             err = true;
2362           }
2363         }
2364         if(err){
2365           tclistdel(rv);
2366           rv = NULL;
2367         }
2368       } else if(!strcmp(name, "getpart")){
2369         if(argc > 0){
2370           const char *kbuf;
2371           int ksiz;
2372           TCLISTVAL(kbuf, args, 0, ksiz);
2373           int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2374           if(off < 0) off = 0;
2375           if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
2376           int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
2377           if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
2378           int vsiz;
2379           char *vbuf = tcbdbget(adb->bdb, kbuf, ksiz, &vsiz);
2380           if(vbuf){
2381             if(off < vsiz){
2382               rv = tclistnew2(1);
2383               vsiz -= off;
2384               if(vsiz > len) vsiz = len;
2385               if(off > 0) memmove(vbuf, vbuf + off, vsiz);
2386               tclistpushmalloc(rv, vbuf, vsiz);
2387             } else {
2388               rv = NULL;
2389               TCFREE(vbuf);
2390             }
2391           } else {
2392             rv = NULL;
2393           }
2394         } else {
2395           rv = NULL;
2396         }
2397       } else if(!strcmp(name, "iterinit")){
2398         rv = tclistnew2(1);
2399         bool err = false;
2400         if(argc > 0){
2401           const char *kbuf;
2402           int ksiz;
2403           TCLISTVAL(kbuf, args, 0, ksiz);
2404           if(!tcbdbcurjump(adb->cur, kbuf, ksiz)) err = true;
2405         } else {
2406           if(!tcbdbcurfirst(adb->cur)) err = true;
2407         }
2408         if(err){
2409           tclistdel(rv);
2410           rv = NULL;
2411         }
2412       } else if(!strcmp(name, "iternext")){
2413         rv = tclistnew2(1);
2414         int ksiz;
2415         const char *kbuf = tcbdbcurkey3(adb->cur, &ksiz);
2416         if(kbuf){
2417           TCLISTPUSH(rv, kbuf, ksiz);
2418           int vsiz;
2419           const char *vbuf = tcbdbcurval3(adb->cur, &vsiz);
2420           if(vbuf) TCLISTPUSH(rv, vbuf, vsiz);
2421           tcbdbcurnext(adb->cur);
2422         } else {
2423           tclistdel(rv);
2424           rv = NULL;
2425         }
2426       } else if(!strcmp(name, "sync")){
2427         rv = tclistnew2(1);
2428         if(!tcadbsync(adb)){
2429           tclistdel(rv);
2430           rv = NULL;
2431         }
2432       } else if(!strcmp(name, "optimize")){
2433         rv = tclistnew2(1);
2434         const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
2435         if(!tcadboptimize(adb, params)){
2436           tclistdel(rv);
2437           rv = NULL;
2438         }
2439       } else if(!strcmp(name, "vanish")){
2440         rv = tclistnew2(1);
2441         if(!tcadbvanish(adb)){
2442           tclistdel(rv);
2443           rv = NULL;
2444         }
2445       } else if(!strcmp(name, "error")){
2446         rv = tclistnew2(1);
2447         int ecode = tcbdbecode(adb->bdb);
2448         tclistprintf(rv, "%d: %s", ecode, tcbdberrmsg(ecode));
2449         uint8_t flags = tcbdbflags(adb->bdb);
2450         if(flags & BDBFFATAL) tclistprintf(rv, "fatal");
2451       } else if(!strcmp(name, "defrag")){
2452         rv = tclistnew2(1);
2453         int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1;
2454         if(!tcbdbdefrag(adb->bdb, step)){
2455           tclistdel(rv);
2456           rv = NULL;
2457         }
2458       } else if(!strcmp(name, "cacheclear")){
2459         rv = tclistnew2(1);
2460         if(!tcbdbcacheclear(adb->bdb)){
2461           tclistdel(rv);
2462           rv = NULL;
2463         }
2464       } else if(!strcmp(name, "regex")){
2465         if(argc > 0){
2466           const char *regex = TCLISTVALPTR(args, 0);
2467           int options = REG_EXTENDED | REG_NOSUB;
2468           if(*regex == '*'){
2469             options |= REG_ICASE;
2470             regex++;
2471           }
2472           regex_t rbuf;
2473           if(regcomp(&rbuf, regex, options) == 0){
2474             rv = tclistnew();
2475             int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2476             if(max < 1) max = INT_MAX;
2477             BDBCUR *cur = tcbdbcurnew(adb->bdb);
2478             tcbdbcurfirst(cur);
2479             TCXSTR *kxstr = tcxstrnew();
2480             TCXSTR *vxstr = tcxstrnew();
2481             while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){
2482               const char *kbuf = TCXSTRPTR(kxstr);
2483               if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
2484                 TCLISTPUSH(rv, kbuf, TCXSTRSIZE(kxstr));
2485                 TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr));
2486                 max--;
2487               }
2488               tcbdbcurnext(cur);
2489             }
2490             tcxstrdel(vxstr);
2491             tcxstrdel(kxstr);
2492             tcbdbcurdel(cur);
2493             regfree(&rbuf);
2494           } else {
2495             rv = NULL;
2496           }
2497         } else {
2498           rv = NULL;
2499         }
2500       } else if(!strcmp(name, "range")){
2501         rv = tclistnew();
2502         int bksiz = 0;
2503         const char *bkbuf = NULL;
2504         if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz);
2505         int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2506         if(max < 1) max = INT_MAX;
2507         int eksiz = 0;
2508         const char *ekbuf = NULL;
2509         if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz);
2510         TCCMP cmp = tcbdbcmpfunc(adb->bdb);
2511         void *cmpop = tcbdbcmpop(adb->bdb);
2512         BDBCUR *cur = tcbdbcurnew(adb->bdb);
2513         if(bkbuf){
2514           tcbdbcurjump(cur, bkbuf, bksiz);
2515         } else {
2516           tcbdbcurfirst(cur);
2517         }
2518         TCXSTR *kxstr = tcxstrnew();
2519         TCXSTR *vxstr = tcxstrnew();
2520         while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){
2521           const char *kbuf = TCXSTRPTR(kxstr);
2522           int ksiz = TCXSTRSIZE(kxstr);
2523           if(ekbuf && cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) >= 0) break;
2524           TCLISTPUSH(rv, kbuf, ksiz);
2525           TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr));
2526           max--;
2527           tcbdbcurnext(cur);
2528         }
2529         tcxstrdel(vxstr);
2530         tcxstrdel(kxstr);
2531         tcbdbcurdel(cur);
2532       } else {
2533         rv = NULL;
2534       }
2535       break;
2536     case ADBOFDB:
2537       if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){
2538         if(argc > 1){
2539           rv = tclistnew2(1);
2540           const char *kbuf, *vbuf;
2541           int ksiz, vsiz;
2542           TCLISTVAL(kbuf, args, 0, ksiz);
2543           TCLISTVAL(vbuf, args, 1, vsiz);
2544           bool err = false;
2545           if(!strcmp(name, "put")){
2546             if(!tcfdbput2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2547           } else if(!strcmp(name, "putkeep")){
2548             if(!tcfdbputkeep2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2549           } else if(!strcmp(name, "putcat")){
2550             if(!tcfdbputcat2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true;
2551           }
2552           if(err){
2553             tclistdel(rv);
2554             rv = NULL;
2555           }
2556         } else {
2557           rv = NULL;
2558         }
2559       } else if(!strcmp(name, "out")){
2560         if(argc > 0){
2561           rv = tclistnew2(1);
2562           const char *kbuf;
2563           int ksiz;
2564           TCLISTVAL(kbuf, args, 0, ksiz);
2565           if(!tcfdbout2(adb->fdb, kbuf, ksiz)){
2566             tclistdel(rv);
2567             rv = NULL;
2568           }
2569         } else {
2570           rv = NULL;
2571         }
2572       } else if(!strcmp(name, "get")){
2573         if(argc > 0){
2574           rv = tclistnew2(1);
2575           const char *kbuf;
2576           int ksiz;
2577           TCLISTVAL(kbuf, args, 0, ksiz);
2578           int vsiz;
2579           char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz);
2580           if(vbuf){
2581             TCLISTPUSH(rv, vbuf, vsiz);
2582             TCFREE(vbuf);
2583           } else {
2584             tclistdel(rv);
2585             rv = NULL;
2586           }
2587         } else {
2588           rv = NULL;
2589         }
2590       } else if(!strcmp(name, "putlist")){
2591         rv = tclistnew2(1);
2592         bool err = false;
2593         argc--;
2594         for(int i = 0; i < argc; i += 2){
2595           const char *kbuf, *vbuf;
2596           int ksiz, vsiz;
2597           TCLISTVAL(kbuf, args, i, ksiz);
2598           TCLISTVAL(vbuf, args, i + 1, vsiz);
2599           if(!tcfdbput2(adb->fdb, kbuf, ksiz, vbuf, vsiz)){
2600             err = true;
2601             break;
2602           }
2603         }
2604         if(err){
2605           tclistdel(rv);
2606           rv = NULL;
2607         }
2608       } else if(!strcmp(name, "outlist")){
2609         rv = tclistnew2(1);
2610         bool err = false;
2611         for(int i = 0; i < argc; i++){
2612           const char *kbuf;
2613           int ksiz;
2614           TCLISTVAL(kbuf, args, i, ksiz);
2615           if(!tcfdbout2(adb->fdb, kbuf, ksiz) && tcfdbecode(adb->fdb) != TCENOREC){
2616             err = true;
2617             break;
2618           }
2619         }
2620         if(err){
2621           tclistdel(rv);
2622           rv = NULL;
2623         }
2624       } else if(!strcmp(name, "getlist")){
2625         rv = tclistnew2(argc * 2);
2626         bool err = false;
2627         for(int i = 0; i < argc; i++){
2628           const char *kbuf;
2629           int ksiz;
2630           TCLISTVAL(kbuf, args, i, ksiz);
2631           int vsiz;
2632           char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz);
2633           if(vbuf){
2634             TCLISTPUSH(rv, kbuf, ksiz);
2635             TCLISTPUSH(rv, vbuf, vsiz);
2636             TCFREE(vbuf);
2637           } else if(tcfdbecode(adb->fdb) != TCENOREC){
2638             err = true;
2639           }
2640         }
2641         if(err){
2642           tclistdel(rv);
2643           rv = NULL;
2644         }
2645       } else if(!strcmp(name, "getpart")){
2646         if(argc > 0){
2647           const char *kbuf;
2648           int ksiz;
2649           TCLISTVAL(kbuf, args, 0, ksiz);
2650           int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2651           if(off < 0) off = 0;
2652           if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
2653           int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
2654           if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
2655           int vsiz;
2656           char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz);
2657           if(vbuf){
2658             if(off < vsiz){
2659               rv = tclistnew2(1);
2660               vsiz -= off;
2661               if(vsiz > len) vsiz = len;
2662               if(off > 0) memmove(vbuf, vbuf + off, vsiz);
2663               tclistpushmalloc(rv, vbuf, vsiz);
2664             } else {
2665               rv = NULL;
2666               TCFREE(vbuf);
2667             }
2668           } else {
2669             rv = NULL;
2670           }
2671         } else {
2672           rv = NULL;
2673         }
2674       } else if(!strcmp(name, "iterinit")){
2675         rv = tclistnew2(1);
2676         bool err = false;
2677         if(argc > 0){
2678           const char *kbuf;
2679           int ksiz;
2680           TCLISTVAL(kbuf, args, 0, ksiz);
2681           if(!tcfdbiterinit3(adb->fdb, kbuf, ksiz)) err = true;
2682         } else {
2683           if(!tcfdbiterinit(adb->fdb)) err = true;
2684         }
2685         if(err){
2686           tclistdel(rv);
2687           rv = NULL;
2688         }
2689       } else if(!strcmp(name, "iternext")){
2690         rv = tclistnew2(1);
2691         int ksiz;
2692         char *kbuf = tcfdbiternext2(adb->fdb, &ksiz);
2693         if(kbuf){
2694           TCLISTPUSH(rv, kbuf, ksiz);
2695           int vsiz;
2696           char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz);
2697           if(vbuf){
2698             TCLISTPUSH(rv, vbuf, vsiz);
2699             TCFREE(vbuf);
2700           }
2701           TCFREE(kbuf);
2702         } else {
2703           tclistdel(rv);
2704           rv = NULL;
2705         }
2706       } else if(!strcmp(name, "sync")){
2707         rv = tclistnew2(1);
2708         if(!tcadbsync(adb)){
2709           tclistdel(rv);
2710           rv = NULL;
2711         }
2712       } else if(!strcmp(name, "optimize")){
2713         rv = tclistnew2(1);
2714         const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
2715         if(!tcadboptimize(adb, params)){
2716           tclistdel(rv);
2717           rv = NULL;
2718         }
2719       } else if(!strcmp(name, "vanish")){
2720         rv = tclistnew2(1);
2721         if(!tcadbvanish(adb)){
2722           tclistdel(rv);
2723           rv = NULL;
2724         }
2725       } else if(!strcmp(name, "error")){
2726         rv = tclistnew2(1);
2727         int ecode = tcfdbecode(adb->fdb);
2728         tclistprintf(rv, "%d: %s", ecode, tcfdberrmsg(ecode));
2729         uint8_t flags = tcfdbflags(adb->fdb);
2730         if(flags & FDBFFATAL) tclistprintf(rv, "fatal");
2731       } else if(!strcmp(name, "regex")){
2732         if(argc > 0){
2733           const char *regex = TCLISTVALPTR(args, 0);
2734           int options = REG_EXTENDED | REG_NOSUB;
2735           if(*regex == '*'){
2736             options |= REG_ICASE;
2737             regex++;
2738           }
2739           regex_t rbuf;
2740           if(regcomp(&rbuf, regex, options) == 0){
2741             rv = tclistnew();
2742             int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2743             if(max < 1) max = INT_MAX;
2744             tcfdbiterinit(adb->fdb);
2745             char *kbuf;
2746             int ksiz;
2747             while(max > 0 && (kbuf = tcfdbiternext2(adb->fdb, &ksiz))){
2748               if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
2749                 int vsiz;
2750                 char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz);
2751                 if(vbuf){
2752                   TCLISTPUSH(rv, kbuf, ksiz);
2753                   TCLISTPUSH(rv, vbuf, vsiz);
2754                   TCFREE(vbuf);
2755                   max--;
2756                 }
2757               }
2758               TCFREE(kbuf);
2759             }
2760             regfree(&rbuf);
2761           } else {
2762             rv = NULL;
2763           }
2764         } else {
2765           rv = NULL;
2766         }
2767       } else if(!strcmp(name, "range")){
2768         rv = tclistnew();
2769         int bksiz = 0;
2770         const char *bkbuf = NULL;
2771         if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz);
2772         int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2773         if(max < 1) max = INT_MAX;
2774         int eksiz = 0;
2775         const char *ekbuf = NULL;
2776         if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz);
2777         if(bkbuf){
2778           tcfdbiterinit3(adb->fdb, bkbuf, bksiz);
2779         } else {
2780           tcfdbiterinit(adb->fdb);
2781         }
2782         int64_t eid = ekbuf ? tcfdbkeytoid(ekbuf, eksiz) : -1;
2783         char *kbuf;
2784         int ksiz;
2785         while(max > 0 && (kbuf = tcfdbiternext2(adb->fdb, &ksiz)) != NULL){
2786           if(eid > 0 && tcatoi(kbuf) >= eid){
2787             TCFREE(kbuf);
2788             break;
2789           }
2790           int vsiz;
2791           char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz);
2792           if(vbuf){
2793             TCLISTPUSH(rv, kbuf, ksiz);
2794             TCLISTPUSH(rv, vbuf, vsiz);
2795             TCFREE(vbuf);
2796             max--;
2797           }
2798           TCFREE(kbuf);
2799         }
2800       } else {
2801         rv = NULL;
2802       }
2803       break;
2804     case ADBOTDB:
2805       if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){
2806         if(argc > 0){
2807           rv = tclistnew2(1);
2808           char *pkbuf;
2809           int pksiz;
2810           TCLISTVAL(pkbuf, args, 0, pksiz);
2811           argc--;
2812           TCMAP *cols = tcmapnew2(argc);
2813           for(int i = 1; i < argc; i += 2){
2814             const char *kbuf, *vbuf;
2815             int ksiz, vsiz;
2816             TCLISTVAL(kbuf, args, i, ksiz);
2817             TCLISTVAL(vbuf, args, i + 1, vsiz);
2818             tcmapput(cols, kbuf, ksiz, vbuf, vsiz);
2819           }
2820           bool err = false;
2821           if(!strcmp(name, "put")){
2822             if(!tctdbput(adb->tdb, pkbuf, pksiz, cols)) err = true;
2823           } else if(!strcmp(name, "putkeep")){
2824             if(!tctdbputkeep(adb->tdb, pkbuf, pksiz, cols)) err = true;
2825           } else if(!strcmp(name, "putcat")){
2826             if(!tctdbputcat(adb->tdb, pkbuf, pksiz, cols)) err = true;
2827           }
2828           tcmapdel(cols);
2829           if(err){
2830             tclistdel(rv);
2831             rv = NULL;
2832           }
2833         } else {
2834           rv = NULL;
2835         }
2836       } else if(!strcmp(name, "out")){
2837         if(argc > 0){
2838           rv = tclistnew2(1);
2839           char *pkbuf;
2840           int pksiz;
2841           TCLISTVAL(pkbuf, args, 0, pksiz);
2842           if(!tctdbout(adb->tdb, pkbuf, pksiz)){
2843             tclistdel(rv);
2844             rv = NULL;
2845           }
2846         } else {
2847           rv = NULL;
2848         }
2849       } else if(!strcmp(name, "get")){
2850         if(argc > 0){
2851           rv = tclistnew2(1);
2852           char *pkbuf;
2853           int pksiz;
2854           TCLISTVAL(pkbuf, args, 0, pksiz);
2855           TCMAP *cols = tctdbget(adb->tdb, pkbuf, pksiz);
2856           if(cols){
2857             tcmapiterinit(cols);
2858             const char *kbuf;
2859             int ksiz;
2860             while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){
2861               int vsiz;
2862               const char *vbuf = tcmapiterval(kbuf, &vsiz);
2863               TCLISTPUSH(rv, kbuf, ksiz);
2864               TCLISTPUSH(rv, vbuf, vsiz);
2865             }
2866             tcmapdel(cols);
2867           } else {
2868             tclistdel(rv);
2869             rv = NULL;
2870           }
2871         } else {
2872           rv = NULL;
2873         }
2874       } else if(!strcmp(name, "putlist")){
2875         rv = tclistnew2(1);
2876         bool err = false;
2877         argc--;
2878         for(int i = 0; i < argc; i += 2){
2879           const char *kbuf, *vbuf;
2880           int ksiz, vsiz;
2881           TCLISTVAL(kbuf, args, i, ksiz);
2882           TCLISTVAL(vbuf, args, i + 1, vsiz);
2883           if(!tctdbput2(adb->tdb, kbuf, ksiz, vbuf, vsiz)){
2884             err = true;
2885             break;
2886           }
2887         }
2888         if(err){
2889           tclistdel(rv);
2890           rv = NULL;
2891         }
2892       } else if(!strcmp(name, "outlist")){
2893         rv = tclistnew2(1);
2894         bool err = false;
2895         for(int i = 0; i < argc; i++){
2896           const char *kbuf;
2897           int ksiz;
2898           TCLISTVAL(kbuf, args, i, ksiz);
2899           if(!tctdbout(adb->tdb, kbuf, ksiz) && tctdbecode(adb->tdb) != TCENOREC){
2900             err = true;
2901             break;
2902           }
2903         }
2904         if(err){
2905           tclistdel(rv);
2906           rv = NULL;
2907         }
2908       } else if(!strcmp(name, "getlist")){
2909         rv = tclistnew2(argc * 2);
2910         bool err = false;
2911         for(int i = 0; i < argc; i++){
2912           const char *kbuf;
2913           int ksiz;
2914           TCLISTVAL(kbuf, args, i, ksiz);
2915           int vsiz;
2916           char *vbuf = tctdbget2(adb->tdb, kbuf, ksiz, &vsiz);
2917           if(vbuf){
2918             TCLISTPUSH(rv, kbuf, ksiz);
2919             TCLISTPUSH(rv, vbuf, vsiz);
2920             TCFREE(vbuf);
2921           } else if(tctdbecode(adb->tdb) != TCENOREC){
2922             err = true;
2923           }
2924         }
2925         if(err){
2926           tclistdel(rv);
2927           rv = NULL;
2928         }
2929       } else if(!strcmp(name, "getpart")){
2930         if(argc > 0){
2931           const char *kbuf;
2932           int ksiz;
2933           TCLISTVAL(kbuf, args, 0, ksiz);
2934           int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
2935           if(off < 0) off = 0;
2936           if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
2937           int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
2938           if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
2939           int vsiz;
2940           char *vbuf = tctdbget2(adb->tdb, kbuf, ksiz, &vsiz);
2941           if(vbuf){
2942             if(off < vsiz){
2943               rv = tclistnew2(1);
2944               vsiz -= off;
2945               if(vsiz > len) vsiz = len;
2946               if(off > 0) memmove(vbuf, vbuf + off, vsiz);
2947               tclistpushmalloc(rv, vbuf, vsiz);
2948             } else {
2949               rv = NULL;
2950               TCFREE(vbuf);
2951             }
2952           } else {
2953             rv = NULL;
2954           }
2955         } else {
2956           rv = NULL;
2957         }
2958       } else if(!strcmp(name, "iterinit")){
2959         rv = tclistnew2(1);
2960         bool err = false;
2961         if(argc > 0){
2962           const char *pkbuf;
2963           int pksiz;
2964           TCLISTVAL(pkbuf, args, 0, pksiz);
2965           if(!tctdbiterinit2(adb->tdb, pkbuf, pksiz)) err = true;
2966         } else {
2967           if(!tctdbiterinit(adb->tdb)) err = true;
2968         }
2969         if(err){
2970           tclistdel(rv);
2971           rv = NULL;
2972         }
2973       } else if(!strcmp(name, "iternext")){
2974         rv = tclistnew2(1);
2975         int pksiz;
2976         char *pkbuf = tctdbiternext(adb->tdb, &pksiz);
2977         if(pkbuf){
2978           TCLISTPUSH(rv, pkbuf, pksiz);
2979           int csiz;
2980           char *cbuf = tctdbget2(adb->tdb, pkbuf, pksiz, &csiz);
2981           if(cbuf){
2982             TCLISTPUSH(rv, cbuf, csiz);
2983             TCFREE(cbuf);
2984           }
2985           TCFREE(pkbuf);
2986         } else {
2987           tclistdel(rv);
2988           rv = NULL;
2989         }
2990       } else if(!strcmp(name, "sync")){
2991         rv = tclistnew2(1);
2992         if(!tcadbsync(adb)){
2993           tclistdel(rv);
2994           rv = NULL;
2995         }
2996       } else if(!strcmp(name, "optimize")){
2997         rv = tclistnew2(1);
2998         const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
2999         if(!tcadboptimize(adb, params)){
3000           tclistdel(rv);
3001           rv = NULL;
3002         }
3003       } else if(!strcmp(name, "vanish")){
3004         rv = tclistnew2(1);
3005         if(!tcadbvanish(adb)){
3006           tclistdel(rv);
3007           rv = NULL;
3008         }
3009       } else if(!strcmp(name, "error")){
3010         rv = tclistnew2(1);
3011         int ecode = tctdbecode(adb->tdb);
3012         tclistprintf(rv, "%d: %s", ecode, tctdberrmsg(ecode));
3013         uint8_t flags = tctdbflags(adb->tdb);
3014         if(flags & TDBFFATAL) tclistprintf(rv, "fatal");
3015       } else if(!strcmp(name, "defrag")){
3016         rv = tclistnew2(1);
3017         int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1;
3018         if(!tctdbdefrag(adb->tdb, step)){
3019           tclistdel(rv);
3020           rv = NULL;
3021         }
3022       } else if(!strcmp(name, "cacheclear")){
3023         rv = tclistnew2(1);
3024         if(!tctdbcacheclear(adb->tdb)){
3025           tclistdel(rv);
3026           rv = NULL;
3027         }
3028       } else if(!strcmp(name, "regex")){
3029         if(argc > 0){
3030           const char *regex = TCLISTVALPTR(args, 0);
3031           int options = REG_EXTENDED | REG_NOSUB;
3032           if(*regex == '*'){
3033             options |= REG_ICASE;
3034             regex++;
3035           }
3036           regex_t rbuf;
3037           if(regcomp(&rbuf, regex, options) == 0){
3038             rv = tclistnew();
3039             int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
3040             if(max < 1) max = INT_MAX;
3041             tctdbiterinit(adb->tdb);
3042             char *kbuf;
3043             int ksiz;
3044             while(max > 0 && (kbuf = tctdbiternext(adb->tdb, &ksiz))){
3045               if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
3046                 int vsiz;
3047                 char *vbuf = tctdbget2(adb->tdb, kbuf, ksiz, &vsiz);
3048                 if(vbuf){
3049                   TCLISTPUSH(rv, kbuf, ksiz);
3050                   TCLISTPUSH(rv, vbuf, vsiz);
3051                   TCFREE(vbuf);
3052                   max--;
3053                 }
3054               }
3055               TCFREE(kbuf);
3056             }
3057             regfree(&rbuf);
3058           } else {
3059             rv = NULL;
3060           }
3061         } else {
3062           rv = NULL;
3063         }
3064       } else if(!strcmp(name, "setindex")){
3065         rv = tclistnew2(1);
3066         bool err = false;
3067         argc--;
3068         for(int i = 0; i < argc; i += 2){
3069           const char *kbuf, *vbuf;
3070           kbuf = TCLISTVALPTR(args, i);
3071           vbuf = TCLISTVALPTR(args, i + 1);
3072           int type = tctdbstrtoindextype(vbuf);
3073           if(type >= 0){
3074             if(!tctdbsetindex(adb->tdb, kbuf, type)) err = true;
3075           } else {
3076             err = true;
3077           }
3078         }
3079         if(err){
3080           tclistdel(rv);
3081           rv = NULL;
3082         }
3083       } else if(!strcmp(name, "search") || !strcmp(name, "metasearch")){
3084         bool toout = false;
3085         bool tocnt = false;
3086         bool tohint = false;
3087         TDBQRY *qry = tctdbqrynew(adb->tdb);
3088         TDBQRY **qrys = NULL;
3089         int qnum = 0;
3090         int mstype = TDBMSUNION;
3091         TCLIST *cnames = NULL;
3092         for(int i = 0; i < argc; i++){
3093           const char *arg;
3094           int asiz;
3095           TCLISTVAL(arg, args, i, asiz);
3096           TCLIST *tokens = tcstrsplit2(arg, asiz);
3097           int tnum = TCLISTNUM(tokens);
3098           if(tnum > 0){
3099             const char *cmd = TCLISTVALPTR(tokens, 0);
3100             if((!strcmp(cmd, "addcond") || !strcmp(cmd, "cond")) && tnum > 3){
3101               const char *name = TCLISTVALPTR(tokens, 1);
3102               const char *opstr = TCLISTVALPTR(tokens, 2);
3103               const char *expr = TCLISTVALPTR(tokens, 3);
3104               int op = tctdbqrystrtocondop(opstr);
3105               if(op >= 0) tctdbqryaddcond(qry, name, op, expr);
3106             } else if((!strcmp(cmd, "setorder") || !strcmp(cmd, "order")) && tnum > 2){
3107               const char *name = TCLISTVALPTR(tokens, 1);
3108               const char *typestr = TCLISTVALPTR(tokens, 2);
3109               int type = tctdbqrystrtoordertype(typestr);
3110               if(type >= 0) tctdbqrysetorder(qry, name, type);
3111             } else if((!strcmp(cmd, "setlimit") || !strcmp(cmd, "limit") ||
3112                        !strcmp(cmd, "setmax") || !strcmp(cmd, "max") ) && tnum > 1){
3113               const char *maxstr = TCLISTVALPTR(tokens, 1);
3114               int max = tcatoi(maxstr);
3115               int skip = 0;
3116               if(tnum > 2){
3117                 maxstr = TCLISTVALPTR(tokens, 2);
3118                 skip = tcatoi(maxstr);
3119               }
3120               tctdbqrysetlimit(qry, max, skip);
3121             } else if(!strcmp(cmd, "get") || !strcmp(cmd, "columns")){
3122               if(!cnames) cnames = tclistnew();
3123               for(int j = 1; j < tnum; j++){
3124                 const char *token;
3125                 int tsiz;
3126                 TCLISTVAL(token, tokens, j, tsiz);
3127                 TCLISTPUSH(cnames, token, tsiz);
3128               }
3129             } else if(!strcmp(cmd, "next")){
3130               if(qrys){
3131                 TCREALLOC(qrys, qrys, sizeof(*qrys) * (qnum + 1));
3132               } else {
3133                 TCMALLOC(qrys, sizeof(*qrys) * 2);
3134                 qrys[0] = qry;
3135                 qnum = 1;
3136               }
3137               qry = tctdbqrynew(adb->tdb);
3138               qrys[qnum++] = qry;
3139             } else if(!strcmp(cmd, "mstype") && tnum > 1){
3140               const char *typestr = TCLISTVALPTR(tokens, 1);
3141               mstype = tctdbstrtometasearcytype(typestr);
3142               if(mstype < 0) mstype = TDBMSUNION;
3143             } else if(!strcmp(cmd, "out") || !strcmp(cmd, "remove")){
3144               toout = true;
3145             } else if(!strcmp(cmd, "count")){
3146               tocnt = true;
3147             } else if(!strcmp(cmd, "hint")){
3148               tohint = true;
3149             }
3150           }
3151           tclistdel(tokens);
3152         }
3153         if(toout){
3154           if(cnames){
3155             rv = tclistnew2(1);
3156             void *opq[2];
3157             opq[0] = rv;
3158             opq[1] = cnames;
3159             if(!tctdbqryproc2(qry, tcadbtdbqrygetout, opq)){
3160               tclistdel(rv);
3161               rv = NULL;
3162             }
3163           } else {
3164             if(tctdbqrysearchout2(qry)){
3165               rv = tclistnew2(1);
3166             } else {
3167               rv = NULL;
3168             }
3169           }
3170         } else {
3171           if(qrys){
3172             rv = tctdbmetasearch(qrys, qnum, mstype);
3173           } else {
3174             rv = tctdbqrysearch(qry);
3175           }
3176           if(cnames){
3177             int cnnum = TCLISTNUM(cnames);
3178             int rnum = TCLISTNUM(rv);
3179             TCLIST *nrv = tclistnew2(rnum);
3180             for(int i = 0; i < rnum; i++){
3181               const char *pkbuf;
3182               int pksiz;
3183               TCLISTVAL(pkbuf, rv, i, pksiz);
3184               TCMAP *cols = tctdbget(adb->tdb, pkbuf, pksiz);
3185               if(cols){
3186                 tcmapput(cols, "", 0, pkbuf, pksiz);
3187                 tcmapmove(cols, "", 0, true);
3188                 if(cnnum > 0){
3189                   TCMAP *ncols = tcmapnew2(cnnum + 1);
3190                   for(int j = 0; j < cnnum; j++){
3191                     const char *cname;
3192                     int cnsiz;
3193                     TCLISTVAL(cname, cnames, j, cnsiz);
3194                     int cvsiz;
3195                     const char *cvalue = tcmapget(cols, cname, cnsiz, &cvsiz);
3196                     if(cvalue) tcmapput(ncols, cname, cnsiz, cvalue, cvsiz);
3197                   }
3198                   tcmapdel(cols);
3199                   cols = ncols;
3200                 }
3201                 int csiz;
3202                 char *cbuf = tcstrjoin4(cols, &csiz);
3203                 tclistpushmalloc(nrv, cbuf, csiz);
3204                 tcmapdel(cols);
3205               }
3206             }
3207             tclistdel(rv);
3208             rv = nrv;
3209           }
3210         }
3211         if(tocnt && rv){
3212           tclistclear(rv);
3213           char numbuf[TCNUMBUFSIZ];
3214           int len = sprintf(numbuf, "%d", tctdbqrycount(qry));
3215           TCLISTPUSH(rv, numbuf, len);
3216         }
3217         if(tohint && rv){
3218           TCXSTR *hbuf = tcxstrnew();
3219           TCXSTRCAT(hbuf, "", 1);
3220           TCXSTRCAT(hbuf, "", 1);
3221           TCXSTRCAT(hbuf, "[[HINT]]\n", 9);
3222           const char *hint = tctdbqryhint(qrys ? qrys[0] : qry);
3223           TCXSTRCAT(hbuf, hint, strlen(hint));
3224           TCLISTPUSH(rv, TCXSTRPTR(hbuf), TCXSTRSIZE(hbuf));
3225           tcxstrdel(hbuf);
3226         }
3227         if(cnames) tclistdel(cnames);
3228         if(qrys){
3229           for(int i = 0; i < qnum; i++){
3230             tctdbqrydel(qrys[i]);
3231           }
3232           TCFREE(qrys);
3233         } else {
3234           tctdbqrydel(qry);
3235         }
3236       } else if(!strcmp(name, "genuid")){
3237         rv = tclistnew2(1);
3238         char numbuf[TCNUMBUFSIZ];
3239         int nsiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb));
3240         TCLISTPUSH(rv, numbuf, nsiz);
3241       } else {
3242         rv = NULL;
3243       }
3244       break;
3245     case ADBOSKEL:
3246       skel = adb->skel;
3247       if(skel->misc){
3248         rv = skel->misc(skel->opq, name, args);
3249       } else {
3250         rv = NULL;
3251       }
3252       break;
3253     default:
3254       rv = NULL;
3255       break;
3256   }
3257   return rv;
3258 }
3259 
3260 
3261 
3262 /*************************************************************************************************
3263  * features for experts
3264  *************************************************************************************************/
3265 
3266 
3267 /* Set an extra database sleleton to an abstract database object. */
tcadbsetskel(TCADB * adb,ADBSKEL * skel)3268 bool tcadbsetskel(TCADB *adb, ADBSKEL *skel){
3269   assert(skel);
3270   if(adb->omode != ADBOVOID) return false;
3271   if(adb->skel) TCFREE(adb->skel);
3272   adb->skel = tcmemdup(skel, sizeof(*skel));
3273   return true;
3274 }
3275 
3276 
3277 /* Set the multiple database skeleton to an abstract database object. */
tcadbsetskelmulti(TCADB * adb,int num)3278 bool tcadbsetskelmulti(TCADB *adb, int num){
3279   assert(adb && num >= 0);
3280   if(adb->omode != ADBOVOID) return false;
3281   if(num < 1) return false;
3282   if(num > CHAR_MAX) num = CHAR_MAX;
3283   ADBMUL *mul = tcadbmulnew(num);
3284   ADBSKEL skel;
3285   memset(&skel, 0, sizeof(skel));
3286   skel.opq = mul;
3287   skel.del = (void (*)(void *))tcadbmuldel;
3288   skel.open = (bool (*)(void *, const char *))tcadbmulopen;
3289   skel.close = (bool (*)(void *))tcadbmulclose;
3290   skel.put = (bool (*)(void *, const void *, int, const void *, int))tcadbmulput;
3291   skel.putkeep = (bool (*)(void *, const void *, int, const void *, int))tcadbmulputkeep;
3292   skel.putcat = (bool (*)(void *, const void *, int, const void *, int))tcadbmulputcat;
3293   skel.out = (bool (*)(void *, const void *, int))tcadbmulout;
3294   skel.get = (void *(*)(void *, const void *, int, int *))tcadbmulget;
3295   skel.vsiz = (int (*)(void *, const void *, int))tcadbmulvsiz;
3296   skel.iterinit = (bool (*)(void *))tcadbmuliterinit;
3297   skel.iternext = (void *(*)(void *, int *))tcadbmuliternext;
3298   skel.fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcadbmulfwmkeys;
3299   skel.addint = (int (*)(void *, const void *, int, int))tcadbmuladdint;
3300   skel.adddouble = (double (*)(void *, const void *, int, double))tcadbmuladddouble;
3301   skel.sync = (bool (*)(void *))tcadbmulsync;
3302   skel.optimize = (bool (*)(void *, const char *))tcadbmuloptimize;
3303   skel.vanish = (bool (*)(void *))tcadbmulvanish;
3304   skel.copy = (bool (*)(void *, const char *))tcadbmulcopy;
3305   skel.tranbegin = (bool (*)(void *))tcadbmultranbegin;
3306   skel.trancommit = (bool (*)(void *))tcadbmultrancommit;
3307   skel.tranabort = (bool (*)(void *))tcadbmultranabort;
3308   skel.path = (const char *(*)(void *))tcadbmulpath;
3309   skel.rnum = (uint64_t (*)(void *))tcadbmulrnum;
3310   skel.size = (uint64_t (*)(void *))tcadbmulsize;
3311   skel.misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcadbmulmisc;
3312   skel.putproc =
3313     (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcadbmulputproc;
3314   skel.foreach = (bool (*)(void *, TCITER, void *))tcadbmulforeach;
3315   if(!tcadbsetskel(adb, &skel)){
3316     tcadbmuldel(mul);
3317     return false;
3318   }
3319   return true;
3320 }
3321 
3322 
3323 /* Get the open mode of an abstract database object. */
tcadbomode(TCADB * adb)3324 int tcadbomode(TCADB *adb){
3325   assert(adb);
3326   return adb->omode;
3327 }
3328 
3329 
3330 /* Get the concrete database object of an abstract database object. */
tcadbreveal(TCADB * adb)3331 void *tcadbreveal(TCADB *adb){
3332   assert(adb);
3333   void *rv;
3334   switch(adb->omode){
3335     case ADBOMDB:
3336       rv = adb->mdb;
3337       break;
3338     case ADBONDB:
3339       rv = adb->ndb;
3340       break;
3341     case ADBOHDB:
3342       rv = adb->hdb;
3343       break;
3344     case ADBOBDB:
3345       rv = adb->bdb;
3346       break;
3347     case ADBOFDB:
3348       rv = adb->fdb;
3349       break;
3350     case ADBOTDB:
3351       rv = adb->tdb;
3352       break;
3353     case ADBOSKEL:
3354       rv = adb->skel;
3355       break;
3356     default:
3357       rv = NULL;
3358       break;
3359   }
3360   return rv;
3361 }
3362 
3363 
3364 /* Store a record into an abstract database object with a duplication handler. */
tcadbputproc(TCADB * adb,const void * kbuf,int ksiz,const void * vbuf,int vsiz,TCPDPROC proc,void * op)3365 bool tcadbputproc(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
3366                   TCPDPROC proc, void *op){
3367   assert(adb && kbuf && ksiz >= 0 && proc);
3368   bool err = false;
3369   ADBSKEL *skel;
3370   switch(adb->omode){
3371     case ADBOMDB:
3372       if(tcmdbputproc(adb->mdb, kbuf, ksiz, vbuf, vsiz, proc, op)){
3373         if(adb->capnum > 0 || adb->capsiz > 0){
3374           adb->capcnt++;
3375           if((adb->capcnt & 0xff) == 0){
3376             if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100)
3377               tcmdbcutfront(adb->mdb, 0x100);
3378             if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz)
3379               tcmdbcutfront(adb->mdb, 0x200);
3380           }
3381         }
3382       } else {
3383         err = true;
3384       }
3385       break;
3386     case ADBONDB:
3387       if(tcndbputproc(adb->ndb, kbuf, ksiz, vbuf, vsiz, proc, op)){
3388         if(adb->capnum > 0 || adb->capsiz > 0){
3389           adb->capcnt++;
3390           if((adb->capcnt & 0xff) == 0){
3391             if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100)
3392               tcndbcutfringe(adb->ndb, 0x100);
3393             if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz)
3394               tcndbcutfringe(adb->ndb, 0x200);
3395           }
3396         }
3397       } else {
3398         err = true;
3399       }
3400       break;
3401     case ADBOHDB:
3402       if(!tchdbputproc(adb->hdb, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true;
3403       break;
3404     case ADBOBDB:
3405       if(!tcbdbputproc(adb->bdb, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true;
3406       break;
3407     case ADBOFDB:
3408       if(!tcfdbputproc(adb->fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz, proc, op)) err = true;
3409       break;
3410     case ADBOTDB:
3411       if(!tctdbputproc(adb->tdb, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true;
3412       break;
3413     case ADBOSKEL:
3414       skel = adb->skel;
3415       if(skel->putproc){
3416         if(!skel->putproc(skel->opq, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true;
3417       } else {
3418         err = true;
3419       }
3420       break;
3421     default:
3422       err = true;
3423       break;
3424   }
3425   return !err;
3426 }
3427 
3428 
3429 /* Process each record atomically of an abstract database object. */
tcadbforeach(TCADB * adb,TCITER iter,void * op)3430 bool tcadbforeach(TCADB *adb, TCITER iter, void *op){
3431   assert(adb && iter);
3432   bool err = false;
3433   ADBSKEL *skel;
3434   switch(adb->omode){
3435     case ADBOMDB:
3436       tcmdbforeach(adb->mdb, iter, op);
3437       break;
3438     case ADBONDB:
3439       tcndbforeach(adb->ndb, iter, op);
3440       break;
3441     case ADBOHDB:
3442       if(!tchdbforeach(adb->hdb, iter, op)) err = true;
3443       break;
3444     case ADBOBDB:
3445       if(!tcbdbforeach(adb->bdb, iter, op)) err = true;
3446       break;
3447     case ADBOFDB:
3448       if(!tcfdbforeach(adb->fdb, iter, op)) err = true;
3449       break;
3450     case ADBOTDB:
3451       if(!tctdbforeach(adb->tdb, iter, op)) err = true;
3452       break;
3453     case ADBOSKEL:
3454       skel = adb->skel;
3455       if(skel->foreach){
3456         if(!skel->foreach(skel->opq, iter, op)) err = true;
3457       } else {
3458         err = true;
3459       }
3460       break;
3461     default:
3462       err = true;
3463       break;
3464   }
3465   return !err;
3466 }
3467 
3468 
3469 /* Map records of an abstract database object into another B+ tree database. */
tcadbmapbdb(TCADB * adb,TCLIST * keys,TCBDB * bdb,ADBMAPPROC proc,void * op,int64_t csiz)3470 bool tcadbmapbdb(TCADB *adb, TCLIST *keys, TCBDB *bdb, ADBMAPPROC proc, void *op, int64_t csiz){
3471   assert(adb && bdb && proc);
3472   if(csiz < 0) csiz = 256LL << 20;
3473   TCLIST *recs = tclistnew2(tclmin(csiz / 64 + 256, INT_MAX / 4));
3474   ADBMAPBDB map;
3475   map.adb = adb;
3476   map.bdb = bdb;
3477   map.recs = recs;
3478   map.proc = proc;
3479   map.op = op;
3480   map.rsiz = 0;
3481   map.csiz = csiz;
3482   bool err = false;
3483   if(keys){
3484     int knum = TCLISTNUM(keys);
3485     for(int i = 0; i < knum && !err; i++){
3486       const char *kbuf;
3487       int ksiz;
3488       TCLISTVAL(kbuf, keys, i, ksiz);
3489       int vsiz;
3490       char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
3491       if(vbuf){
3492         if(!tcadbmapbdbiter(kbuf, ksiz, vbuf, vsiz, &map)) err = true;
3493         TCFREE(vbuf);
3494         if(map.rsiz > map.csiz && !tcadbmapbdbdump(&map)) err = true;
3495       }
3496       if(map.rsiz > 0 && !tcadbmapbdbdump(&map)) err = true;
3497     }
3498   } else {
3499     if(!tcadbforeach(adb, tcadbmapbdbiter, &map)) err = true;
3500   }
3501   if(map.rsiz > 0 && !tcadbmapbdbdump(&map)) err = true;
3502   tclistdel(recs);
3503   return !err;
3504 }
3505 
3506 
3507 /* Emit records generated by the mapping function into the result map. */
tcadbmapbdbemit(void * map,const char * kbuf,int ksiz,const char * vbuf,int vsiz)3508 bool tcadbmapbdbemit(void *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
3509   assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3510   ADBMAPBDB *mymap = map;
3511   int rsiz = sizeof(ksiz) + ksiz + vsiz;
3512   char stack[TCNUMBUFSIZ*8];
3513   char *rbuf;
3514   if(rsiz <= sizeof(stack)){
3515     rbuf = stack;
3516   } else {
3517     TCMALLOC(rbuf, rsiz);
3518   }
3519   bool err = false;
3520   char *wp = rbuf;
3521   memcpy(wp, &ksiz, sizeof(ksiz));
3522   wp += sizeof(ksiz);
3523   memcpy(wp, kbuf, ksiz);
3524   wp += ksiz;
3525   memcpy(wp, vbuf, vsiz);
3526   TCLISTPUSH(mymap->recs, rbuf, rsiz);
3527   mymap->rsiz += rsiz + sizeof(TCLISTDATUM);
3528   if(rbuf != stack) TCFREE(rbuf);
3529   if(mymap->rsiz > mymap->csiz && !tcadbmapbdbdump(map)) err = true;
3530   return !err;
3531 }
3532 
3533 
3534 
3535 /*************************************************************************************************
3536  * private features
3537  *************************************************************************************************/
3538 
3539 
3540 /* Create a multiple database object.
3541    `num' specifies the number of inner databases.
3542    The return value is the new multiple database object. */
tcadbmulnew(int num)3543 static ADBMUL *tcadbmulnew(int num){
3544   assert(num > 0);
3545   ADBMUL *mul;
3546   TCMALLOC(mul, sizeof(*mul));
3547   mul->adbs = NULL;
3548   mul->num = num;
3549   mul->iter = -1;
3550   mul->path = NULL;
3551   return mul;
3552 }
3553 
3554 
3555 /* Delete a multiple database object.
3556    `mul' specifies the multiple database object. */
tcadbmuldel(ADBMUL * mul)3557 static void tcadbmuldel(ADBMUL *mul){
3558   assert(mul);
3559   if(mul->adbs) tcadbmulclose(mul);
3560   TCFREE(mul);
3561 }
3562 
3563 
3564 /* Open a multiple database.
3565    `mul' specifies the multiple database object.
3566    If successful, the return value is true, else, it is false. */
tcadbmulopen(ADBMUL * mul,const char * name)3567 static bool tcadbmulopen(ADBMUL *mul, const char *name){
3568   assert(mul && name);
3569   if(mul->adbs) return false;
3570   mul->iter = -1;
3571   TCLIST *elems = tcstrsplit(name, "#");
3572   char *path = tclistshift2(elems);
3573   if(!path){
3574     tclistdel(elems);
3575     return false;
3576   }
3577   const char *ext = strrchr(path, MYEXTCHR);
3578   if(!ext) ext = "";
3579   const char *params = strchr(name, '#');
3580   if(!params) params = "";
3581   bool owmode = true;
3582   bool ocmode = true;
3583   bool otmode = false;
3584   int ln = TCLISTNUM(elems);
3585   for(int i = 0; i < ln; i++){
3586     const char *elem = TCLISTVALPTR(elems, i);
3587     char *pv = strchr(elem, '=');
3588     if(!pv) continue;
3589     *(pv++) = '\0';
3590     if(!tcstricmp(elem, "mode")){
3591       owmode = strchr(pv, 'w') || strchr(pv, 'W');
3592       ocmode = strchr(pv, 'c') || strchr(pv, 'C');
3593       otmode = strchr(pv, 't') || strchr(pv, 'T');
3594     }
3595   }
3596   tclistdel(elems);
3597   bool err = false;
3598   char *gpat = tcsprintf("%s%c%s*%s", path, MYPATHCHR, ADBMULPREFIX, ext);
3599   TCLIST *cpaths = tcglobpat(gpat);
3600   tclistsort(cpaths);
3601   int cnum = TCLISTNUM(cpaths);
3602   if(owmode){
3603     if(otmode){
3604       for(int i = 0; i < cnum; i++){
3605         const char *cpath = TCLISTVALPTR(cpaths, i);
3606         if(unlink(cpath) != 0) err = true;
3607       }
3608       tclistclear(cpaths);
3609       cnum = 0;
3610     }
3611     if(ocmode && cnum < 1){
3612       if(mkdir(path, ADBDIRMODE) != 0 && errno != EEXIST){
3613         err = true;
3614       } else {
3615         for(int i = 0; i < mul->num; i++){
3616           tclistprintf(cpaths, "%s%c%s%03d%s", path, MYPATHCHR, ADBMULPREFIX, i + 1, ext);
3617         }
3618         cnum = TCLISTNUM(cpaths);
3619       }
3620     }
3621   }
3622   if(!err && cnum > 0){
3623     TCADB **adbs;
3624     TCMALLOC(adbs, sizeof(*adbs) * cnum);
3625     for(int i = 0; i < cnum; i++){
3626       TCADB *adb = tcadbnew();
3627       const char *cpath = TCLISTVALPTR(cpaths, i);
3628       char *cname = tcsprintf("%s%s", cpath, params);
3629       if(!tcadbopen(adb, cname)) err = true;
3630       TCFREE(cname);
3631       adbs[i] = adb;
3632     }
3633     if(err){
3634       for(int i = cnum - 1; i >= 0; i--){
3635         tcadbdel(adbs[i]);
3636       }
3637       TCFREE(adbs);
3638     } else {
3639       mul->adbs = adbs;
3640       mul->num = cnum;
3641       mul->path = path;
3642       path = NULL;
3643     }
3644   }
3645   tclistdel(cpaths);
3646   TCFREE(gpat);
3647   TCFREE(path);
3648   return !err;
3649 }
3650 
3651 
3652 /* Close a multiple database object.
3653    `mul' specifies the multiple database object.
3654    If successful, the return value is true, else, it is false. */
tcadbmulclose(ADBMUL * mul)3655 static bool tcadbmulclose(ADBMUL *mul){
3656   assert(mul);
3657   if(!mul->adbs) return false;
3658   TCADB **adbs = mul->adbs;
3659   int num = mul->num;
3660   bool err = false;
3661   for(int i = num - 1; i >= 0; i--){
3662     TCADB *adb = adbs[i];
3663     if(!tcadbclose(adb)) err = true;
3664     tcadbdel(adb);
3665   }
3666   TCFREE(mul->path);
3667   TCFREE(adbs);
3668   mul->adbs = NULL;
3669   mul->path = NULL;
3670   return !err;
3671 }
3672 
3673 
3674 /* Store a record into a multiple database object.
3675    `mul' specifies the multiple database object.
3676    `kbuf' specifies the pointer to the region of the key.
3677    `ksiz' specifies the size of the region of the key.
3678    `vbuf' specifies the pointer to the region of the value.
3679    `vsiz' specifies the size of the region of the value.
3680    If successful, the return value is true, else, it is false. */
tcadbmulput(ADBMUL * mul,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3681 static bool tcadbmulput(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3682   assert(mul && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3683   if(!mul->adbs) return false;
3684   int idx = tcadbmulidx(mul, kbuf, ksiz);
3685   TCADB *adb = mul->adbs[idx];
3686   return tcadbput(adb, kbuf, ksiz, vbuf, vsiz);
3687 }
3688 
3689 
3690 /* Store a new record into a multiple database object.
3691    `mul' specifies the multiple database object.
3692    `kbuf' specifies the pointer to the region of the key.
3693    `ksiz' specifies the size of the region of the key.
3694    `vbuf' specifies the pointer to the region of the value.
3695    `vsiz' specifies the size of the region of the value.
3696    If successful, the return value is true, else, it is false. */
tcadbmulputkeep(ADBMUL * mul,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3697 static bool tcadbmulputkeep(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3698   assert(mul && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3699   if(!mul->adbs) return false;
3700   int idx = tcadbmulidx(mul, kbuf, ksiz);
3701   TCADB *adb = mul->adbs[idx];
3702   return tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz);
3703 }
3704 
3705 
3706 /* Concatenate a value at the end of the existing record in a multiple database object.
3707    `mul' specifies the multiple database object.
3708    `kbuf' specifies the pointer to the region of the key.
3709    `ksiz' specifies the size of the region of the key.
3710    `vbuf' specifies the pointer to the region of the value.
3711    `vsiz' specifies the size of the region of the value.
3712    If successful, the return value is true, else, it is false. */
tcadbmulputcat(ADBMUL * mul,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3713 static bool tcadbmulputcat(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3714   assert(mul && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3715   if(!mul->adbs) return false;
3716   int idx = tcadbmulidx(mul, kbuf, ksiz);
3717   TCADB *adb = mul->adbs[idx];
3718   return tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz);
3719 }
3720 
3721 
3722 /* Remove a record of a multiple database object.
3723    `mul' specifies the multiple database object.
3724    `kbuf' specifies the pointer to the region of the key.
3725    `ksiz' specifies the size of the region of the key.
3726    If successful, the return value is true, else, it is false. */
tcadbmulout(ADBMUL * mul,const void * kbuf,int ksiz)3727 static bool tcadbmulout(ADBMUL *mul, const void *kbuf, int ksiz){
3728   assert(mul && kbuf && ksiz >= 0);
3729   if(!mul->adbs) return false;
3730   int idx = tcadbmulidx(mul, kbuf, ksiz);
3731   TCADB *adb = mul->adbs[idx];
3732   return tcadbout(adb, kbuf, ksiz);
3733 }
3734 
3735 
3736 /* Retrieve a record in a multiple database object.
3737    `mul' specifies the multiple database object.
3738    `kbuf' specifies the pointer to the region of the key.
3739    `ksiz' specifies the size of the region of the key.
3740    `sp' specifies the pointer to the variable into which the size of the region of the return
3741    value is assigned.
3742    If successful, the return value is the pointer to the region of the value of the corresponding
3743    record. */
tcadbmulget(ADBMUL * mul,const void * kbuf,int ksiz,int * sp)3744 static void *tcadbmulget(ADBMUL *mul, const void *kbuf, int ksiz, int *sp){
3745   assert(mul && kbuf && ksiz >= 0 && sp);
3746   if(!mul->adbs) return false;
3747   int idx = tcadbmulidx(mul, kbuf, ksiz);
3748   TCADB *adb = mul->adbs[idx];
3749   return tcadbget(adb, kbuf, ksiz, sp);
3750 }
3751 
3752 
3753 /* Get the size of the value of a record in a multiple database object.
3754    `mul' specifies the multiple database object.
3755    `kbuf' specifies the pointer to the region of the key.
3756    `ksiz' specifies the size of the region of the key.
3757    If successful, the return value is the size of the value of the corresponding record, else,
3758    it is -1. */
tcadbmulvsiz(ADBMUL * mul,const void * kbuf,int ksiz)3759 static int tcadbmulvsiz(ADBMUL *mul, const void *kbuf, int ksiz){
3760   assert(mul && kbuf && ksiz >= 0);
3761   if(!mul->adbs) return false;
3762   int idx = tcadbmulidx(mul, kbuf, ksiz);
3763   TCADB *adb = mul->adbs[idx];
3764   return tcadbvsiz(adb, kbuf, ksiz);
3765 }
3766 
3767 
3768 /* Initialize the iterator of a multiple database object.
3769    `mul' specifies the multiple database object.
3770    If successful, the return value is true, else, it is false. */
tcadbmuliterinit(ADBMUL * mul)3771 static bool tcadbmuliterinit(ADBMUL *mul){
3772   assert(mul);
3773   if(!mul->adbs) return false;
3774   mul->iter = -1;
3775   TCADB **adbs = mul->adbs;
3776   int num = mul->num;
3777   bool err = false;
3778   for(int i = 0; i < num; i++){
3779     if(!tcadbiterinit(adbs[i])) err = true;
3780   }
3781   if(err) return false;
3782   mul->iter = 0;
3783   return true;
3784 }
3785 
3786 
3787 /* Get the next key of the iterator of a multiple database object.
3788    `mul' specifies the multiple database object.
3789    `sp' specifies the pointer to the variable into which the size of the region of the return
3790    value is assigned.
3791    If successful, the return value is the pointer to the region of the next key, else, it is
3792    `NULL'. */
tcadbmuliternext(ADBMUL * mul,int * sp)3793 static void *tcadbmuliternext(ADBMUL *mul, int *sp){
3794   assert(mul && sp);
3795   if(!mul->adbs || mul->iter < 0) return false;
3796   while(mul->iter < mul->num){
3797     TCADB *adb = mul->adbs[mul->iter];
3798     char *rv = tcadbiternext(adb, sp);
3799     if(rv) return rv;
3800     mul->iter++;
3801   }
3802   mul->iter = -1;
3803   return NULL;
3804 }
3805 
3806 
3807 /* Get forward matching keys in a multiple database object.
3808    `mul' specifies the multiple database object.
3809    `pbuf' specifies the pointer to the region of the prefix.
3810    `psiz' specifies the size of the region of the prefix.
3811    `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
3812    specified.
3813    The return value is a list object of the corresponding keys. */
tcadbmulfwmkeys(ADBMUL * mul,const void * pbuf,int psiz,int max)3814 static TCLIST *tcadbmulfwmkeys(ADBMUL *mul, const void *pbuf, int psiz, int max){
3815   assert(mul && pbuf && psiz >= 0);
3816   if(!mul->adbs) return tclistnew2(1);
3817   if(max < 0) max = INT_MAX;
3818   TCADB **adbs = mul->adbs;
3819   int num = mul->num;
3820   TCLIST *rv = tclistnew();
3821   for(int i = 0; i < num && TCLISTNUM(rv) < max; i++){
3822     TCLIST *res = tcadbfwmkeys(adbs[i], pbuf, psiz, max);
3823     int rnum = TCLISTNUM(res);
3824     for(int j = 0; j < rnum && TCLISTNUM(rv) < max; j++){
3825       const char *vbuf;
3826       int vsiz;
3827       TCLISTVAL(vbuf, res, j, vsiz);
3828       TCLISTPUSH(rv, vbuf, vsiz);
3829     }
3830     tclistdel(res);
3831   }
3832   return rv;
3833 }
3834 
3835 
3836 /* Add an integer to a record in a multiple database object.
3837    `mul' specifies the multiple database object.
3838    `kbuf' specifies the pointer to the region of the key.
3839    `ksiz' specifies the size of the region of the key.
3840    `num' specifies the additional value.
3841    If successful, the return value is the summation value, else, it is `INT_MIN'. */
tcadbmuladdint(ADBMUL * mul,const void * kbuf,int ksiz,int num)3842 static int tcadbmuladdint(ADBMUL *mul, const void *kbuf, int ksiz, int num){
3843   assert(mul && kbuf && ksiz >= 0);
3844   if(!mul->adbs) return INT_MIN;
3845   int idx = tcadbmulidx(mul, kbuf, ksiz);
3846   TCADB *adb = mul->adbs[idx];
3847   return tcadbaddint(adb, kbuf, ksiz, num);
3848 }
3849 
3850 
3851 /* Add a real number to a record in a multiple database object.
3852    `mul' specifies the multiple database object.
3853    `kbuf' specifies the pointer to the region of the key.
3854    `ksiz' specifies the size of the region of the key.
3855    `num' specifies the additional value.
3856    If successful, the return value is the summation value, else, it is Not-a-Number. */
tcadbmuladddouble(ADBMUL * mul,const void * kbuf,int ksiz,double num)3857 static double tcadbmuladddouble(ADBMUL *mul, const void *kbuf, int ksiz, double num){
3858   assert(mul && kbuf && ksiz >= 0);
3859   if(!mul->adbs) return nan("");
3860   int idx = tcadbmulidx(mul, kbuf, ksiz);
3861   TCADB *adb = mul->adbs[idx];
3862   return tcadbadddouble(adb, kbuf, ksiz, num);
3863 }
3864 
3865 
3866 /* Synchronize updated contents of a multiple database object with the file and the device.
3867    `mul' specifies the multiple database object.
3868    If successful, the return value is true, else, it is false. */
tcadbmulsync(ADBMUL * mul)3869 static bool tcadbmulsync(ADBMUL *mul){
3870   assert(mul);
3871   if(!mul->adbs) return false;
3872   TCADB **adbs = mul->adbs;
3873   int num = mul->num;
3874   bool err = false;
3875   for(int i = 0; i < num; i++){
3876     if(!tcadbsync(adbs[i])) err = true;
3877   }
3878   return !err;
3879 }
3880 
3881 
3882 /* Optimize the storage of a multiple database object.
3883    `mul' specifies the multiple database object.
3884    `params' specifies the string of the tuning parameters, which works as with the tuning
3885    of parameters the function `tcadbmulopen'.
3886    If successful, the return value is true, else, it is false. */
tcadbmuloptimize(ADBMUL * mul,const char * params)3887 static bool tcadbmuloptimize(ADBMUL *mul, const char *params){
3888   assert(mul);
3889   if(!mul->adbs) return false;
3890   mul->iter = -1;
3891   TCADB **adbs = mul->adbs;
3892   int num = mul->num;
3893   bool err = false;
3894   for(int i = 0; i < num; i++){
3895     if(!tcadboptimize(adbs[i], params)) err = true;
3896   }
3897   return !err;
3898 }
3899 
3900 
3901 /* Remove all records of a multiple database object.
3902    `mul' specifies the multiple database object.
3903    If successful, the return value is true, else, it is false. */
tcadbmulvanish(ADBMUL * mul)3904 static bool tcadbmulvanish(ADBMUL *mul){
3905   assert(mul);
3906   if(!mul->adbs) return false;
3907   mul->iter = -1;
3908   TCADB **adbs = mul->adbs;
3909   int num = mul->num;
3910   bool err = false;
3911   for(int i = 0; i < num; i++){
3912     if(!tcadbvanish(adbs[i])) err = true;
3913   }
3914   return !err;
3915 }
3916 
3917 
3918 /* Copy the database file of a multiple database object.
3919    `mul' specifies the multiple database object.
3920    `path' specifies the path of the destination file.
3921    If successful, the return value is true, else, it is false.  False is returned if the executed
3922    command returns non-zero code. */
tcadbmulcopy(ADBMUL * mul,const char * path)3923 static bool tcadbmulcopy(ADBMUL *mul, const char *path){
3924   assert(mul && path);
3925   TCADB **adbs = mul->adbs;
3926   int num = mul->num;
3927   bool err = false;
3928   if(*path == '@'){
3929     for(int i = 0; i < num; i++){
3930       if(!tcadbcopy(adbs[i], path)) err = true;
3931     }
3932   } else {
3933     if(mkdir(path, ADBDIRMODE) == -1 && errno != EEXIST) return false;
3934     for(int i = 0; i < num; i++){
3935       TCADB *adb = adbs[i];
3936       const char *cpath = tcadbpath(adb);
3937       if(cpath){
3938         const char *cname = strrchr(cpath, MYPATHCHR);
3939         cname = cname ? cname + 1 : cpath;
3940         const char *ext = strrchr(cname, MYEXTCHR);
3941         if(!ext) ext = "";
3942         char *npath = tcsprintf("%s%c%s%03d%s", path, MYPATHCHR, ADBMULPREFIX, i + 1, ext);
3943         if(!tcadbcopy(adb, npath)) err = true;
3944         TCFREE(npath);
3945       } else {
3946         err = true;
3947       }
3948     }
3949   }
3950   return !err;
3951 }
3952 
3953 
3954 /* Begin the transaction of a multiple database object.
3955    `mul' specifies the multiple database object.
3956    If successful, the return value is true, else, it is false. */
tcadbmultranbegin(ADBMUL * mul)3957 static bool tcadbmultranbegin(ADBMUL *mul){
3958   assert(mul);
3959   if(!mul->adbs) return false;
3960   TCADB **adbs = mul->adbs;
3961   int num = mul->num;
3962   bool err = false;
3963   for(int i = 0; i < num; i++){
3964     if(!tcadbtranbegin(adbs[i])){
3965       while(--i >= 0){
3966         tcadbtranabort(adbs[i]);
3967       }
3968       err = true;
3969       break;
3970     }
3971   }
3972   return !err;
3973 }
3974 
3975 
3976 /* Commit the transaction of a multiple database object.
3977    `mul' specifies the multiple database object.
3978    If successful, the return value is true, else, it is false. */
tcadbmultrancommit(ADBMUL * mul)3979 static bool tcadbmultrancommit(ADBMUL *mul){
3980   assert(mul);
3981   if(!mul->adbs) return false;
3982   TCADB **adbs = mul->adbs;
3983   int num = mul->num;
3984   bool err = false;
3985   for(int i = num - 1; i >= 0; i--){
3986     if(!tcadbtrancommit(adbs[i])) err = true;
3987   }
3988   return !err;
3989 }
3990 
3991 
3992 /* Abort the transaction of a multiple database object.
3993    `mul' specifies the multiple database object.
3994    If successful, the return value is true, else, it is false. */
tcadbmultranabort(ADBMUL * mul)3995 static bool tcadbmultranabort(ADBMUL *mul){
3996   assert(mul);
3997   if(!mul->adbs) return false;
3998   TCADB **adbs = mul->adbs;
3999   int num = mul->num;
4000   bool err = false;
4001   for(int i = num - 1; i >= 0; i--){
4002     if(!tcadbtranabort(adbs[i])) err = true;
4003   }
4004   return !err;
4005 }
4006 
4007 
4008 /* Get the file path of a multiple database object.
4009    `mul' specifies the multiple database object.
4010    The return value is the path of the database file or `NULL' if the object does not connect to
4011    any database. */
tcadbmulpath(ADBMUL * mul)4012 static const char *tcadbmulpath(ADBMUL *mul){
4013   assert(mul);
4014   if(!mul->adbs) return NULL;
4015   return mul->path;
4016 }
4017 
4018 
4019 /* Get the number of records of a multiple database object.
4020    `mul' specifies the multiple database object.
4021    The return value is the number of records or 0 if the object does not connect to any database
4022    instance. */
tcadbmulrnum(ADBMUL * mul)4023 static uint64_t tcadbmulrnum(ADBMUL *mul){
4024   assert(mul);
4025   if(!mul->adbs) return 0;
4026   TCADB **adbs = mul->adbs;
4027   int num = mul->num;
4028   uint64_t rnum = 0;
4029   for(int i = 0; i < num; i++){
4030     rnum += tcadbrnum(adbs[i]);
4031   }
4032   return rnum;
4033 }
4034 
4035 
4036 /* Get the size of the database of a multiple database object.
4037    `mul' specifies the multiple database object.
4038    The return value is the size of the database or 0 if the object does not connect to any
4039    database instance. */
tcadbmulsize(ADBMUL * mul)4040 static uint64_t tcadbmulsize(ADBMUL *mul){
4041   assert(mul);
4042   if(!mul->adbs) return 0;
4043   TCADB **adbs = mul->adbs;
4044   int num = mul->num;
4045   uint64_t size = 0;
4046   for(int i = 0; i < num; i++){
4047     size += tcadbsize(adbs[i]);
4048   }
4049   return size;
4050 }
4051 
4052 
4053 /* Call a versatile function for miscellaneous operations of a multiple database object.
4054    `mul' specifies the multiple database object.
4055    `name' specifies the name of the function.
4056    `args' specifies a list object containing arguments.
4057    If successful, the return value is a list object of the result. */
tcadbmulmisc(ADBMUL * mul,const char * name,const TCLIST * args)4058 static TCLIST *tcadbmulmisc(ADBMUL *mul, const char *name, const TCLIST *args){
4059   assert(mul && name);
4060   if(!mul->adbs) return NULL;
4061   TCADB **adbs = mul->adbs;
4062   int num = mul->num;
4063   TCLIST *rv = tclistnew();
4064   if(*name == '@'){
4065     name++;
4066     int anum = TCLISTNUM(args) - 1;
4067     TCLIST *targs = tclistnew2(2);
4068     for(int i = 0; i < anum; i++){
4069       const char *kbuf;
4070       int ksiz;
4071       TCLISTVAL(kbuf, args, i, ksiz);
4072       tclistclear(targs);
4073       TCLISTPUSH(targs, kbuf, ksiz);
4074       int idx = tcadbmulidx(mul, kbuf, ksiz);
4075       TCADB *adb = mul->adbs[idx];
4076       TCLIST *res = tcadbmisc(adb, name, targs);
4077       if(res){
4078         int rnum = TCLISTNUM(res);
4079         for(int j = 0; j < rnum; j++){
4080           const char *vbuf;
4081           int vsiz;
4082           TCLISTVAL(vbuf, res, j, vsiz);
4083           TCLISTPUSH(rv, vbuf, vsiz);
4084         }
4085         tclistdel(res);
4086       }
4087     }
4088     tclistdel(targs);
4089   } else if(*name == '%'){
4090     name++;
4091     int anum = TCLISTNUM(args) - 1;
4092     TCLIST *targs = tclistnew2(2);
4093     for(int i = 0; i < anum; i += 2){
4094       const char *kbuf, *vbuf;
4095       int ksiz, vsiz;
4096       TCLISTVAL(kbuf, args, i, ksiz);
4097       TCLISTVAL(vbuf, args, i + 1, vsiz);
4098       tclistclear(targs);
4099       TCLISTPUSH(targs, kbuf, ksiz);
4100       TCLISTPUSH(targs, vbuf, vsiz);
4101       int idx = tcadbmulidx(mul, kbuf, ksiz);
4102       TCADB *adb = mul->adbs[idx];
4103       TCLIST *res = tcadbmisc(adb, name, targs);
4104       if(res){
4105         int rnum = TCLISTNUM(res);
4106         for(int j = 0; j < rnum; j++){
4107           TCLISTVAL(vbuf, res, j, vsiz);
4108           TCLISTPUSH(rv, vbuf, vsiz);
4109         }
4110         tclistdel(res);
4111       }
4112     }
4113     tclistdel(targs);
4114   } else {
4115     for(int i = 0; i < num; i++){
4116       TCLIST *res = tcadbmisc(adbs[i], name, args);
4117       if(res){
4118         int rnum = TCLISTNUM(res);
4119         for(int j = 0; j < rnum; j++){
4120           const char *vbuf;
4121           int vsiz;
4122           TCLISTVAL(vbuf, res, j, vsiz);
4123           TCLISTPUSH(rv, vbuf, vsiz);
4124         }
4125         tclistdel(res);
4126       } else {
4127         tclistdel(rv);
4128         rv = NULL;
4129         break;
4130       }
4131     }
4132   }
4133   return rv;
4134 }
4135 
4136 
4137 /* Store a record into a multiple database object with a duplication handler.
4138    `mul' specifies the multiple database object.
4139    `kbuf' specifies the pointer to the region of the key.
4140    `ksiz' specifies the size of the region of the key.
4141    `vbuf' specifies the pointer to the region of the value.
4142    `vsiz' specifies the size of the region of the value.
4143    `proc' specifies the pointer to the callback function to process duplication.
4144    `op' specifies an arbitrary pointer to be given as a parameter of the callback function.
4145    If successful, the return value is true, else, it is false. */
tcadbmulputproc(ADBMUL * mul,const void * kbuf,int ksiz,const void * vbuf,int vsiz,TCPDPROC proc,void * op)4146 static bool tcadbmulputproc(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
4147                             TCPDPROC proc, void *op){
4148   assert(mul && kbuf && ksiz >= 0 && proc);
4149   if(!mul->adbs) return false;
4150   int idx = tcadbmulidx(mul, kbuf, ksiz);
4151   TCADB *adb = mul->adbs[idx];
4152   return tcadbputproc(adb, kbuf, ksiz, vbuf, vsiz, proc, op);
4153 }
4154 
4155 
4156 /* Process each record atomically of a multiple database object.
4157    `mul' specifies the multiple database object.
4158    `iter' specifies the pointer to the iterator function called for each record.
4159    `op' specifies an arbitrary pointer to be given as a parameter of the iterator function.
4160    If successful, the return value is true, else, it is false. */
tcadbmulforeach(ADBMUL * mul,TCITER iter,void * op)4161 static bool tcadbmulforeach(ADBMUL *mul, TCITER iter, void *op){
4162   assert(mul && iter);
4163   if(!mul->adbs) return false;
4164   TCADB **adbs = mul->adbs;
4165   int num = mul->num;
4166   bool err = false;
4167   for(int i = 0; i < num; i++){
4168     if(!tcadbforeach(adbs[i], iter, op)){
4169       err = true;
4170       break;
4171     }
4172   }
4173   return !err;
4174 }
4175 
4176 
4177 /* Get the database index of a multiple database object.
4178    `mul' specifies the multiple database object.
4179    `kbuf' specifies the pointer to the region of the key.
4180    `ksiz' specifies the size of the region of the key.
4181    The return value is the bucket index. */
tcadbmulidx(ADBMUL * mul,const void * kbuf,int ksiz)4182 static int tcadbmulidx(ADBMUL *mul, const void *kbuf, int ksiz){
4183   assert(mul && kbuf && ksiz >= 0);
4184   uint32_t hash = 20090810;
4185   const char *rp = (char *)kbuf + ksiz;
4186   while(ksiz--){
4187     hash = (hash * 29) ^ *(uint8_t *)--rp;
4188   }
4189   return hash % mul->num;
4190 }
4191 
4192 
4193 /* Call the mapping function for every record of a multiple database object.
4194    `kbuf' specifies the pointer to the region of the key.
4195    `ksiz' specifies the size of the region of the key.
4196    `vbuf' specifies the pointer to the region of the value.
4197    `vsiz' specifies the size of the region of the value.
4198    `op' specifies the pointer to the optional opaque object.
4199    The return value is true to continue iteration or false to stop iteration. */
tcadbmapbdbiter(const void * kbuf,int ksiz,const void * vbuf,int vsiz,void * op)4200 static bool tcadbmapbdbiter(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){
4201   assert(kbuf && ksiz >= 0 && vbuf && vsiz >= 0 && op);
4202   ADBMAPBDB *map = op;
4203   bool err = false;
4204   if(!map->proc(map, kbuf, ksiz, vbuf, vsiz, map->op)) err = true;
4205   return !err;
4206 }
4207 
4208 
4209 /* Dump all cached records into the B+ tree database.
4210    `map' specifies the mapper object for the B+ tree database.
4211    The return value is true if successful, else, it is false. */
tcadbmapbdbdump(ADBMAPBDB * map)4212 static bool tcadbmapbdbdump(ADBMAPBDB *map){
4213   assert(map);
4214   TCBDB *bdb = map->bdb;
4215   TCLIST *recs = map->recs;
4216   int rnum = TCLISTNUM(recs);
4217   TCCMP cmp = tcbdbcmpfunc(bdb);
4218   if(cmp == tccmplexical){
4219     tclistsortex(recs, tcadbmapreccmplexical);
4220   } else if(cmp == tccmpdecimal){
4221     tclistsortex(recs, tcadbmapreccmpdecimal);
4222   } else if(cmp == tccmpint32){
4223     tclistsortex(recs, tcadbmapreccmpint32);
4224   } else if(cmp == tccmpint64){
4225     tclistsortex(recs, tcadbmapreccmpint64);
4226   }
4227   bool err = false;
4228   for(int i = 0; i < rnum; i++){
4229     const char *rbuf;
4230     int rsiz;
4231     TCLISTVAL(rbuf, recs, i, rsiz);
4232     int ksiz;
4233     memcpy(&ksiz, rbuf, sizeof(ksiz));
4234     const char *kbuf = rbuf + sizeof(ksiz);
4235     if(!tcbdbputdup(bdb, kbuf, ksiz, kbuf + ksiz, rsiz - sizeof(ksiz) - ksiz)){
4236       err = true;
4237       break;
4238     }
4239   }
4240   tclistclear(recs);
4241   map->rsiz = 0;
4242   return !err;
4243 }
4244 
4245 
4246 /* Compare two list elements by lexical order for mapping.
4247    `a' specifies the pointer to one element.
4248    `b' specifies the pointer to the other element.
4249    The return value is positive if the former is big, negative if the latter is big, 0 if both
4250    are equivalent. */
tcadbmapreccmplexical(const TCLISTDATUM * a,const TCLISTDATUM * b)4251 static int tcadbmapreccmplexical(const TCLISTDATUM *a, const TCLISTDATUM *b){
4252   assert(a && b);
4253   unsigned char *ao = (unsigned char *)((TCLISTDATUM *)a)->ptr;
4254   unsigned char *bo = (unsigned char *)((TCLISTDATUM *)b)->ptr;
4255   int size = (((TCLISTDATUM *)a)->size < ((TCLISTDATUM *)b)->size) ?
4256     ((TCLISTDATUM *)a)->size : ((TCLISTDATUM *)b)->size;
4257   for(int i = sizeof(int); i < size; i++){
4258     if(ao[i] > bo[i]) return 1;
4259     if(ao[i] < bo[i]) return -1;
4260   }
4261   return ((TCLISTDATUM *)a)->size - ((TCLISTDATUM *)b)->size;
4262 }
4263 
4264 
4265 /* Compare two keys as decimal strings of real numbers for mapping.
4266    `a' specifies the pointer to one element.
4267    `b' specifies the pointer to the other element.
4268    The return value is positive if the former is big, negative if the latter is big, 0 if both
4269    are equivalent. */
tcadbmapreccmpdecimal(const TCLISTDATUM * a,const TCLISTDATUM * b)4270 static int tcadbmapreccmpdecimal(const TCLISTDATUM *a, const TCLISTDATUM *b){
4271   assert(a && b);
4272   return tccmpdecimal(((TCLISTDATUM *)a)->ptr + sizeof(int), a->size - sizeof(int),
4273                       ((TCLISTDATUM *)b)->ptr + sizeof(int), b->size - sizeof(int), NULL);
4274 }
4275 
4276 
4277 /* Compare two list elements as 32-bit integers in the native byte order for mapping.
4278    `a' specifies the pointer to one element.
4279    `b' specifies the pointer to the other element.
4280    The return value is positive if the former is big, negative if the latter is big, 0 if both
4281    are equivalent. */
tcadbmapreccmpint32(const TCLISTDATUM * a,const TCLISTDATUM * b)4282 static int tcadbmapreccmpint32(const TCLISTDATUM *a, const TCLISTDATUM *b){
4283   assert(a && b);
4284   return tccmpint32(((TCLISTDATUM *)a)->ptr + sizeof(int), a->size - sizeof(int),
4285                     ((TCLISTDATUM *)b)->ptr + sizeof(int), b->size - sizeof(int), NULL);
4286 }
4287 
4288 
4289 /* Compare two list elements as 64-bit integers in the native byte order for mapping.
4290    `a' specifies the pointer to one element.
4291    `b' specifies the pointer to the other element.
4292    The return value is positive if the former is big, negative if the latter is big, 0 if both
4293    are equivalent. */
tcadbmapreccmpint64(const TCLISTDATUM * a,const TCLISTDATUM * b)4294 static int tcadbmapreccmpint64(const TCLISTDATUM *a, const TCLISTDATUM *b){
4295   assert(a && b);
4296   return tccmpint64(((TCLISTDATUM *)a)->ptr + sizeof(int), a->size - sizeof(int),
4297                     ((TCLISTDATUM *)b)->ptr + sizeof(int), b->size - sizeof(int), NULL);
4298 }
4299 
4300 
4301 /* Retrieve and remove each record corresponding to a query object.
4302    `pkbuf' specifies the pointer to the region of the primary key.
4303    `pksiz' specifies the size of the region of the primary key.
4304    `cols' specifies a map object containing columns.
4305    `op' specifies the pointer to the optional opaque object.
4306    The return value is flags of the post treatment by bitwise-or.
4307    If successful, the return value is true, else, it is false. */
tcadbtdbqrygetout(const void * pkbuf,int pksiz,TCMAP * cols,void * op)4308 static int tcadbtdbqrygetout(const void *pkbuf, int pksiz, TCMAP *cols, void *op){
4309   TCLIST *rv = ((void **)op)[0];
4310   TCLIST *cnames = ((void **)op)[1];
4311   int cnnum = TCLISTNUM(cnames);
4312   tcmapput(cols, "", 0, pkbuf, pksiz);
4313   tcmapmove(cols, "", 0, true);
4314   if(cnnum > 0){
4315     TCMAP *ncols = tcmapnew2(cnnum + 1);
4316     for(int j = 0; j < cnnum; j++){
4317       const char *cname;
4318       int cnsiz;
4319       TCLISTVAL(cname, cnames, j, cnsiz);
4320       int cvsiz;
4321       const char *cvalue = tcmapget(cols, cname, cnsiz, &cvsiz);
4322       if(cvalue) tcmapput(ncols, cname, cnsiz, cvalue, cvsiz);
4323     }
4324     int csiz;
4325     char *cbuf = tcstrjoin4(ncols, &csiz);
4326     tclistpushmalloc(rv, cbuf, csiz);
4327     tcmapdel(ncols);
4328   } else {
4329     int csiz;
4330     char *cbuf = tcstrjoin4(cols, &csiz);
4331     tclistpushmalloc(rv, cbuf, csiz);
4332   }
4333   return TDBQPOUT;
4334 }
4335 
4336 
4337 
4338 // END OF FILE
4339