]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/tokyocabinet/tcbmttest.c
ebl Add tokyocabinet source to bacula
[bacula/bacula] / bacula / src / lib / tokyocabinet / tcbmttest.c
1 /*************************************************************************************************
2  * The test cases of the B+ tree database API
3  *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
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 <tcbdb.h>
19 #include "myconf.h"
20
21 #define RECBUFSIZ      32                // buffer for records
22
23 typedef struct {                         // type of structure for write thread
24   TCBDB *bdb;
25   int rnum;
26   bool rnd;
27   int id;
28 } TARGWRITE;
29
30 typedef struct {                         // type of structure for read thread
31   TCBDB *bdb;
32   int rnum;
33   bool wb;
34   bool rnd;
35   int id;
36 } TARGREAD;
37
38 typedef struct {                         // type of structure for remove thread
39   TCBDB *bdb;
40   int rnum;
41   bool rnd;
42   int id;
43 } TARGREMOVE;
44
45 typedef struct {                         // type of structure for wicked thread
46   TCBDB *bdb;
47   int rnum;
48   bool nc;
49   int id;
50   TCMAP *map;
51 } TARGWICKED;
52
53 typedef struct {                         // type of structure for typical thread
54   TCBDB *bdb;
55   int rnum;
56   bool nc;
57   int rratio;
58   int id;
59 } TARGTYPICAL;
60
61
62 /* global variables */
63 const char *g_progname;                  // program name
64 int g_dbgfd;                             // debugging output
65
66
67 /* function prototypes */
68 int main(int argc, char **argv);
69 static void usage(void);
70 static void iprintf(const char *format, ...);
71 static void eprint(TCBDB *bdb, const char *func);
72 static void mprint(TCBDB *bdb);
73 static int myrand(int range);
74 static int myrandnd(int range);
75 static int runwrite(int argc, char **argv);
76 static int runread(int argc, char **argv);
77 static int runremove(int argc, char **argv);
78 static int runwicked(int argc, char **argv);
79 static int runtypical(int argc, char **argv);
80 static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb,
81                      int bnum, int apow, int fpow, int opts, int omode, bool rnd);
82 static int procread(const char *path, int tnum, int omode, bool wb, bool rnd);
83 static int procremove(const char *path, int tnum, int omode, bool rnd);
84 static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc);
85 static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb,
86                        int bnum, int apow, int fpow, int opts, int omode, bool nc, int rratio);
87 static void *threadwrite(void *targ);
88 static void *threadread(void *targ);
89 static void *threadremove(void *targ);
90 static void *threadwicked(void *targ);
91 static void *threadtypical(void *targ);
92
93
94 /* main routine */
95 int main(int argc, char **argv){
96   g_progname = argv[0];
97   g_dbgfd = -1;
98   const char *ebuf = getenv("TCDBGFD");
99   if(ebuf) g_dbgfd = atoi(ebuf);
100   srand((unsigned int)(tctime() * 1000) % UINT_MAX);
101   if(argc < 2) usage();
102   int rv = 0;
103   if(!strcmp(argv[1], "write")){
104     rv = runwrite(argc, argv);
105   } else if(!strcmp(argv[1], "read")){
106     rv = runread(argc, argv);
107   } else if(!strcmp(argv[1], "remove")){
108     rv = runremove(argc, argv);
109   } else if(!strcmp(argv[1], "wicked")){
110     rv = runwicked(argc, argv);
111   } else if(!strcmp(argv[1], "typical")){
112     rv = runtypical(argc, argv);
113   } else {
114     usage();
115   }
116   return rv;
117 }
118
119
120 /* print the usage and exit */
121 static void usage(void){
122   fprintf(stderr, "%s: test cases of the B+ tree database API of Tokyo Cabinet\n", g_progname);
123   fprintf(stderr, "\n");
124   fprintf(stderr, "usage:\n");
125   fprintf(stderr, "  %s write [-tl] [-td|-tb] [-nl|-nb] [-rnd] path tnum rnum"
126           " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
127   fprintf(stderr, "  %s read [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname);
128   fprintf(stderr, "  %s remove [-nl|-nb] [-rnd] path tnum\n", g_progname);
129   fprintf(stderr, "  %s wicked [-tl] [-td|-tb] [-nl|-nb] [-nc] path tnum rnum\n",
130           g_progname);
131   fprintf(stderr, "  %s typical [-tl] [-td|-tb] [-nl|-nb] [-nc] [-rr] path tnum rnum"
132           " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
133   fprintf(stderr, "\n");
134   exit(1);
135 }
136
137
138 /* print formatted information string and flush the buffer */
139 static void iprintf(const char *format, ...){
140   va_list ap;
141   va_start(ap, format);
142   vprintf(format, ap);
143   fflush(stdout);
144   va_end(ap);
145 }
146
147
148 /* print error message of hash database */
149 static void eprint(TCBDB *bdb, const char *func){
150   const char *path = tcbdbpath(bdb);
151   int ecode = tcbdbecode(bdb);
152   fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
153           g_progname, path ? path : "-", func, ecode, tcbdberrmsg(ecode));
154 }
155
156
157 /* print members of hash database */
158 static void mprint(TCBDB *bdb){
159   if(bdb->hdb->cnt_writerec < 0) return;
160   iprintf("max leaf member: %d\n", tcbdblmemb(bdb));
161   iprintf("max node member: %d\n", tcbdbnmemb(bdb));
162   iprintf("leaf number: %d\n", tcbdblnum(bdb));
163   iprintf("node number: %d\n", tcbdbnnum(bdb));
164   iprintf("bucket number: %lld\n", (long long)tcbdbbnum(bdb));
165   iprintf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb));
166   iprintf("cnt_saveleaf: %lld\n", (long long)bdb->cnt_saveleaf);
167   iprintf("cnt_loadleaf: %lld\n", (long long)bdb->cnt_loadleaf);
168   iprintf("cnt_killleaf: %lld\n", (long long)bdb->cnt_killleaf);
169   iprintf("cnt_adjleafc: %lld\n", (long long)bdb->cnt_adjleafc);
170   iprintf("cnt_savenode: %lld\n", (long long)bdb->cnt_savenode);
171   iprintf("cnt_loadnode: %lld\n", (long long)bdb->cnt_loadnode);
172   iprintf("cnt_adjnodec: %lld\n", (long long)bdb->cnt_adjnodec);
173   iprintf("cnt_writerec: %lld\n", (long long)bdb->hdb->cnt_writerec);
174   iprintf("cnt_reuserec: %lld\n", (long long)bdb->hdb->cnt_reuserec);
175   iprintf("cnt_moverec: %lld\n", (long long)bdb->hdb->cnt_moverec);
176   iprintf("cnt_readrec: %lld\n", (long long)bdb->hdb->cnt_readrec);
177   iprintf("cnt_searchfbp: %lld\n", (long long)bdb->hdb->cnt_searchfbp);
178   iprintf("cnt_insertfbp: %lld\n", (long long)bdb->hdb->cnt_insertfbp);
179   iprintf("cnt_splicefbp: %lld\n", (long long)bdb->hdb->cnt_splicefbp);
180   iprintf("cnt_dividefbp: %lld\n", (long long)bdb->hdb->cnt_dividefbp);
181   iprintf("cnt_mergefbp: %lld\n", (long long)bdb->hdb->cnt_mergefbp);
182   iprintf("cnt_reducefbp: %lld\n", (long long)bdb->hdb->cnt_reducefbp);
183   iprintf("cnt_appenddrp: %lld\n", (long long)bdb->hdb->cnt_appenddrp);
184   iprintf("cnt_deferdrp: %lld\n", (long long)bdb->hdb->cnt_deferdrp);
185   iprintf("cnt_flushdrp: %lld\n", (long long)bdb->hdb->cnt_flushdrp);
186   iprintf("cnt_adjrecc: %lld\n", (long long)bdb->hdb->cnt_adjrecc);
187 }
188
189
190 /* get a random number */
191 static int myrand(int range){
192   return (int)((double)range * rand() / (RAND_MAX + 1.0));
193 }
194
195
196 /* get a random number based on normal distribution */
197 static int myrandnd(int range){
198   int num = (int)tcdrandnd(range >> 1, range / 10);
199   return (num < 0 || num >= range) ? 0 : num;
200 }
201
202
203 /* parse arguments of write command */
204 static int runwrite(int argc, char **argv){
205   char *path = NULL;
206   char *tstr = NULL;
207   char *rstr = NULL;
208   char *lmstr = NULL;
209   char *nmstr = NULL;
210   char *bstr = NULL;
211   char *astr = NULL;
212   char *fstr = NULL;
213   int opts = 0;
214   int omode = 0;
215   bool rnd = false;
216   for(int i = 2; i < argc; i++){
217     if(!path && argv[i][0] == '-'){
218       if(!strcmp(argv[i], "-tl")){
219         opts |= BDBTLARGE;
220       } else if(!strcmp(argv[i], "-td")){
221         opts |= BDBTDEFLATE;
222       } else if(!strcmp(argv[i], "-tb")){
223         opts |= BDBTTCBS;
224       } else if(!strcmp(argv[i], "-nl")){
225         omode |= BDBONOLCK;
226       } else if(!strcmp(argv[i], "-nb")){
227         omode |= BDBOLCKNB;
228       } else if(!strcmp(argv[i], "-rnd")){
229         rnd = true;
230       } else {
231         usage();
232       }
233     } else if(!path){
234       path = argv[i];
235     } else if(!tstr){
236       tstr = argv[i];
237     } else if(!rstr){
238       rstr = argv[i];
239     } else if(!lmstr){
240       lmstr = argv[i];
241     } else if(!nmstr){
242       nmstr = argv[i];
243     } else if(!bstr){
244       bstr = argv[i];
245     } else if(!astr){
246       astr = argv[i];
247     } else if(!fstr){
248       fstr = argv[i];
249     } else {
250       usage();
251     }
252   }
253   if(!path || !tstr || !rstr) usage();
254   int tnum = atoi(tstr);
255   int rnum = atoi(rstr);
256   if(tnum < 1 || rnum < 1) usage();
257   int lmemb = lmstr ? atoi(lmstr) : -1;
258   int nmemb = nmstr ? atoi(nmstr) : -1;
259   int bnum = bstr ? atoi(bstr) : -1;
260   int apow = astr ? atoi(astr) : -1;
261   int fpow = fstr ? atoi(fstr) : -1;
262   int rv = procwrite(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, rnd);
263   return rv;
264 }
265
266
267 /* parse arguments of read command */
268 static int runread(int argc, char **argv){
269   char *path = NULL;
270   char *tstr = NULL;
271   int omode = 0;
272   bool rnd = false;
273   bool wb = false;
274   for(int i = 2; i < argc; i++){
275     if(!path && argv[i][0] == '-'){
276       if(!strcmp(argv[i], "-nl")){
277         omode |= BDBONOLCK;
278       } else if(!strcmp(argv[i], "-nb")){
279         omode |= BDBOLCKNB;
280       } else if(!strcmp(argv[i], "-wb")){
281         wb = true;
282       } else if(!strcmp(argv[i], "-rnd")){
283         rnd = true;
284       } else {
285         usage();
286       }
287     } else if(!path){
288       path = argv[i];
289     } else if(!tstr){
290       tstr = argv[i];
291     } else {
292       usage();
293     }
294   }
295   if(!path || !tstr) usage();
296   int tnum = atoi(tstr);
297   if(tnum < 1) usage();
298   int rv = procread(path, tnum, omode, wb, rnd);
299   return rv;
300 }
301
302
303 /* parse arguments of remove command */
304 static int runremove(int argc, char **argv){
305   char *path = NULL;
306   char *tstr = NULL;
307   int omode = 0;
308   bool rnd = false;
309   for(int i = 2; i < argc; i++){
310     if(!path && argv[i][0] == '-'){
311       if(!strcmp(argv[i], "-nl")){
312         omode |= BDBONOLCK;
313       } else if(!strcmp(argv[i], "-nb")){
314         omode |= BDBOLCKNB;
315       } else if(!strcmp(argv[i], "-rnd")){
316         rnd = true;
317       } else {
318         usage();
319       }
320     } else if(!path){
321       path = argv[i];
322     } else if(!tstr){
323       tstr = argv[i];
324     } else {
325       usage();
326     }
327   }
328   if(!path || !tstr) usage();
329   int tnum = atoi(tstr);
330   if(tnum < 1) usage();
331   int rv = procremove(path, tnum, omode, rnd);
332   return rv;
333 }
334
335
336 /* parse arguments of wicked command */
337 static int runwicked(int argc, char **argv){
338   char *path = NULL;
339   char *tstr = NULL;
340   char *rstr = NULL;
341   int opts = 0;
342   int omode = 0;
343   bool nc = false;
344   for(int i = 2; i < argc; i++){
345     if(!path && argv[i][0] == '-'){
346       if(!strcmp(argv[i], "-tl")){
347         opts |= BDBTLARGE;
348       } else if(!strcmp(argv[i], "-td")){
349         opts |= BDBTDEFLATE;
350       } else if(!strcmp(argv[i], "-tb")){
351         opts |= BDBTTCBS;
352       } else if(!strcmp(argv[i], "-nl")){
353         omode |= BDBONOLCK;
354       } else if(!strcmp(argv[i], "-nb")){
355         omode |= BDBOLCKNB;
356       } else if(!strcmp(argv[i], "-nc")){
357         nc = true;
358       } else {
359         usage();
360       }
361     } else if(!path){
362       path = argv[i];
363     } else if(!tstr){
364       tstr = argv[i];
365     } else if(!rstr){
366       rstr = argv[i];
367     } else {
368       usage();
369     }
370   }
371   if(!path || !tstr || !rstr) usage();
372   int tnum = atoi(tstr);
373   int rnum = atoi(rstr);
374   if(tnum < 1 || rnum < 1) usage();
375   int rv = procwicked(path, tnum, rnum, opts, omode, nc);
376   return rv;
377 }
378
379
380 /* parse arguments of typical command */
381 static int runtypical(int argc, char **argv){
382   char *path = NULL;
383   char *tstr = NULL;
384   char *rstr = NULL;
385   char *lmstr = NULL;
386   char *nmstr = NULL;
387   char *bstr = NULL;
388   char *astr = NULL;
389   char *fstr = NULL;
390   int opts = 0;
391   int omode = 0;
392   int rratio = -1;
393   bool nc = false;
394   for(int i = 2; i < argc; i++){
395     if(!path && argv[i][0] == '-'){
396       if(!strcmp(argv[i], "-tl")){
397         opts |= BDBTLARGE;
398       } else if(!strcmp(argv[i], "-td")){
399         opts |= BDBTDEFLATE;
400       } else if(!strcmp(argv[i], "-tb")){
401         opts |= BDBTTCBS;
402       } else if(!strcmp(argv[i], "-nl")){
403         omode |= BDBONOLCK;
404       } else if(!strcmp(argv[i], "-nb")){
405         omode |= BDBOLCKNB;
406       } else if(!strcmp(argv[i], "-nc")){
407         nc = true;
408       } else if(!strcmp(argv[i], "-rr")){
409         if(++i >= argc) usage();
410         rratio = atoi(argv[i]);
411       } else {
412         usage();
413       }
414     } else if(!path){
415       path = argv[i];
416     } else if(!tstr){
417       tstr = argv[i];
418     } else if(!rstr){
419       rstr = argv[i];
420     } else if(!lmstr){
421       lmstr = argv[i];
422     } else if(!nmstr){
423       nmstr = argv[i];
424     } else if(!bstr){
425       bstr = argv[i];
426     } else if(!astr){
427       astr = argv[i];
428     } else if(!fstr){
429       fstr = argv[i];
430     } else {
431       usage();
432     }
433   }
434   if(!path || !tstr || !rstr) usage();
435   int tnum = atoi(tstr);
436   int rnum = atoi(rstr);
437   if(tnum < 1 || rnum < 1) usage();
438   int lmemb = lmstr ? atoi(lmstr) : -1;
439   int nmemb = nmstr ? atoi(nmstr) : -1;
440   int bnum = bstr ? atoi(bstr) : -1;
441   int apow = astr ? atoi(astr) : -1;
442   int fpow = fstr ? atoi(fstr) : -1;
443   int rv = proctypical(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, nc, rratio);
444   return rv;
445 }
446
447
448 /* perform write command */
449 static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb,
450                      int bnum, int apow, int fpow, int opts, int omode, bool rnd){
451   iprintf("<Writing Test>\n  path=%s  tnum=%d  rnum=%d  lmemb=%d  nmemb=%d"
452           "  bnum=%d  apow=%d  fpow=%d  opts=%d  omode=%d  rnd=%d\n\n",
453           path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, rnd);
454   bool err = false;
455   double stime = tctime();
456   TCBDB *bdb = tcbdbnew();
457   if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
458   if(!tcbdbsetmutex(bdb)){
459     eprint(bdb, "tcbdbsetmutex");
460     err = true;
461   }
462   if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
463     eprint(bdb, "tcbdbtune");
464     err = true;
465   }
466   if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
467     eprint(bdb, "tcbdbopen");
468     err = true;
469   }
470   TARGWRITE targs[tnum];
471   pthread_t threads[tnum];
472   if(tnum == 1){
473     targs[0].bdb = bdb;
474     targs[0].rnum = rnum;
475     targs[0].rnd = rnd;
476     targs[0].id = 0;
477     if(threadwrite(targs) != NULL) err = true;
478   } else {
479     for(int i = 0; i < tnum; i++){
480       targs[i].bdb = bdb;
481       targs[i].rnum = rnum;
482       targs[i].rnd = rnd;
483       targs[i].id = i;
484       if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
485         eprint(bdb, "pthread_create");
486         targs[i].id = -1;
487         err = true;
488       }
489     }
490     for(int i = 0; i < tnum; i++){
491       if(targs[i].id == -1) continue;
492       void *rv;
493       if(pthread_join(threads[i], &rv) != 0){
494         eprint(bdb, "pthread_join");
495         err = true;
496       } else if(rv){
497         err = true;
498       }
499     }
500   }
501   iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
502   iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
503   mprint(bdb);
504   if(!tcbdbclose(bdb)){
505     eprint(bdb, "tcbdbclose");
506     err = true;
507   }
508   tcbdbdel(bdb);
509   iprintf("time: %.3f\n", tctime() - stime);
510   iprintf("%s\n\n", err ? "error" : "ok");
511   return err ? 1 : 0;
512 }
513
514
515 /* perform read command */
516 static int procread(const char *path, int tnum, int omode, bool wb, bool rnd){
517   iprintf("<Reading Test>\n  path=%s  tnum=%d  omode=%d  wb=%d  rnd=%d\n\n",
518           path, tnum, omode, wb, rnd);
519   bool err = false;
520   double stime = tctime();
521   TCBDB *bdb = tcbdbnew();
522   if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
523   if(!tcbdbsetmutex(bdb)){
524     eprint(bdb, "tcbdbsetmutex");
525     err = true;
526   }
527   if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
528     eprint(bdb, "tcbdbopen");
529     err = true;
530   }
531   int rnum = tcbdbrnum(bdb) / tnum;
532   TARGREAD targs[tnum];
533   pthread_t threads[tnum];
534   if(tnum == 1){
535     targs[0].bdb = bdb;
536     targs[0].rnum = rnum;
537     targs[0].wb = wb;
538     targs[0].rnd = rnd;
539     targs[0].id = 0;
540     if(threadread(targs) != NULL) err = true;
541   } else {
542     for(int i = 0; i < tnum; i++){
543       targs[i].bdb = bdb;
544       targs[i].rnum = rnum;
545       targs[i].wb = wb;
546       targs[i].rnd = rnd;
547       targs[i].id = i;
548       if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
549         eprint(bdb, "pthread_create");
550         targs[i].id = -1;
551         err = true;
552       }
553     }
554     for(int i = 0; i < tnum; i++){
555       if(targs[i].id == -1) continue;
556       void *rv;
557       if(pthread_join(threads[i], &rv) != 0){
558         eprint(bdb, "pthread_join");
559         err = true;
560       } else if(rv){
561         err = true;
562       }
563     }
564   }
565   iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
566   iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
567   mprint(bdb);
568   if(!tcbdbclose(bdb)){
569     eprint(bdb, "tcbdbclose");
570     err = true;
571   }
572   tcbdbdel(bdb);
573   iprintf("time: %.3f\n", tctime() - stime);
574   iprintf("%s\n\n", err ? "error" : "ok");
575   return err ? 1 : 0;
576 }
577
578
579 /* perform remove command */
580 static int procremove(const char *path, int tnum, int omode, bool rnd){
581   iprintf("<Removing Test>\n  path=%s  tnum=%d  omode=%d  rnd=%d\n\n", path, tnum, omode, rnd);
582   bool err = false;
583   double stime = tctime();
584   TCBDB *bdb = tcbdbnew();
585   if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
586   if(!tcbdbsetmutex(bdb)){
587     eprint(bdb, "tcbdbsetmutex");
588     err = true;
589   }
590   if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
591     eprint(bdb, "tcbdbopen");
592     err = true;
593   }
594   int rnum = tcbdbrnum(bdb) / tnum;
595   TARGREMOVE targs[tnum];
596   pthread_t threads[tnum];
597   if(tnum == 1){
598     targs[0].bdb = bdb;
599     targs[0].rnum = rnum;
600     targs[0].rnd = rnd;
601     targs[0].id = 0;
602     if(threadremove(targs) != NULL) err = true;
603   } else {
604     for(int i = 0; i < tnum; i++){
605       targs[i].bdb = bdb;
606       targs[i].rnum = rnum;
607       targs[i].rnd = rnd;
608       targs[i].id = i;
609       if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
610         eprint(bdb, "pthread_create");
611         targs[i].id = -1;
612         err = true;
613       }
614     }
615     for(int i = 0; i < tnum; i++){
616       if(targs[i].id == -1) continue;
617       void *rv;
618       if(pthread_join(threads[i], &rv) != 0){
619         eprint(bdb, "pthread_join");
620         err = true;
621       } else if(rv){
622         err = true;
623       }
624     }
625   }
626   iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
627   iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
628   mprint(bdb);
629   if(!tcbdbclose(bdb)){
630     eprint(bdb, "tcbdbclose");
631     err = true;
632   }
633   tcbdbdel(bdb);
634   iprintf("time: %.3f\n", tctime() - stime);
635   iprintf("%s\n\n", err ? "error" : "ok");
636   return err ? 1 : 0;
637 }
638
639
640 /* perform wicked command */
641 static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc){
642   iprintf("<Writing Test>\n  path=%s  tnum=%d  rnum=%d  opts=%d  omode=%d  nc=%d\n\n",
643           path, tnum, rnum, opts, omode, nc);
644   bool err = false;
645   double stime = tctime();
646   TCBDB *bdb = tcbdbnew();
647   if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
648   if(!tcbdbsetmutex(bdb)){
649     eprint(bdb, "tcbdbsetmutex");
650     err = true;
651   }
652   if(!tcbdbtune(bdb, 10, 10, rnum / 50, 10, -1, opts)){
653     eprint(bdb, "tcbdbtune");
654     err = true;
655   }
656   if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
657     eprint(bdb, "tcbdbopen");
658     err = true;
659   }
660   TARGWICKED targs[tnum];
661   pthread_t threads[tnum];
662   TCMAP *map = tcmapnew();
663   if(tnum == 1){
664     targs[0].bdb = bdb;
665     targs[0].rnum = rnum;
666     targs[0].nc = nc;
667     targs[0].id = 0;
668     targs[0].map = map;
669     if(threadwicked(targs) != NULL) err = true;
670   } else {
671     for(int i = 0; i < tnum; i++){
672       targs[i].bdb = bdb;
673       targs[i].rnum = rnum;
674       targs[i].nc = nc;
675       targs[i].id = i;
676       targs[i].map = map;
677       targs[i].map = map;
678       if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){
679         eprint(bdb, "pthread_create");
680         targs[i].id = -1;
681         err = true;
682       }
683     }
684     for(int i = 0; i < tnum; i++){
685       if(targs[i].id == -1) continue;
686       void *rv;
687       if(pthread_join(threads[i], &rv) != 0){
688         eprint(bdb, "pthread_join");
689         err = true;
690       } else if(rv){
691         err = true;
692       }
693     }
694   }
695   if(!nc){
696     if(!tcbdbsync(bdb)){
697       eprint(bdb, "tcbdbsync");
698       err = true;
699     }
700     if(tcbdbrnum(bdb) != tcmaprnum(map)){
701       eprint(bdb, "(validation)");
702       err = true;
703     }
704     int end = rnum * tnum;
705     for(int i = 1; i <= end && !err; i++){
706       char kbuf[RECBUFSIZ];
707       int ksiz = sprintf(kbuf, "%d", i - 1);
708       int vsiz;
709       const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
710       int rsiz;
711       char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
712       if(vbuf){
713         putchar('.');
714         if(!rbuf){
715           eprint(bdb, "tcbdbget");
716           err = true;
717         } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
718           eprint(bdb, "(validation)");
719           err = true;
720         }
721       } else {
722         putchar('*');
723         if(rbuf || tcbdbecode(bdb) != TCENOREC){
724           eprint(bdb, "(validation)");
725           err = true;
726         }
727       }
728       free(rbuf);
729       if(i % 50 == 0) iprintf(" (%08d)\n", i);
730     }
731     if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
732   }
733   tcmapdel(map);
734   iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
735   iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
736   mprint(bdb);
737   if(!tcbdbclose(bdb)){
738     eprint(bdb, "tcbdbclose");
739     err = true;
740   }
741   tcbdbdel(bdb);
742   iprintf("time: %.3f\n", tctime() - stime);
743   iprintf("%s\n\n", err ? "error" : "ok");
744   return err ? 1 : 0;
745 }
746
747
748 /* perform typical command */
749 static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb,
750                        int bnum, int apow, int fpow, int opts, int omode, bool nc, int rratio){
751   iprintf("<Typical Access Test>\n  path=%s  tnum=%d  rnum=%d  lmemb=%d  nmemb=%d"
752           "  bnum=%d  apow=%d  fpow=%d  opts=%d  omode=%d  nc=%d  rratio=%d\n\n",
753           path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, nc, rratio);
754   bool err = false;
755   double stime = tctime();
756   TCBDB *bdb = tcbdbnew();
757   if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
758   if(!tcbdbsetmutex(bdb)){
759     eprint(bdb, "tcbdbsetmutex");
760     err = true;
761   }
762   if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
763     eprint(bdb, "tcbdbtune");
764     err = true;
765   }
766   if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
767     eprint(bdb, "tcbdbopen");
768     err = true;
769   }
770   TARGTYPICAL targs[tnum];
771   pthread_t threads[tnum];
772   if(tnum == 1){
773     targs[0].bdb = bdb;
774     targs[0].rnum = rnum;
775     targs[0].nc = nc;
776     targs[0].rratio = rratio;
777     targs[0].id = 0;
778     if(threadtypical(targs) != NULL) err = true;
779   } else {
780     for(int i = 0; i < tnum; i++){
781       targs[i].bdb = bdb;
782       targs[i].rnum = rnum;
783       targs[i].nc = nc;
784       targs[i].rratio = rratio;
785       targs[i].id = i;
786       if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
787         eprint(bdb, "pthread_create");
788         targs[i].id = -1;
789         err = true;
790       }
791     }
792     for(int i = 0; i < tnum; i++){
793       if(targs[i].id == -1) continue;
794       void *rv;
795       if(pthread_join(threads[i], &rv) != 0){
796         eprint(bdb, "pthread_join");
797         err = true;
798       } else if(rv){
799         err = true;
800       }
801     }
802   }
803   iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
804   iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
805   mprint(bdb);
806   if(!tcbdbclose(bdb)){
807     eprint(bdb, "tcbdbclose");
808     err = true;
809   }
810   tcbdbdel(bdb);
811   iprintf("time: %.3f\n", tctime() - stime);
812   iprintf("%s\n\n", err ? "error" : "ok");
813   return err ? 1 : 0;
814 }
815
816
817 /* thread the write function */
818 static void *threadwrite(void *targ){
819   TCBDB *bdb = ((TARGWRITE *)targ)->bdb;
820   int rnum = ((TARGWRITE *)targ)->rnum;
821   bool rnd = ((TARGWRITE *)targ)->rnd;
822   int id = ((TARGWRITE *)targ)->id;
823   bool err = false;
824   int base = id * rnum;
825   for(int i = 1; i <= rnum; i++){
826     char buf[RECBUFSIZ];
827     int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
828     if(!tcbdbput(bdb, buf, len, buf, len)){
829       eprint(bdb, "tcbdbput");
830       err = true;
831       break;
832     }
833     if(id <= 0 && rnum > 250 && i % (rnum / 250) == 0){
834       putchar('.');
835       fflush(stdout);
836       if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
837     }
838   }
839   return err ? "error" : NULL;
840 }
841
842
843 /* thread the read function */
844 static void *threadread(void *targ){
845   TCBDB *bdb = ((TARGREAD *)targ)->bdb;
846   int rnum = ((TARGREAD *)targ)->rnum;
847   bool wb = ((TARGREAD *)targ)->wb;
848   bool rnd = ((TARGREAD *)targ)->rnd;
849   int id = ((TARGREAD *)targ)->id;
850   bool err = false;
851   int base = id * rnum;
852   for(int i = 1; i <= rnum && !err; i++){
853     char kbuf[RECBUFSIZ];
854     int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) : i));
855     int vsiz;
856     if(wb){
857       int vsiz;
858       const char *vbuf = tcbdbget3(bdb, kbuf, ksiz, &vsiz);
859       if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){
860         eprint(bdb, "tcbdbget3");
861         err = true;
862       }
863     } else {
864       char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
865       if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){
866         eprint(bdb, "tcbdbget");
867         err = true;
868       }
869       free(vbuf);
870     }
871     if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
872       putchar('.');
873       fflush(stdout);
874       if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
875     }
876   }
877   return err ? "error" : NULL;
878 }
879
880
881 /* thread the remove function */
882 static void *threadremove(void *targ){
883   TCBDB *bdb = ((TARGREMOVE *)targ)->bdb;
884   int rnum = ((TARGREMOVE *)targ)->rnum;
885   bool rnd = ((TARGREMOVE *)targ)->rnd;
886   int id = ((TARGREMOVE *)targ)->id;
887   bool err = false;
888   int base = id * rnum;
889   for(int i = 1; i <= rnum; i++){
890     char kbuf[RECBUFSIZ];
891     int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) : i));
892     if(!tcbdbout(bdb, kbuf, ksiz) && (!rnd || tcbdbecode(bdb) != TCENOREC)){
893       eprint(bdb, "tcbdbout");
894       err = true;
895       break;
896     }
897     if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
898       putchar('.');
899       fflush(stdout);
900       if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
901     }
902   }
903   return err ? "error" : NULL;
904 }
905
906
907 /* thread the wicked function */
908 static void *threadwicked(void *targ){
909   TCBDB *bdb = ((TARGWICKED *)targ)->bdb;
910   int rnum = ((TARGWICKED *)targ)->rnum;
911   bool nc = ((TARGWICKED *)targ)->nc;
912   int id = ((TARGWICKED *)targ)->id;
913   TCMAP *map = ((TARGWICKED *)targ)->map;
914   BDBCUR *cur = tcbdbcurnew(bdb);
915   bool err = false;
916   for(int i = 1; i <= rnum && !err; i++){
917     char kbuf[RECBUFSIZ];
918     int ksiz = sprintf(kbuf, "%d", myrand(rnum * (id + 1)));
919     char vbuf[RECBUFSIZ];
920     int vsiz = myrand(RECBUFSIZ);
921     memset(vbuf, '*', vsiz);
922     vbuf[vsiz] = '\0';
923     char *rbuf;
924     if(!nc) tcglobalmutexlock();
925     switch(myrand(16)){
926     case 0:
927       if(id == 0) putchar('0');
928       if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
929         eprint(bdb, "tcbdbput");
930         err = true;
931       }
932       if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
933       break;
934     case 1:
935       if(id == 0) putchar('1');
936       if(!tcbdbput2(bdb, kbuf, vbuf)){
937         eprint(bdb, "tcbdbput2");
938         err = true;
939       }
940       if(!nc) tcmapput2(map, kbuf, vbuf);
941       break;
942     case 2:
943       if(id == 0) putchar('2');
944       if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){
945         eprint(bdb, "tcbdbputkeep");
946         err = true;
947       }
948       if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
949       break;
950     case 3:
951       if(id == 0) putchar('3');
952       if(!tcbdbputkeep2(bdb, kbuf, vbuf) && tcbdbecode(bdb) != TCEKEEP){
953         eprint(bdb, "tcbdbputkeep2");
954         err = true;
955       }
956       if(!nc) tcmapputkeep2(map, kbuf, vbuf);
957       break;
958     case 4:
959       if(id == 0) putchar('4');
960       if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
961         eprint(bdb, "tcbdbputcat");
962         err = true;
963       }
964       if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
965       break;
966     case 5:
967       if(id == 0) putchar('5');
968       if(!tcbdbputcat2(bdb, kbuf, vbuf)){
969         eprint(bdb, "tcbdbputcat2");
970         err = true;
971       }
972       if(!nc) tcmapputcat2(map, kbuf, vbuf);
973       break;
974     case 6:
975       if(id == 0) putchar('6');
976       if(nc){
977         if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){
978           eprint(bdb, "tcbdbputdup");
979           err = true;
980         }
981       }
982       break;
983     case 7:
984       if(id == 0) putchar('7');
985       if(nc){
986         if(!tcbdbputdup2(bdb, kbuf, vbuf)){
987           eprint(bdb, "tcbdbputdup2");
988           err = true;
989         }
990       }
991       break;
992     case 8:
993       if(id == 0) putchar('8');
994       if(myrand(10) == 0){
995         if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
996           eprint(bdb, "tcbdbout");
997           err = true;
998         }
999         if(!nc) tcmapout(map, kbuf, ksiz);
1000       }
1001       break;
1002     case 9:
1003       if(id == 0) putchar('9');
1004       if(myrand(10) == 0){
1005         if(!tcbdbout2(bdb, kbuf) && tcbdbecode(bdb) != TCENOREC){
1006           eprint(bdb, "tcbdbout2");
1007           err = true;
1008         }
1009         if(!nc) tcmapout2(map, kbuf);
1010       }
1011       break;
1012     case 10:
1013       if(id == 0) putchar('A');
1014       if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz))){
1015         if(tcbdbecode(bdb) != TCENOREC){
1016           eprint(bdb, "tcbdbget");
1017           err = true;
1018         }
1019         rbuf = tcsprintf("[%d]", myrand(i + 1));
1020         vsiz = strlen(rbuf);
1021       }
1022       vsiz += myrand(vsiz);
1023       if(myrand(3) == 0) vsiz += PATH_MAX;
1024       rbuf = tcrealloc(rbuf, vsiz + 1);
1025       for(int j = 0; j < vsiz; j++){
1026         rbuf[j] = myrand(0x100);
1027       }
1028       if(!tcbdbput(bdb, kbuf, ksiz, rbuf, vsiz)){
1029         eprint(bdb, "tcbdbput");
1030         err = true;
1031       }
1032       if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz);
1033       free(rbuf);
1034       break;
1035     case 11:
1036       if(id == 0) putchar('B');
1037       if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz)) && tcbdbecode(bdb) != TCENOREC){
1038         eprint(bdb, "tcbdbget");
1039         err = true;
1040       }
1041       free(rbuf);
1042       break;
1043     case 12:
1044       if(id == 0) putchar('C');
1045       if(!(rbuf = tcbdbget2(bdb, kbuf)) && tcbdbecode(bdb) != TCENOREC){
1046         eprint(bdb, "tcbdbget");
1047         err = true;
1048       }
1049       free(rbuf);
1050       break;
1051     case 13:
1052       if(id == 0) putchar('D');
1053       if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz) && tcbdbecode(bdb) != TCENOREC){
1054         eprint(bdb, "tcbdbget");
1055         err = true;
1056       }
1057       break;
1058     case 14:
1059       if(id == 0) putchar('E');
1060       if(myrand(rnum / 50) == 0){
1061         switch(myrand(5)){
1062         case 0:
1063           if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
1064             eprint(bdb, "tcbdbcurfirst");
1065             err = true;
1066           }
1067           break;
1068         case 1:
1069           if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
1070             eprint(bdb, "tcbdbcurfirst");
1071             err = true;
1072           }
1073           break;
1074         default:
1075           if(!tcbdbcurjump(cur, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
1076             eprint(bdb, "tcbdbcurjump");
1077             err = true;
1078           }
1079           break;
1080         }
1081       }
1082       TCXSTR *ikey = tcxstrnew();
1083       TCXSTR *ival = tcxstrnew();
1084       for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
1085         if(j % 3 == 0){
1086           if(!tcbdbcurrec(cur, ikey, ival)){
1087             int ecode = tcbdbecode(bdb);
1088             if(ecode != TCEINVALID && ecode != TCENOREC){
1089               eprint(bdb, "tcbdbcurrec");
1090               err = true;
1091             }
1092           }
1093         } else {
1094           int iksiz;
1095           if(!tcbdbcurkey3(cur, &iksiz)){
1096             int ecode = tcbdbecode(bdb);
1097             if(ecode != TCEINVALID && ecode != TCENOREC){
1098               eprint(bdb, "tcbdbcurkey3");
1099               err = true;
1100             }
1101           }
1102         }
1103         if(myrand(5) == 0){
1104           if(!tcbdbcurprev(cur)){
1105             int ecode = tcbdbecode(bdb);
1106             if(ecode != TCEINVALID && ecode != TCENOREC){
1107               eprint(bdb, "tcbdbcurprev");
1108               err = true;
1109             }
1110           }
1111         } else {
1112           if(!tcbdbcurnext(cur)){
1113             int ecode = tcbdbecode(bdb);
1114             if(ecode != TCEINVALID && ecode != TCENOREC){
1115               eprint(bdb, "tcbdbcurnext");
1116               err = true;
1117             }
1118           }
1119         }
1120       }
1121       tcxstrdel(ival);
1122       tcxstrdel(ikey);
1123       break;
1124     default:
1125       if(id == 0) putchar('@');
1126       if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
1127       break;
1128     }
1129     if(!nc) tcglobalmutexunlock();
1130     if(id == 0){
1131       if(i % 50 == 0) iprintf(" (%08d)\n", i);
1132       if(id == 0 && i == rnum / 4){
1133         if(!tcbdboptimize(bdb, -1, -1, -1, -1, -1, -1)){
1134           eprint(bdb, "tcbdboptimize");
1135           err = true;
1136         }
1137         if(!tcbdbcurfirst(cur)){
1138           eprint(bdb, "tcbdbcurfirst");
1139           err = true;
1140         }
1141       }
1142     }
1143   }
1144   tcbdbcurdel(cur);
1145   return err ? "error" : NULL;
1146 }
1147
1148
1149 /* thread the typical function */
1150 static void *threadtypical(void *targ){
1151   TCBDB *bdb = ((TARGTYPICAL *)targ)->bdb;
1152   int rnum = ((TARGTYPICAL *)targ)->rnum;
1153   bool nc = ((TARGTYPICAL *)targ)->nc;
1154   int rratio = ((TARGTYPICAL *)targ)->rratio;
1155   int id = ((TARGTYPICAL *)targ)->id;
1156   bool err = false;
1157   TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL;
1158   int base = id * rnum;
1159   int mrange = tclmax(50 + rratio, 100);
1160   BDBCUR *cur = tcbdbcurnew(bdb);
1161   for(int i = 1; !err && i <= rnum; i++){
1162     char buf[RECBUFSIZ];
1163     int len = sprintf(buf, "%08d", base + myrandnd(i));
1164     int rnd = myrand(mrange);
1165     if(rnd < 10){
1166       if(!tcbdbput(bdb, buf, len, buf, len)){
1167         eprint(bdb, "tcbdbput");
1168         err = true;
1169       }
1170       if(map) tcmapput(map, buf, len, buf, len);
1171     } else if(rnd < 15){
1172       if(!tcbdbputkeep(bdb, buf, len, buf, len) && tcbdbecode(bdb) != TCEKEEP){
1173         eprint(bdb, "tcbdbputkeep");
1174         err = true;
1175       }
1176       if(map) tcmapputkeep(map, buf, len, buf, len);
1177     } else if(rnd < 20){
1178       if(!tcbdbputcat(bdb, buf, len, buf, len)){
1179         eprint(bdb, "tcbdbputcat");
1180         err = true;
1181       }
1182       if(map) tcmapputcat(map, buf, len, buf, len);
1183     } else if(rnd < 25){
1184       if(!tcbdbout(bdb, buf, len) && tcbdbecode(bdb) && tcbdbecode(bdb) != TCENOREC){
1185         eprint(bdb, "tcbdbout");
1186         err = true;
1187       }
1188       if(map) tcmapout(map, buf, len);
1189     } else if(rnd < 27){
1190       switch(myrand(3)){
1191       case 0:
1192         if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
1193           eprint(bdb, "tcbdbcurfirst");
1194           err = true;
1195         }
1196         for(int j = 0; !err && j < 10; j++){
1197           int ksiz;
1198           char *kbuf = tcbdbcurkey(cur, &ksiz);
1199           if(kbuf){
1200             int vsiz;
1201             char *vbuf = tcbdbcurval(cur, &vsiz);
1202             if(vbuf){
1203               free(vbuf);
1204             } else if(tcbdbecode(bdb) != TCENOREC){
1205               eprint(bdb, "tcbdbcurval");
1206               err = true;
1207             }
1208             free(kbuf);
1209           } else if(tcbdbecode(bdb) != TCENOREC){
1210             eprint(bdb, "tcbdbcurkey");
1211             err = true;
1212           }
1213           tcbdbcurnext(cur);
1214         }
1215         break;
1216       case 1:
1217         if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
1218           eprint(bdb, "tcbdbcurlast");
1219           err = true;
1220         }
1221         for(int j = 0; !err && j < 10; j++){
1222           int ksiz;
1223           char *kbuf = tcbdbcurkey(cur, &ksiz);
1224           if(kbuf){
1225             int vsiz;
1226             char *vbuf = tcbdbcurval(cur, &vsiz);
1227             if(vbuf){
1228               free(vbuf);
1229             } else if(tcbdbecode(bdb) != TCENOREC){
1230               eprint(bdb, "tcbdbcurval");
1231               err = true;
1232             }
1233             free(kbuf);
1234           } else if(tcbdbecode(bdb) != TCENOREC){
1235             eprint(bdb, "tcbdbcurkey");
1236             err = true;
1237           }
1238           tcbdbcurprev(cur);
1239         }
1240         break;
1241       case 2:
1242         if(!tcbdbcurjump(cur, buf, len) && tcbdbecode(bdb) != TCENOREC){
1243           eprint(bdb, "tcbdbcurjump");
1244           err = true;
1245         }
1246         for(int j = 0; !err && j < 10; j++){
1247           int ksiz;
1248           char *kbuf = tcbdbcurkey(cur, &ksiz);
1249           if(kbuf){
1250             int vsiz;
1251             char *vbuf = tcbdbcurval(cur, &vsiz);
1252             if(vbuf){
1253               free(vbuf);
1254             } else if(tcbdbecode(bdb) != TCENOREC){
1255               eprint(bdb, "tcbdbcurval");
1256               err = true;
1257             }
1258             free(kbuf);
1259           } else if(tcbdbecode(bdb) != TCENOREC){
1260             eprint(bdb, "tcbdbcurkey");
1261             err = true;
1262           }
1263           tcbdbcurnext(cur);
1264         }
1265         break;
1266       }
1267     } else {
1268       int vsiz;
1269       char *vbuf = tcbdbget(bdb, buf, len, &vsiz);
1270       if(vbuf){
1271         if(map){
1272           int msiz;
1273           const char *mbuf = tcmapget(map, buf, len, &msiz);
1274           if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
1275             eprint(bdb, "(validation)");
1276             err = true;
1277           }
1278         }
1279         free(vbuf);
1280       } else {
1281         if(tcbdbecode(bdb) != TCENOREC){
1282           eprint(bdb, "tcbdbget");
1283           err = true;
1284         }
1285         if(map && tcmapget(map, buf, len, &vsiz)){
1286           eprint(bdb, "(validation)");
1287           err = true;
1288         }
1289       }
1290     }
1291     if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
1292       putchar('.');
1293       fflush(stdout);
1294       if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
1295     }
1296   }
1297   tcbdbcurdel(cur);
1298   if(map){
1299     tcmapiterinit(map);
1300     int ksiz;
1301     const char *kbuf;
1302     while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
1303       int vsiz;
1304       char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
1305       if(vbuf){
1306         int msiz;
1307         const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
1308         if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
1309           eprint(bdb, "(validation)");
1310           err = true;
1311         }
1312         free(vbuf);
1313       } else {
1314         eprint(bdb, "(validation)");
1315         err = true;
1316       }
1317     }
1318     tcmapdel(map);
1319   }
1320   return err ? "error" : NULL;
1321 }
1322
1323
1324
1325 // END OF FILE