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 *************************************************************************************************/
21 #define RECBUFSIZ 32 // buffer for records
23 typedef struct { // type of structure for write thread
31 typedef struct { // type of structure for read thread
39 typedef struct { // type of structure for remove thread
46 typedef struct { // type of structure for wicked thread
54 typedef struct { // type of structure for typical thread
63 /* global variables */
64 const char *g_progname; // program name
65 int g_dbgfd; // debugging output
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);
96 int main(int argc, char **argv){
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();
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);
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",
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");
139 /* print formatted information string and flush the buffer */
140 static void iprintf(const char *format, ...){
142 va_start(ap, format);
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));
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);
180 /* get a random number */
181 static int myrand(int range){
182 return (int)((double)range * rand() / (RAND_MAX + 1.0));
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;
193 /* parse arguments of write command */
194 static int runwrite(int argc, char **argv){
205 for(int i = 2; i < argc; i++){
206 if(!path && argv[i][0] == '-'){
207 if(!strcmp(argv[i], "-tl")){
209 } else if(!strcmp(argv[i], "-td")){
211 } else if(!strcmp(argv[i], "-tb")){
213 } else if(!strcmp(argv[i], "-nl")){
215 } else if(!strcmp(argv[i], "-nb")){
217 } else if(!strcmp(argv[i], "-as")){
219 } else if(!strcmp(argv[i], "-rnd")){
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);
252 /* parse arguments of read command */
253 static int runread(int argc, char **argv){
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")){
267 } else if(!strcmp(argv[i], "-nb")){
269 } else if(!strcmp(argv[i], "-wb")){
271 } else if(!strcmp(argv[i], "-rnd")){
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);
292 /* parse arguments of remove command */
293 static int runremove(int argc, char **argv){
298 for(int i = 2; i < argc; i++){
299 if(!path && argv[i][0] == '-'){
300 if(!strcmp(argv[i], "-nl")){
302 } else if(!strcmp(argv[i], "-nb")){
304 } else if(!strcmp(argv[i], "-rnd")){
317 if(!path || !tstr) usage();
318 int tnum = atoi(tstr);
319 if(tnum < 1) usage();
320 int rv = procremove(path, tnum, omode, rnd);
325 /* parse arguments of wicked command */
326 static int runwicked(int argc, char **argv){
333 for(int i = 2; i < argc; i++){
334 if(!path && argv[i][0] == '-'){
335 if(!strcmp(argv[i], "-tl")){
337 } else if(!strcmp(argv[i], "-td")){
339 } else if(!strcmp(argv[i], "-tb")){
341 } else if(!strcmp(argv[i], "-nl")){
343 } else if(!strcmp(argv[i], "-nb")){
345 } else if(!strcmp(argv[i], "-nc")){
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);
369 /* parse arguments of typical command */
370 static int runtypical(int argc, char **argv){
382 for(int i = 2; i < argc; i++){
383 if(!path && argv[i][0] == '-'){
384 if(!strcmp(argv[i], "-tl")){
386 } else if(!strcmp(argv[i], "-td")){
388 } else if(!strcmp(argv[i], "-tb")){
390 } else if(!strcmp(argv[i], "-rc")){
391 if(++i >= argc) usage();
392 rcnum = atoi(argv[i]);
393 } else if(!strcmp(argv[i], "-nl")){
395 } else if(!strcmp(argv[i], "-nb")){
397 } else if(!strcmp(argv[i], "-nc")){
399 } else if(!strcmp(argv[i], "-rr")){
400 if(++i >= argc) usage();
401 rratio = atoi(argv[i]);
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);
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);
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");
447 if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
448 eprint(hdb, "tchdbtune");
451 if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
452 eprint(hdb, "tchdbopen");
455 TARGWRITE targs[tnum];
456 pthread_t threads[tnum];
459 targs[0].rnum = rnum;
463 if(threadwrite(targs) != NULL) err = true;
465 for(int i = 0; i < tnum; i++){
467 targs[i].rnum = rnum;
471 if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
472 eprint(hdb, "pthread_create");
477 for(int i = 0; i < tnum; i++){
478 if(targs[i].id == -1) continue;
480 if(pthread_join(threads[i], &rv) != 0){
481 eprint(hdb, "pthread_join");
488 iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
489 iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
491 if(!tchdbclose(hdb)){
492 eprint(hdb, "tchdbclose");
496 iprintf("time: %.3f\n", tctime() - stime);
497 iprintf("%s\n\n", err ? "error" : "ok");
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);
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");
514 if(!tchdbsetcache(hdb, rcnum)){
515 eprint(hdb, "tchdbsetcache");
518 if(!tchdbopen(hdb, path, HDBOREADER | omode)){
519 eprint(hdb, "tchdbopen");
522 int rnum = tchdbrnum(hdb) / tnum;
523 TARGREAD targs[tnum];
524 pthread_t threads[tnum];
527 targs[0].rnum = rnum;
531 if(threadread(targs) != NULL) err = true;
533 for(int i = 0; i < tnum; i++){
535 targs[i].rnum = rnum;
539 if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
540 eprint(hdb, "pthread_create");
545 for(int i = 0; i < tnum; i++){
546 if(targs[i].id == -1) continue;
548 if(pthread_join(threads[i], &rv) != 0){
549 eprint(hdb, "pthread_join");
556 iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
557 iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
559 if(!tchdbclose(hdb)){
560 eprint(hdb, "tchdbclose");
564 iprintf("time: %.3f\n", tctime() - stime);
565 iprintf("%s\n\n", err ? "error" : "ok");
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);
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");
581 if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
582 eprint(hdb, "tchdbopen");
585 int rnum = tchdbrnum(hdb) / tnum;
586 TARGREMOVE targs[tnum];
587 pthread_t threads[tnum];
590 targs[0].rnum = rnum;
593 if(threadremove(targs) != NULL) err = true;
595 for(int i = 0; i < tnum; i++){
597 targs[i].rnum = rnum;
600 if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
601 eprint(hdb, "pthread_create");
606 for(int i = 0; i < tnum; i++){
607 if(targs[i].id == -1) continue;
609 if(pthread_join(threads[i], &rv) != 0){
610 eprint(hdb, "pthread_join");
617 iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
618 iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
620 if(!tchdbclose(hdb)){
621 eprint(hdb, "tchdbclose");
625 iprintf("time: %.3f\n", tctime() - stime);
626 iprintf("%s\n\n", err ? "error" : "ok");
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);
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");
643 if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){
644 eprint(hdb, "tchdbtune");
647 if(!tchdbsetcache(hdb, rnum / 2)){
648 eprint(hdb, "tchdbsetcache");
651 if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
652 eprint(hdb, "tchdbopen");
655 if(!tchdbiterinit(hdb)){
656 eprint(hdb, "tchdbiterinit");
659 TARGWICKED targs[tnum];
660 pthread_t threads[tnum];
661 TCMAP *map = tcmapnew();
664 targs[0].rnum = rnum;
668 if(threadwicked(targs) != NULL) err = true;
670 for(int i = 0; i < tnum; i++){
672 targs[i].rnum = rnum;
677 if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){
678 eprint(hdb, "pthread_create");
683 for(int i = 0; i < tnum; i++){
684 if(targs[i].id == -1) continue;
686 if(pthread_join(threads[i], &rv) != 0){
687 eprint(hdb, "pthread_join");
696 eprint(hdb, "tchdbsync");
699 if(tchdbrnum(hdb) != tcmaprnum(map)){
700 eprint(hdb, "(validation)");
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);
708 const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
710 char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
714 eprint(hdb, "tchdbget");
716 } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
717 eprint(hdb, "(validation)");
722 if(rbuf || tchdbecode(hdb) != TCENOREC){
723 eprint(hdb, "(validation)");
728 if(i % 50 == 0) iprintf(" (%08d)\n", i);
730 if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
733 iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
734 iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
736 if(!tchdbclose(hdb)){
737 eprint(hdb, "tchdbclose");
741 iprintf("time: %.3f\n", tctime() - stime);
742 iprintf("%s\n\n", err ? "error" : "ok");
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);
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");
761 if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
762 eprint(hdb, "tchdbtune");
765 if(!tchdbsetcache(hdb, rcnum)){
766 eprint(hdb, "tchdbsetcache");
769 if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
770 eprint(hdb, "tchdbopen");
773 TARGTYPICAL targs[tnum];
774 pthread_t threads[tnum];
777 targs[0].rnum = rnum;
779 targs[0].rratio = rratio;
781 if(threadtypical(targs) != NULL) err = true;
783 for(int i = 0; i < tnum; i++){
785 targs[i].rnum = rnum;
787 targs[i].rratio= rratio;
789 if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
790 eprint(hdb, "pthread_create");
795 for(int i = 0; i < tnum; i++){
796 if(targs[i].id == -1) continue;
798 if(pthread_join(threads[i], &rv) != 0){
799 eprint(hdb, "pthread_join");
806 iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
807 iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
809 if(!tchdbclose(hdb)){
810 eprint(hdb, "tchdbclose");
814 iprintf("time: %.3f\n", tctime() - stime);
815 iprintf("%s\n\n", err ? "error" : "ok");
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;
828 int base = id * rnum;
829 for(int i = 1; i <= rnum; i++){
831 int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
833 if(!tchdbputasync(hdb, buf, len, buf, len)){
834 eprint(hdb, "tchdbput");
839 if(!tchdbput(hdb, buf, len, buf, len)){
840 eprint(hdb, "tchdbput");
845 if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
848 if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
851 return err ? "error" : NULL;
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;
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));
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");
876 char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
877 if(!vbuf && (!rnd || tchdbecode(hdb) != TCENOREC)){
878 eprint(hdb, "tchdbget");
883 if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
886 if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
889 return err ? "error" : NULL;
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;
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");
909 if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
912 if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
915 return err ? "error" : NULL;
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;
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);
935 if(!nc) tcglobalmutexlock();
938 if(id == 0) putchar('0');
939 if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
940 eprint(hdb, "tchdbput");
943 if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
946 if(id == 0) putchar('1');
947 if(!tchdbput2(hdb, kbuf, vbuf)){
948 eprint(hdb, "tchdbput2");
951 if(!nc) tcmapput2(map, kbuf, vbuf);
954 if(id == 0) putchar('2');
955 if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){
956 eprint(hdb, "tchdbputkeep");
959 if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
962 if(id == 0) putchar('3');
963 if(!tchdbputkeep2(hdb, kbuf, vbuf) && tchdbecode(hdb) != TCEKEEP){
964 eprint(hdb, "tchdbputkeep2");
967 if(!nc) tcmapputkeep2(map, kbuf, vbuf);
970 if(id == 0) putchar('4');
971 if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
972 eprint(hdb, "tchdbputcat");
975 if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
978 if(id == 0) putchar('5');
979 if(!tchdbputcat2(hdb, kbuf, vbuf)){
980 eprint(hdb, "tchdbputcat2");
983 if(!nc) tcmapputcat2(map, kbuf, vbuf);
986 if(id == 0) putchar('6');
987 if(i > rnum / 4 * 3){
988 if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){
989 eprint(hdb, "tchdbputasync");
993 if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
994 eprint(hdb, "tchdbput");
998 if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
1001 if(id == 0) putchar('7');
1002 if(i > rnum / 4 * 3){
1003 if(!tchdbputasync2(hdb, kbuf, vbuf)){
1004 eprint(hdb, "tchdbputasync2");
1008 if(!tchdbput2(hdb, kbuf, vbuf)){
1009 eprint(hdb, "tchdbput2");
1013 if(!nc) tcmapput2(map, kbuf, vbuf);
1016 if(id == 0) putchar('8');
1017 if(myrand(10) == 0){
1018 if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
1019 eprint(hdb, "tchdbout");
1022 if(!nc) tcmapout(map, kbuf, ksiz);
1026 if(id == 0) putchar('9');
1027 if(myrand(10) == 0){
1028 if(!tchdbout2(hdb, kbuf) && tchdbecode(hdb) != TCENOREC){
1029 eprint(hdb, "tchdbout2");
1032 if(!nc) tcmapout2(map, kbuf);
1036 if(id == 0) putchar('A');
1037 if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz))){
1038 if(tchdbecode(hdb) != TCENOREC){
1039 eprint(hdb, "tchdbget");
1042 rbuf = tcsprintf("[%d]", myrand(i + 1));
1043 vsiz = strlen(rbuf);
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);
1051 if(!tchdbput(hdb, kbuf, ksiz, rbuf, vsiz)){
1052 eprint(hdb, "tchdbput");
1055 if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz);
1059 if(id == 0) putchar('B');
1060 if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz)) && tchdbecode(hdb) != TCENOREC){
1061 eprint(hdb, "tchdbget");
1067 if(id == 0) putchar('C');
1068 if(!(rbuf = tchdbget2(hdb, kbuf)) && tchdbecode(hdb) != TCENOREC){
1069 eprint(hdb, "tchdbget");
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");
1083 if(id == 0) putchar('E');
1084 if(myrand(rnum / 50) == 0){
1085 if(!tchdbiterinit(hdb)){
1086 eprint(hdb, "tchdbiterinit");
1090 TCXSTR *ikey = tcxstrnew();
1091 TCXSTR *ival = tcxstrnew();
1092 for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
1094 if(!tchdbiternext3(hdb, ikey, ival)){
1095 int ecode = tchdbecode(hdb);
1096 if(ecode != TCEINVALID && ecode != TCENOREC){
1097 eprint(hdb, "tchdbiternext3");
1103 char *ikbuf = tchdbiternext(hdb, &iksiz);
1107 int ecode = tchdbecode(hdb);
1108 if(ecode != TCEINVALID && ecode != TCENOREC){
1109 eprint(hdb, "tchdbiternext");
1119 if(id == 0) putchar('@');
1120 if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
1123 if(!nc) tcglobalmutexunlock();
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");
1131 if(!tchdbiterinit(hdb)){
1132 eprint(hdb, "tchdbiterinit");
1138 return err ? "error" : NULL;
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;
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);
1158 if(!tchdbput(hdb, buf, len, buf, len)){
1159 eprint(hdb, "tchdbput");
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");
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");
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");
1182 if(!tchdbput(hdb, buf, len, buf, len)){
1183 eprint(hdb, "tchdbput");
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");
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");
1199 for(int j = 0; !err && j < 10; j++){
1201 char *kbuf = tchdbiternext(hdb, &ksiz);
1204 } else if(tchdbecode(hdb) != TCEINVALID && tchdbecode(hdb) != TCENOREC){
1205 eprint(hdb, "tchdbiternext");
1211 char *vbuf = tchdbget(hdb, buf, len, &vsiz);
1215 const char *mbuf = tcmapget(map, buf, len, &msiz);
1216 if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
1217 eprint(hdb, "(validation)");
1223 if(tchdbecode(hdb) != TCENOREC){
1224 eprint(hdb, "tchdbget");
1227 if(map && tcmapget(map, buf, len, &vsiz)){
1228 eprint(hdb, "(validation)");
1233 if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
1236 if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
1243 while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
1245 char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
1248 const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
1249 if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
1250 eprint(hdb, "(validation)");
1255 eprint(hdb, "(validation)");
1261 return err ? "error" : NULL;