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