1 /*************************************************************************************************
2  * The test cases of the file tree database
3  *                                                      Copyright (C) 2009-2012 Mikio Hirabayashi
4  * This file is part of Kyoto Cabinet.
5  * This program is free software: you can redistribute it and/or modify it under the terms of
6  * the GNU General Public License as published by the Free Software Foundation, either version
7  * 3 of the License, or any later version.
8  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10  * See the GNU General Public License for more details.
11  * You should have received a copy of the GNU General Public License along with this program.
12  * If not, see <http://www.gnu.org/licenses/>.
13  *************************************************************************************************/
14 
15 
16 #include <kchashdb.h>
17 #include "cmdcommon.h"
18 
19 
20 // global variables
21 const char* g_progname;                  // program name
22 uint32_t g_randseed;                     // random seed
23 int64_t g_memusage;                      // memory usage
24 
25 
26 // function prototypes
27 int main(int argc, char** argv);
28 static void usage();
29 static void dberrprint(kc::BasicDB* db, int32_t line, const char* func);
30 static void dbmetaprint(kc::BasicDB* db, bool verbose);
31 static int32_t runorder(int argc, char** argv);
32 static int32_t runqueue(int argc, char** argv);
33 static int32_t runwicked(int argc, char** argv);
34 static int32_t runtran(int argc, char** argv);
35 static int32_t procorder(const char* path, int64_t rnum, int32_t thnum, bool rnd, int32_t mode,
36                          bool tran, int32_t oflags, int32_t apow, int32_t fpow,
37                          int32_t opts, int64_t bnum, int32_t psiz, int64_t msiz,
38                          int64_t dfunit, int64_t pccap, kc::Comparator* rcomp, bool lv);
39 static int32_t procqueue(const char* path, int64_t rnum, int32_t thnum, int32_t itnum, bool rnd,
40                          int32_t oflags, int32_t apow, int32_t fpow, int32_t opts, int64_t bnum,
41                          int32_t psiz, int64_t msiz, int64_t dfunit, int64_t pccap,
42                          kc::Comparator* rcomp, bool lv);
43 static int32_t procwicked(const char* path, int64_t rnum, int32_t thnum, int32_t itnum,
44                           int32_t oflags, int32_t apow, int32_t fpow, int32_t opts, int64_t bnum,
45                           int32_t psiz, int64_t msiz, int64_t dfunit, int64_t pccap,
46                           kc::Comparator* rcomp, bool lv);
47 static int32_t proctran(const char* path, int64_t rnum, int32_t thnum, int32_t itnum, bool hard,
48                         int32_t oflags, int32_t apow, int32_t fpow, int32_t opts, int64_t bnum,
49                         int32_t psiz, int64_t msiz, int64_t dfunit, int64_t pccap,
50                         kc::Comparator* rcomp, bool lv);
51 
52 
53 // main routine
main(int argc,char ** argv)54 int main(int argc, char** argv) {
55   g_progname = argv[0];
56   const char* ebuf = kc::getenv("KCRNDSEED");
57   g_randseed = ebuf ? (uint32_t)kc::atoi(ebuf) : (uint32_t)(kc::time() * 1000);
58   mysrand(g_randseed);
59   g_memusage = memusage();
60   kc::setstdiobin();
61   if (argc < 2) usage();
62   int32_t rv = 0;
63   if (!std::strcmp(argv[1], "order")) {
64     rv = runorder(argc, argv);
65   } else if (!std::strcmp(argv[1], "queue")) {
66     rv = runqueue(argc, argv);
67   } else if (!std::strcmp(argv[1], "wicked")) {
68     rv = runwicked(argc, argv);
69   } else if (!std::strcmp(argv[1], "tran")) {
70     rv = runtran(argc, argv);
71   } else {
72     usage();
73   }
74   if (rv != 0) {
75     oprintf("FAILED: KCRNDSEED=%u PID=%ld", g_randseed, (long)kc::getpid());
76     for (int32_t i = 0; i < argc; i++) {
77       oprintf(" %s", argv[i]);
78     }
79     oprintf("\n\n");
80   }
81   return rv;
82 }
83 
84 
85 // print the usage and exit
usage()86 static void usage() {
87   eprintf("%s: test cases of the file tree database of Kyoto Cabinet\n", g_progname);
88   eprintf("\n");
89   eprintf("usage:\n");
90   eprintf("  %s order [-th num] [-rnd] [-set|-get|-getw|-rem|-etc] [-tran]"
91           " [-oat|-oas|-onl|-otl|-onr] [-apow num] [-fpow num] [-ts] [-tl] [-tc] [-bnum num]"
92           " [-psiz num] [-msiz num] [-dfunit num] [-pccap num] [-rcd|-rcld|-rcdd] [-lv]"
93           " path rnum\n", g_progname);
94   eprintf("  %s queue [-th num] [-it num] [-rnd] [-oat|-oas|-onl|-otl|-onr]"
95           " [-apow num] [-fpow num] [-ts] [-tl] [-tc] [-bnum num] [-psiz num] [-msiz num]"
96           " [-dfunit num] [-pccap num] [-rcd|-rcld|-rcdd] [-lv] path rnum\n", g_progname);
97   eprintf("  %s wicked [-th num] [-it num] [-oat|-oas|-onl|-otl|-onr]"
98           " [-apow num] [-fpow num] [-ts] [-tl] [-tc] [-bnum num] [-psiz num] [-msiz num]"
99           " [-dfunit num] [-pccap num] [-rcd|-rcld|-rcdd] [-lv] path rnum\n", g_progname);
100   eprintf("  %s tran [-th num] [-it num] [-hard] [-oat|-oas|-onl|-otl|-onr]"
101           " [-apow num] [-fpow num] [-ts] [-tl] [-tc] [-bnum num] [-psiz num] [-msiz num]"
102           " [-dfunit num] [-pccap num] [-rcd|-rcld|-rcdd] [-lv] path rnum\n", g_progname);
103   eprintf("\n");
104   std::exit(1);
105 }
106 
107 
108 // print the error message of a database
dberrprint(kc::BasicDB * db,int32_t line,const char * func)109 static void dberrprint(kc::BasicDB* db, int32_t line, const char* func) {
110   const kc::BasicDB::Error& err = db->error();
111   oprintf("%s: %d: %s: %s: %d: %s: %s\n",
112           g_progname, line, func, db->path().c_str(), err.code(), err.name(), err.message());
113 }
114 
115 
116 // print members of a database
dbmetaprint(kc::BasicDB * db,bool verbose)117 static void dbmetaprint(kc::BasicDB* db, bool verbose) {
118   if (verbose) {
119     std::map<std::string, std::string> status;
120     status["opaque"] = "";
121     status["fbpnum_used"] = "";
122     status["bnum_used"] = "";
123     status["cusage_lcnt"] = "";
124     status["cusage_lsiz"] = "";
125     status["cusage_icnt"] = "";
126     status["cusage_isiz"] = "";
127     status["tree_level"] = "";
128     if (db->status(&status)) {
129       uint32_t type = kc::atoi(status["type"].c_str());
130       oprintf("type: %s (%s) (type=0x%02X)\n",
131               kc::BasicDB::typecname(type), kc::BasicDB::typestring(type), type);
132       uint32_t chksum = kc::atoi(status["chksum"].c_str());
133       oprintf("format version: %s (libver=%s.%s) (chksum=0x%02X)\n", status["fmtver"].c_str(),
134               status["libver"].c_str(), status["librev"].c_str(), chksum);
135       oprintf("path: %s\n", status["path"].c_str());
136       int32_t flags = kc::atoi(status["flags"].c_str());
137       oprintf("status flags:");
138       if (flags & kc::TreeDB::FOPEN) oprintf(" open");
139       if (flags & kc::TreeDB::FFATAL) oprintf(" fatal");
140       oprintf(" (flags=%d)", flags);
141       if (kc::atoi(status["recovered"].c_str()) > 0) oprintf(" (recovered)");
142       if (kc::atoi(status["reorganized"].c_str()) > 0) oprintf(" (reorganized)");
143       if (kc::atoi(status["trimmed"].c_str()) > 0) oprintf(" (trimmed)");
144       oprintf("\n", flags);
145       int32_t apow = kc::atoi(status["apow"].c_str());
146       oprintf("alignment: %d (apow=%d)\n", 1 << apow, apow);
147       int32_t fpow = kc::atoi(status["fpow"].c_str());
148       int32_t fbpnum = fpow > 0 ? 1 << fpow : 0;
149       int32_t fbpused = kc::atoi(status["fbpnum_used"].c_str());
150       int64_t frgcnt = kc::atoi(status["frgcnt"].c_str());
151       oprintf("free block pool: %d (fpow=%d) (used=%d) (frg=%lld)\n",
152               fbpnum, fpow, fbpused, (long long)frgcnt);
153       int32_t opts = kc::atoi(status["opts"].c_str());
154       oprintf("options:");
155       if (opts & kc::TreeDB::TSMALL) oprintf(" small");
156       if (opts & kc::TreeDB::TLINEAR) oprintf(" linear");
157       if (opts & kc::TreeDB::TCOMPRESS) oprintf(" compress");
158       oprintf(" (opts=%d)\n", opts);
159       oprintf("comparator: %s\n", status["rcomp"].c_str());
160       if (status["opaque"].size() >= 16) {
161         const char* opaque = status["opaque"].c_str();
162         oprintf("opaque:");
163         if (std::count(opaque, opaque + 16, 0) != 16) {
164           for (int32_t i = 0; i < 16; i++) {
165             oprintf(" %02X", ((unsigned char*)opaque)[i]);
166           }
167         } else {
168           oprintf(" 0");
169         }
170         oprintf("\n");
171       }
172       int64_t bnum = kc::atoi(status["bnum"].c_str());
173       int64_t bnumused = kc::atoi(status["bnum_used"].c_str());
174       int64_t count = kc::atoi(status["count"].c_str());
175       int64_t pnum = kc::atoi(status["pnum"].c_str());
176       int64_t lcnt = kc::atoi(status["lcnt"].c_str());
177       int64_t icnt = kc::atoi(status["icnt"].c_str());
178       int32_t tlevel = kc::atoi(status["tree_level"].c_str());
179       int32_t psiz = kc::atoi(status["psiz"].c_str());
180       double load = 0;
181       if (pnum > 0 && bnumused > 0) {
182         load = (double)pnum / bnumused;
183         if (!(opts & kc::TreeDB::TLINEAR)) load = std::log(load + 1) / std::log(2.0);
184       }
185       oprintf("buckets: %lld (used=%lld) (load=%.2f)\n",
186               (long long)bnum, (long long)bnumused, load);
187       oprintf("pages: %lld (leaf=%lld) (inner=%lld) (level=%d) (psiz=%d)\n",
188               (long long)pnum, (long long)lcnt, (long long)icnt, tlevel, psiz);
189       int64_t pccap = kc::atoi(status["pccap"].c_str());
190       int64_t cusage = kc::atoi(status["cusage"].c_str());
191       int64_t culcnt = kc::atoi(status["cusage_lcnt"].c_str());
192       int64_t culsiz = kc::atoi(status["cusage_lsiz"].c_str());
193       int64_t cuicnt = kc::atoi(status["cusage_icnt"].c_str());
194       int64_t cuisiz = kc::atoi(status["cusage_isiz"].c_str());
195       oprintf("cache: %lld (cap=%lld) (ratio=%.2f) (leaf=%lld:%lld) (inner=%lld:%lld)\n",
196               (long long)cusage, (long long)pccap, (double)cusage / pccap,
197               (long long)culsiz, (long long)culcnt, (long long)cuisiz, (long long)cuicnt);
198       std::string cntstr = unitnumstr(count);
199       oprintf("count: %lld (%s)\n", count, cntstr.c_str());
200       int64_t size = kc::atoi(status["size"].c_str());
201       int64_t msiz = kc::atoi(status["msiz"].c_str());
202       int64_t realsize = kc::atoi(status["realsize"].c_str());
203       std::string sizestr = unitnumstrbyte(size);
204       oprintf("size: %lld (%s) (map=%lld)", size, sizestr.c_str(), (long long)msiz);
205       if (size != realsize) oprintf(" (gap=%lld)", (long long)(realsize - size));
206       oprintf("\n");
207     }
208   } else {
209     oprintf("count: %lld\n", (long long)db->count());
210     oprintf("size: %lld\n", (long long)db->size());
211   }
212   int64_t musage = memusage();
213   if (musage > 0) oprintf("memory: %lld\n", (long long)(musage - g_memusage));
214 }
215 
216 
217 // parse arguments of order command
runorder(int argc,char ** argv)218 static int32_t runorder(int argc, char** argv) {
219   bool argbrk = false;
220   const char* path = NULL;
221   const char* rstr = NULL;
222   int32_t thnum = 1;
223   bool rnd = false;
224   int32_t mode = 0;
225   bool tran = false;
226   int32_t oflags = 0;
227   int32_t apow = -1;
228   int32_t fpow = -1;
229   int32_t opts = 0;
230   int64_t bnum = -1;
231   int64_t psiz = -1;
232   int64_t msiz = -1;
233   int64_t dfunit = -1;
234   int64_t pccap = 0;
235   kc::Comparator* rcomp = NULL;
236   bool lv = false;
237   for (int32_t i = 2; i < argc; i++) {
238     if (!argbrk && argv[i][0] == '-') {
239       if (!std::strcmp(argv[i], "--")) {
240         argbrk = true;
241       } else if (!std::strcmp(argv[i], "-th")) {
242         if (++i >= argc) usage();
243         thnum = kc::atoix(argv[i]);
244       } else if (!std::strcmp(argv[i], "-rnd")) {
245         rnd = true;
246       } else if (!std::strcmp(argv[i], "-set")) {
247         mode = 's';
248       } else if (!std::strcmp(argv[i], "-get")) {
249         mode = 'g';
250       } else if (!std::strcmp(argv[i], "-getw")) {
251         mode = 'w';
252       } else if (!std::strcmp(argv[i], "-rem")) {
253         mode = 'r';
254       } else if (!std::strcmp(argv[i], "-etc")) {
255         mode = 'e';
256       } else if (!std::strcmp(argv[i], "-tran")) {
257         tran = true;
258       } else if (!std::strcmp(argv[i], "-oat")) {
259         oflags |= kc::TreeDB::OAUTOTRAN;
260       } else if (!std::strcmp(argv[i], "-oas")) {
261         oflags |= kc::TreeDB::OAUTOSYNC;
262       } else if (!std::strcmp(argv[i], "-onl")) {
263         oflags |= kc::TreeDB::ONOLOCK;
264       } else if (!std::strcmp(argv[i], "-otl")) {
265         oflags |= kc::TreeDB::OTRYLOCK;
266       } else if (!std::strcmp(argv[i], "-onr")) {
267         oflags |= kc::TreeDB::ONOREPAIR;
268       } else if (!std::strcmp(argv[i], "-apow")) {
269         if (++i >= argc) usage();
270         apow = kc::atoix(argv[i]);
271       } else if (!std::strcmp(argv[i], "-fpow")) {
272         if (++i >= argc) usage();
273         fpow = kc::atoix(argv[i]);
274       } else if (!std::strcmp(argv[i], "-ts")) {
275         opts |= kc::TreeDB::TSMALL;
276       } else if (!std::strcmp(argv[i], "-tl")) {
277         opts |= kc::TreeDB::TLINEAR;
278       } else if (!std::strcmp(argv[i], "-tc")) {
279         opts |= kc::TreeDB::TCOMPRESS;
280       } else if (!std::strcmp(argv[i], "-bnum")) {
281         if (++i >= argc) usage();
282         bnum = kc::atoix(argv[i]);
283       } else if (!std::strcmp(argv[i], "-psiz")) {
284         if (++i >= argc) usage();
285         psiz = kc::atoix(argv[i]);
286       } else if (!std::strcmp(argv[i], "-msiz")) {
287         if (++i >= argc) usage();
288         msiz = kc::atoix(argv[i]);
289       } else if (!std::strcmp(argv[i], "-dfunit")) {
290         if (++i >= argc) usage();
291         dfunit = kc::atoix(argv[i]);
292       } else if (!std::strcmp(argv[i], "-pccap")) {
293         if (++i >= argc) usage();
294         pccap = kc::atoix(argv[i]);
295       } else if (!std::strcmp(argv[i], "-rcd")) {
296         rcomp = kc::DECIMALCOMP;
297       } else if (!std::strcmp(argv[i], "-rcld")) {
298         rcomp = kc::LEXICALDESCCOMP;
299       } else if (!std::strcmp(argv[i], "-rcdd")) {
300         rcomp = kc::DECIMALDESCCOMP;
301       } else if (!std::strcmp(argv[i], "-lv")) {
302         lv = true;
303       } else {
304         usage();
305       }
306     } else if (!path) {
307       argbrk = true;
308       path = argv[i];
309     } else if (!rstr) {
310       rstr = argv[i];
311     } else {
312       usage();
313     }
314   }
315   if (!path || !rstr) usage();
316   int64_t rnum = kc::atoix(rstr);
317   if (rnum < 1 || thnum < 1) usage();
318   if (thnum > THREADMAX) thnum = THREADMAX;
319   int32_t rv = procorder(path, rnum, thnum, rnd, mode, tran, oflags,
320                          apow, fpow, opts, bnum, psiz, msiz, dfunit, pccap, rcomp, lv);
321   return rv;
322 }
323 
324 
325 // parse arguments of queue command
runqueue(int argc,char ** argv)326 static int32_t runqueue(int argc, char** argv) {
327   bool argbrk = false;
328   const char* path = NULL;
329   const char* rstr = NULL;
330   int32_t thnum = 1;
331   int32_t itnum = 1;
332   bool rnd = false;
333   int32_t oflags = 0;
334   int32_t apow = -1;
335   int32_t fpow = -1;
336   int32_t opts = 0;
337   int64_t bnum = -1;
338   int64_t psiz = -1;
339   int64_t msiz = -1;
340   int64_t dfunit = -1;
341   int64_t pccap = 0;
342   kc::Comparator* rcomp = NULL;
343   bool lv = false;
344   for (int32_t i = 2; i < argc; i++) {
345     if (!argbrk && argv[i][0] == '-') {
346       if (!std::strcmp(argv[i], "--")) {
347         argbrk = true;
348       } else if (!std::strcmp(argv[i], "-th")) {
349         if (++i >= argc) usage();
350         thnum = kc::atoix(argv[i]);
351       } else if (!std::strcmp(argv[i], "-it")) {
352         if (++i >= argc) usage();
353         itnum = kc::atoix(argv[i]);
354       } else if (!std::strcmp(argv[i], "-rnd")) {
355         rnd = true;
356       } else if (!std::strcmp(argv[i], "-oat")) {
357         oflags |= kc::TreeDB::OAUTOTRAN;
358       } else if (!std::strcmp(argv[i], "-oas")) {
359         oflags |= kc::TreeDB::OAUTOSYNC;
360       } else if (!std::strcmp(argv[i], "-onl")) {
361         oflags |= kc::TreeDB::ONOLOCK;
362       } else if (!std::strcmp(argv[i], "-otl")) {
363         oflags |= kc::TreeDB::OTRYLOCK;
364       } else if (!std::strcmp(argv[i], "-onr")) {
365         oflags |= kc::TreeDB::ONOREPAIR;
366       } else if (!std::strcmp(argv[i], "-apow")) {
367         if (++i >= argc) usage();
368         apow = kc::atoix(argv[i]);
369       } else if (!std::strcmp(argv[i], "-fpow")) {
370         if (++i >= argc) usage();
371         fpow = kc::atoix(argv[i]);
372       } else if (!std::strcmp(argv[i], "-ts")) {
373         opts |= kc::TreeDB::TSMALL;
374       } else if (!std::strcmp(argv[i], "-tl")) {
375         opts |= kc::TreeDB::TLINEAR;
376       } else if (!std::strcmp(argv[i], "-tc")) {
377         opts |= kc::TreeDB::TCOMPRESS;
378       } else if (!std::strcmp(argv[i], "-bnum")) {
379         if (++i >= argc) usage();
380         bnum = kc::atoix(argv[i]);
381       } else if (!std::strcmp(argv[i], "-psiz")) {
382         if (++i >= argc) usage();
383         psiz = kc::atoix(argv[i]);
384       } else if (!std::strcmp(argv[i], "-msiz")) {
385         if (++i >= argc) usage();
386         msiz = kc::atoix(argv[i]);
387       } else if (!std::strcmp(argv[i], "-dfunit")) {
388         if (++i >= argc) usage();
389         dfunit = kc::atoix(argv[i]);
390       } else if (!std::strcmp(argv[i], "-pccap")) {
391         if (++i >= argc) usage();
392         pccap = kc::atoix(argv[i]);
393       } else if (!std::strcmp(argv[i], "-rcd")) {
394         rcomp = kc::DECIMALCOMP;
395       } else if (!std::strcmp(argv[i], "-rcld")) {
396         rcomp = kc::LEXICALDESCCOMP;
397       } else if (!std::strcmp(argv[i], "-rcdd")) {
398         rcomp = kc::DECIMALDESCCOMP;
399       } else if (!std::strcmp(argv[i], "-lv")) {
400         lv = true;
401       } else {
402         usage();
403       }
404     } else if (!path) {
405       argbrk = true;
406       path = argv[i];
407     } else if (!rstr) {
408       rstr = argv[i];
409     } else {
410       usage();
411     }
412   }
413   if (!path || !rstr) usage();
414   int64_t rnum = kc::atoix(rstr);
415   if (rnum < 1 || thnum < 1 || itnum < 1) usage();
416   if (thnum > THREADMAX) thnum = THREADMAX;
417   int32_t rv = procqueue(path, rnum, thnum, itnum, rnd, oflags,
418                          apow, fpow, opts, bnum, psiz, msiz, dfunit, pccap, rcomp, lv);
419   return rv;
420 }
421 
422 
423 // parse arguments of wicked command
runwicked(int argc,char ** argv)424 static int32_t runwicked(int argc, char** argv) {
425   bool argbrk = false;
426   const char* path = NULL;
427   const char* rstr = NULL;
428   int32_t thnum = 1;
429   int32_t itnum = 1;
430   int32_t oflags = 0;
431   int32_t apow = -1;
432   int32_t fpow = -1;
433   int32_t opts = 0;
434   int64_t bnum = -1;
435   int64_t psiz = -1;
436   int64_t msiz = -1;
437   int64_t dfunit = -1;
438   int64_t pccap = 0;
439   kc::Comparator* rcomp = NULL;
440   bool lv = false;
441   for (int32_t i = 2; i < argc; i++) {
442     if (!argbrk && argv[i][0] == '-') {
443       if (!std::strcmp(argv[i], "--")) {
444         argbrk = true;
445       } else if (!std::strcmp(argv[i], "-th")) {
446         if (++i >= argc) usage();
447         thnum = kc::atoix(argv[i]);
448       } else if (!std::strcmp(argv[i], "-it")) {
449         if (++i >= argc) usage();
450         itnum = kc::atoix(argv[i]);
451       } else if (!std::strcmp(argv[i], "-oat")) {
452         oflags |= kc::TreeDB::OAUTOTRAN;
453       } else if (!std::strcmp(argv[i], "-oas")) {
454         oflags |= kc::TreeDB::OAUTOSYNC;
455       } else if (!std::strcmp(argv[i], "-onl")) {
456         oflags |= kc::TreeDB::ONOLOCK;
457       } else if (!std::strcmp(argv[i], "-otl")) {
458         oflags |= kc::TreeDB::OTRYLOCK;
459       } else if (!std::strcmp(argv[i], "-onr")) {
460         oflags |= kc::TreeDB::ONOREPAIR;
461       } else if (!std::strcmp(argv[i], "-apow")) {
462         if (++i >= argc) usage();
463         apow = kc::atoix(argv[i]);
464       } else if (!std::strcmp(argv[i], "-fpow")) {
465         if (++i >= argc) usage();
466         fpow = kc::atoix(argv[i]);
467       } else if (!std::strcmp(argv[i], "-ts")) {
468         opts |= kc::TreeDB::TSMALL;
469       } else if (!std::strcmp(argv[i], "-tl")) {
470         opts |= kc::TreeDB::TLINEAR;
471       } else if (!std::strcmp(argv[i], "-tc")) {
472         opts |= kc::TreeDB::TCOMPRESS;
473       } else if (!std::strcmp(argv[i], "-bnum")) {
474         if (++i >= argc) usage();
475         bnum = kc::atoix(argv[i]);
476       } else if (!std::strcmp(argv[i], "-psiz")) {
477         if (++i >= argc) usage();
478         psiz = kc::atoix(argv[i]);
479       } else if (!std::strcmp(argv[i], "-msiz")) {
480         if (++i >= argc) usage();
481         msiz = kc::atoix(argv[i]);
482       } else if (!std::strcmp(argv[i], "-dfunit")) {
483         if (++i >= argc) usage();
484         dfunit = kc::atoix(argv[i]);
485       } else if (!std::strcmp(argv[i], "-pccap")) {
486         if (++i >= argc) usage();
487         pccap = kc::atoix(argv[i]);
488       } else if (!std::strcmp(argv[i], "-rcd")) {
489         rcomp = kc::DECIMALCOMP;
490       } else if (!std::strcmp(argv[i], "-rcld")) {
491         rcomp = kc::LEXICALDESCCOMP;
492       } else if (!std::strcmp(argv[i], "-rcdd")) {
493         rcomp = kc::DECIMALDESCCOMP;
494       } else if (!std::strcmp(argv[i], "-lv")) {
495         lv = true;
496       } else {
497         usage();
498       }
499     } else if (!path) {
500       argbrk = true;
501       path = argv[i];
502     } else if (!rstr) {
503       rstr = argv[i];
504     } else {
505       usage();
506     }
507   }
508   if (!path || !rstr) usage();
509   int64_t rnum = kc::atoix(rstr);
510   if (rnum < 1 || thnum < 1 || itnum < 1) usage();
511   if (thnum > THREADMAX) thnum = THREADMAX;
512   int32_t rv = procwicked(path, rnum, thnum, itnum, oflags,
513                           apow, fpow, opts, bnum, psiz, msiz, dfunit, pccap, rcomp, lv);
514   return rv;
515 }
516 
517 
518 // parse arguments of tran command
runtran(int argc,char ** argv)519 static int32_t runtran(int argc, char** argv) {
520   bool argbrk = false;
521   const char* path = NULL;
522   const char* rstr = NULL;
523   int32_t thnum = 1;
524   int32_t itnum = 1;
525   bool hard = false;
526   int32_t oflags = 0;
527   int32_t apow = -1;
528   int32_t fpow = -1;
529   int32_t opts = 0;
530   int64_t bnum = -1;
531   int64_t psiz = -1;
532   int64_t msiz = -1;
533   int64_t dfunit = -1;
534   int64_t pccap = 0;
535   kc::Comparator* rcomp = NULL;
536   bool lv = false;
537   for (int32_t i = 2; i < argc; i++) {
538     if (!argbrk && argv[i][0] == '-') {
539       if (!std::strcmp(argv[i], "--")) {
540         argbrk = true;
541       } else if (!std::strcmp(argv[i], "-th")) {
542         if (++i >= argc) usage();
543         thnum = kc::atoix(argv[i]);
544       } else if (!std::strcmp(argv[i], "-it")) {
545         if (++i >= argc) usage();
546         itnum = kc::atoix(argv[i]);
547       } else if (!std::strcmp(argv[i], "-hard")) {
548         hard = true;
549       } else if (!std::strcmp(argv[i], "-oat")) {
550         oflags |= kc::TreeDB::OAUTOTRAN;
551       } else if (!std::strcmp(argv[i], "-oas")) {
552         oflags |= kc::TreeDB::OAUTOSYNC;
553       } else if (!std::strcmp(argv[i], "-onl")) {
554         oflags |= kc::TreeDB::ONOLOCK;
555       } else if (!std::strcmp(argv[i], "-otl")) {
556         oflags |= kc::TreeDB::OTRYLOCK;
557       } else if (!std::strcmp(argv[i], "-onr")) {
558         oflags |= kc::TreeDB::ONOREPAIR;
559       } else if (!std::strcmp(argv[i], "-apow")) {
560         if (++i >= argc) usage();
561         apow = kc::atoix(argv[i]);
562       } else if (!std::strcmp(argv[i], "-fpow")) {
563         if (++i >= argc) usage();
564         fpow = kc::atoix(argv[i]);
565       } else if (!std::strcmp(argv[i], "-ts")) {
566         opts |= kc::TreeDB::TSMALL;
567       } else if (!std::strcmp(argv[i], "-tl")) {
568         opts |= kc::TreeDB::TLINEAR;
569       } else if (!std::strcmp(argv[i], "-tc")) {
570         opts |= kc::TreeDB::TCOMPRESS;
571       } else if (!std::strcmp(argv[i], "-bnum")) {
572         if (++i >= argc) usage();
573         bnum = kc::atoix(argv[i]);
574       } else if (!std::strcmp(argv[i], "-psiz")) {
575         if (++i >= argc) usage();
576         psiz = kc::atoix(argv[i]);
577       } else if (!std::strcmp(argv[i], "-msiz")) {
578         if (++i >= argc) usage();
579         msiz = kc::atoix(argv[i]);
580       } else if (!std::strcmp(argv[i], "-dfunit")) {
581         if (++i >= argc) usage();
582         dfunit = kc::atoix(argv[i]);
583       } else if (!std::strcmp(argv[i], "-pccap")) {
584         if (++i >= argc) usage();
585         pccap = kc::atoix(argv[i]);
586       } else if (!std::strcmp(argv[i], "-rcd")) {
587         rcomp = kc::DECIMALCOMP;
588       } else if (!std::strcmp(argv[i], "-rcld")) {
589         rcomp = kc::LEXICALDESCCOMP;
590       } else if (!std::strcmp(argv[i], "-rcdd")) {
591         rcomp = kc::DECIMALDESCCOMP;
592       } else if (!std::strcmp(argv[i], "-lv")) {
593         lv = true;
594       } else {
595         usage();
596       }
597     } else if (!path) {
598       argbrk = true;
599       path = argv[i];
600     } else if (!rstr) {
601       rstr = argv[i];
602     } else {
603       usage();
604     }
605   }
606   if (!path || !rstr) usage();
607   int64_t rnum = kc::atoix(rstr);
608   if (rnum < 1 || thnum < 1 || itnum < 1) usage();
609   if (thnum > THREADMAX) thnum = THREADMAX;
610   int32_t rv = proctran(path, rnum, thnum, itnum, hard, oflags,
611                         apow, fpow, opts, bnum, psiz, msiz, dfunit, pccap, rcomp, lv);
612   return rv;
613 }
614 
615 
616 // perform order command
procorder(const char * path,int64_t rnum,int32_t thnum,bool rnd,int32_t mode,bool tran,int32_t oflags,int32_t apow,int32_t fpow,int32_t opts,int64_t bnum,int32_t psiz,int64_t msiz,int64_t dfunit,int64_t pccap,kc::Comparator * rcomp,bool lv)617 static int32_t procorder(const char* path, int64_t rnum, int32_t thnum, bool rnd, int32_t mode,
618                          bool tran, int32_t oflags, int32_t apow, int32_t fpow,
619                          int32_t opts, int64_t bnum, int32_t psiz, int64_t msiz,
620                          int64_t dfunit, int64_t pccap, kc::Comparator* rcomp, bool lv) {
621   oprintf("<In-order Test>\n  seed=%u  path=%s  rnum=%lld  thnum=%d  rnd=%d  mode=%d  tran=%d"
622           "  oflags=%d  apow=%d  fpow=%d  opts=%d  bnum=%lld  psiz=%d  msiz=%lld"
623           "  dfunit=%lld  pccap=%lld  rcomp=%p  lv=%d\n\n",
624           g_randseed, path, (long long)rnum, thnum, rnd, mode, tran, oflags, apow, fpow, opts,
625           (long long)bnum, psiz, (long long)msiz, (long long)dfunit, (long long)pccap,
626           rcomp, lv);
627   bool err = false;
628   kc::TreeDB db;
629   oprintf("opening the database:\n");
630   double stime = kc::time();
631   db.tune_logger(stdlogger(g_progname, &std::cout),
632                  lv ? kc::UINT32MAX : kc::BasicDB::Logger::WARN | kc::BasicDB::Logger::ERROR);
633   if (apow >= 0) db.tune_alignment(apow);
634   if (fpow >= 0) db.tune_fbp(fpow);
635   if (opts > 0) db.tune_options(opts);
636   if (bnum > 0) db.tune_buckets(bnum);
637   if (psiz > 0) db.tune_page(psiz);
638   if (msiz >= 0) db.tune_map(msiz);
639   if (dfunit > 0) db.tune_defrag(dfunit);
640   if (pccap > 0) db.tune_page_cache(pccap);
641   if (rcomp) db.tune_comparator(rcomp);
642   uint32_t omode = kc::TreeDB::OWRITER | kc::TreeDB::OCREATE | kc::TreeDB::OTRUNCATE;
643   if (mode == 'r') {
644     omode = kc::TreeDB::OWRITER | kc::TreeDB::OCREATE;
645   } else if (mode == 'g' || mode == 'w') {
646     omode = kc::TreeDB::OREADER;
647   }
648   if (!db.open(path, omode | oflags)) {
649     dberrprint(&db, __LINE__, "DB::open");
650     err = true;
651   }
652   double etime = kc::time();
653   dbmetaprint(&db, false);
654   oprintf("time: %.3f\n", etime - stime);
655   if (mode == 0 || mode == 's' || mode == 'e') {
656     oprintf("setting records:\n");
657     stime = kc::time();
658     class ThreadSet : public kc::Thread {
659      public:
660       void setparams(int32_t id, kc::BasicDB* db, int64_t rnum, int32_t thnum,
661                      bool rnd, bool tran) {
662         id_ = id;
663         db_ = db;
664         rnum_ = rnum;
665         thnum_ = thnum;
666         err_ = false;
667         rnd_ = rnd;
668         tran_ = tran;
669       }
670       bool error() {
671         return err_;
672       }
673       void run() {
674         int64_t base = id_ * rnum_;
675         int64_t range = rnum_ * thnum_;
676         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
677           if (tran_ && !db_->begin_transaction(false)) {
678             dberrprint(db_, __LINE__, "DB::begin_transaction");
679             err_ = true;
680           }
681           char kbuf[RECBUFSIZ];
682           size_t ksiz = std::sprintf(kbuf, "%08lld",
683                                      (long long)(rnd_ ? myrand(range) + 1 : base + i));
684           if (!db_->set(kbuf, ksiz, kbuf, ksiz)) {
685             dberrprint(db_, __LINE__, "DB::set");
686             err_ = true;
687           }
688           if (rnd_ && i % 8 == 0) {
689             switch (myrand(8)) {
690               case 0: {
691                 if (!db_->set(kbuf, ksiz, kbuf, ksiz)) {
692                   dberrprint(db_, __LINE__, "DB::set");
693                   err_ = true;
694                 }
695                 break;
696               }
697               case 1: {
698                 if (!db_->append(kbuf, ksiz, kbuf, ksiz)) {
699                   dberrprint(db_, __LINE__, "DB::append");
700                   err_ = true;
701                 }
702                 break;
703               }
704               case 2: {
705                 if (!db_->remove(kbuf, ksiz) && db_->error() != kc::BasicDB::Error::NOREC) {
706                   dberrprint(db_, __LINE__, "DB::remove");
707                   err_ = true;
708                 }
709                 break;
710               }
711               case 3: {
712                 kc::DB::Cursor* cur = db_->cursor();
713                 if (cur->jump(kbuf, ksiz)) {
714                   switch (myrand(8)) {
715                     default: {
716                       size_t rsiz;
717                       char* rbuf = cur->get_key(&rsiz, myrand(10) == 0);
718                       if (rbuf) {
719                         delete[] rbuf;
720                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
721                         dberrprint(db_, __LINE__, "Cursor::get_key");
722                         err_ = true;
723                       }
724                       break;
725                     }
726                     case 1: {
727                       size_t rsiz;
728                       char* rbuf = cur->get_value(&rsiz, myrand(10) == 0);
729                       if (rbuf) {
730                         delete[] rbuf;
731                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
732                         dberrprint(db_, __LINE__, "Cursor::get_value");
733                         err_ = true;
734                       }
735                       break;
736                     }
737                     case 2: {
738                       size_t rksiz;
739                       const char* rvbuf;
740                       size_t rvsiz;
741                       char* rkbuf = cur->get(&rksiz, &rvbuf, &rvsiz, myrand(10) == 0);
742                       if (rkbuf) {
743                         delete[] rkbuf;
744                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
745                         dberrprint(db_, __LINE__, "Cursor::get");
746                         err_ = true;
747                       }
748                       break;
749                     }
750                     case 3: {
751                       std::string key, value;
752                       if (!cur->get(&key, &value, myrand(10) == 0) &&
753                           db_->error() != kc::BasicDB::Error::NOREC) {
754                         dberrprint(db_, __LINE__, "Cursor::get");
755                         err_ = true;
756                       }
757                       break;
758                     }
759                     case 4: {
760                       if (myrand(8) == 0 && !cur->remove() &&
761                           db_->error() != kc::BasicDB::Error::NOREC) {
762                         dberrprint(db_, __LINE__, "Cursor::remove");
763                         err_ = true;
764                       }
765                       break;
766                     }
767                   }
768                 } else if (db_->error() != kc::BasicDB::Error::NOREC) {
769                   dberrprint(db_, __LINE__, "Cursor::jump");
770                   err_ = true;
771                 }
772                 delete cur;
773                 break;
774               }
775               default: {
776                 size_t vsiz;
777                 char* vbuf = db_->get(kbuf, ksiz, &vsiz);
778                 if (vbuf) {
779                   delete[] vbuf;
780                 } else if (db_->error() != kc::BasicDB::Error::NOREC) {
781                   dberrprint(db_, __LINE__, "DB::get");
782                   err_ = true;
783                 }
784                 break;
785               }
786             }
787           }
788           if (tran_ && !db_->end_transaction(true)) {
789             dberrprint(db_, __LINE__, "DB::end_transaction");
790             err_ = true;
791           }
792           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
793             oputchar('.');
794             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
795           }
796         }
797       }
798      private:
799       int32_t id_;
800       kc::BasicDB* db_;
801       int64_t rnum_;
802       int32_t thnum_;
803       bool err_;
804       bool rnd_;
805       bool tran_;
806     };
807     ThreadSet threadsets[THREADMAX];
808     if (thnum < 2) {
809       threadsets[0].setparams(0, &db, rnum, thnum, rnd, tran);
810       threadsets[0].run();
811       if (threadsets[0].error()) err = true;
812     } else {
813       for (int32_t i = 0; i < thnum; i++) {
814         threadsets[i].setparams(i, &db, rnum, thnum, rnd, tran);
815         threadsets[i].start();
816       }
817       for (int32_t i = 0; i < thnum; i++) {
818         threadsets[i].join();
819         if (threadsets[i].error()) err = true;
820       }
821     }
822     etime = kc::time();
823     dbmetaprint(&db, mode == 's');
824     oprintf("time: %.3f\n", etime - stime);
825   }
826   if (mode == 'e') {
827     oprintf("adding records:\n");
828     stime = kc::time();
829     class ThreadAdd : public kc::Thread {
830      public:
831       void setparams(int32_t id, kc::BasicDB* db, int64_t rnum, int32_t thnum,
832                      bool rnd, bool tran) {
833         id_ = id;
834         db_ = db;
835         rnum_ = rnum;
836         thnum_ = thnum;
837         err_ = false;
838         rnd_ = rnd;
839         tran_ = tran;
840       }
841       bool error() {
842         return err_;
843       }
844       void run() {
845         int64_t base = id_ * rnum_;
846         int64_t range = rnum_ * thnum_;
847         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
848           if (tran_ && !db_->begin_transaction(false)) {
849             dberrprint(db_, __LINE__, "DB::begin_transaction");
850             err_ = true;
851           }
852           char kbuf[RECBUFSIZ];
853           size_t ksiz = std::sprintf(kbuf, "%08lld",
854                                      (long long)(rnd_ ? myrand(range) + 1 : base + i));
855           if (!db_->add(kbuf, ksiz, kbuf, ksiz) &&
856               db_->error() != kc::BasicDB::Error::DUPREC) {
857             dberrprint(db_, __LINE__, "DB::add");
858             err_ = true;
859           }
860           if (tran_ && !db_->end_transaction(true)) {
861             dberrprint(db_, __LINE__, "DB::end_transaction");
862             err_ = true;
863           }
864           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
865             oputchar('.');
866             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
867           }
868         }
869       }
870      private:
871       int32_t id_;
872       kc::BasicDB* db_;
873       int64_t rnum_;
874       int32_t thnum_;
875       bool err_;
876       bool rnd_;
877       bool tran_;
878     };
879     ThreadAdd threadadds[THREADMAX];
880     if (thnum < 2) {
881       threadadds[0].setparams(0, &db, rnum, thnum, rnd, tran);
882       threadadds[0].run();
883       if (threadadds[0].error()) err = true;
884     } else {
885       for (int32_t i = 0; i < thnum; i++) {
886         threadadds[i].setparams(i, &db, rnum, thnum, rnd, tran);
887         threadadds[i].start();
888       }
889       for (int32_t i = 0; i < thnum; i++) {
890         threadadds[i].join();
891         if (threadadds[i].error()) err = true;
892       }
893     }
894     etime = kc::time();
895     dbmetaprint(&db, false);
896     oprintf("time: %.3f\n", etime - stime);
897   }
898   if (mode == 'e') {
899     oprintf("appending records:\n");
900     stime = kc::time();
901     class ThreadAppend : public kc::Thread {
902      public:
903       void setparams(int32_t id, kc::BasicDB* db, int64_t rnum, int32_t thnum,
904                      bool rnd, bool tran) {
905         id_ = id;
906         db_ = db;
907         rnum_ = rnum;
908         thnum_ = thnum;
909         err_ = false;
910         rnd_ = rnd;
911         tran_ = tran;
912       }
913       bool error() {
914         return err_;
915       }
916       void run() {
917         int64_t base = id_ * rnum_;
918         int64_t range = rnum_ * thnum_;
919         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
920           if (tran_ && !db_->begin_transaction(false)) {
921             dberrprint(db_, __LINE__, "DB::begin_transaction");
922             err_ = true;
923           }
924           char kbuf[RECBUFSIZ];
925           size_t ksiz = std::sprintf(kbuf, "%08lld",
926                                      (long long)(rnd_ ? myrand(range) + 1 : base + i));
927           if (!db_->append(kbuf, ksiz, kbuf, ksiz)) {
928             dberrprint(db_, __LINE__, "DB::append");
929             err_ = true;
930           }
931           if (tran_ && !db_->end_transaction(true)) {
932             dberrprint(db_, __LINE__, "DB::end_transaction");
933             err_ = true;
934           }
935           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
936             oputchar('.');
937             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
938           }
939         }
940       }
941      private:
942       int32_t id_;
943       kc::BasicDB* db_;
944       int64_t rnum_;
945       int32_t thnum_;
946       bool err_;
947       bool rnd_;
948       bool tran_;
949     };
950     ThreadAppend threadappends[THREADMAX];
951     if (thnum < 2) {
952       threadappends[0].setparams(0, &db, rnum, thnum, rnd, tran);
953       threadappends[0].run();
954       if (threadappends[0].error()) err = true;
955     } else {
956       for (int32_t i = 0; i < thnum; i++) {
957         threadappends[i].setparams(i, &db, rnum, thnum, rnd, tran);
958         threadappends[i].start();
959       }
960       for (int32_t i = 0; i < thnum; i++) {
961         threadappends[i].join();
962         if (threadappends[i].error()) err = true;
963       }
964     }
965     etime = kc::time();
966     dbmetaprint(&db, false);
967     oprintf("time: %.3f\n", etime - stime);
968     char* opaque = db.opaque();
969     if (opaque) {
970       std::memcpy(opaque, "1234567890123456", 16);
971       if (!db.synchronize_opaque()) {
972         dberrprint(&db, __LINE__, "DB::synchronize_opaque");
973         err = true;
974       }
975     } else {
976       dberrprint(&db, __LINE__, "DB::opaque");
977       err = true;
978     }
979   }
980   if (mode == 0 || mode == 'g' || mode == 'e') {
981     oprintf("getting records:\n");
982     stime = kc::time();
983     class ThreadGet : public kc::Thread {
984      public:
985       void setparams(int32_t id, kc::BasicDB* db, int64_t rnum, int32_t thnum,
986                      bool rnd, bool tran) {
987         id_ = id;
988         db_ = db;
989         rnum_ = rnum;
990         thnum_ = thnum;
991         err_ = false;
992         rnd_ = rnd;
993         tran_ = tran;
994       }
995       bool error() {
996         return err_;
997       }
998       void run() {
999         int64_t base = id_ * rnum_;
1000         int64_t range = rnum_ * thnum_;
1001         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
1002           if (tran_ && !db_->begin_transaction(false)) {
1003             dberrprint(db_, __LINE__, "DB::begin_transaction");
1004             err_ = true;
1005           }
1006           char kbuf[RECBUFSIZ];
1007           size_t ksiz = std::sprintf(kbuf, "%08lld",
1008                                      (long long)(rnd_ ? myrand(range) + 1 : base + i));
1009           size_t vsiz;
1010           char* vbuf = db_->get(kbuf, ksiz, &vsiz);
1011           if (vbuf) {
1012             if (vsiz < ksiz || std::memcmp(vbuf, kbuf, ksiz)) {
1013               dberrprint(db_, __LINE__, "DB::get");
1014               err_ = true;
1015             }
1016             delete[] vbuf;
1017           } else if (!rnd_ || db_->error() != kc::BasicDB::Error::NOREC) {
1018             dberrprint(db_, __LINE__, "DB::get");
1019             err_ = true;
1020           }
1021           if (rnd_ && i % 8 == 0) {
1022             switch (myrand(8)) {
1023               case 0: {
1024                 if (!db_->set(kbuf, ksiz, kbuf, ksiz) &&
1025                     db_->error() != kc::BasicDB::Error::NOPERM) {
1026                   dberrprint(db_, __LINE__, "DB::set");
1027                   err_ = true;
1028                 }
1029                 break;
1030               }
1031               case 1: {
1032                 if (!db_->append(kbuf, ksiz, kbuf, ksiz) &&
1033                     db_->error() != kc::BasicDB::Error::NOPERM) {
1034                   dberrprint(db_, __LINE__, "DB::append");
1035                   err_ = true;
1036                 }
1037                 break;
1038               }
1039               case 2: {
1040                 if (!db_->remove(kbuf, ksiz) &&
1041                     db_->error() != kc::BasicDB::Error::NOPERM &&
1042                     db_->error() != kc::BasicDB::Error::NOREC) {
1043                   dberrprint(db_, __LINE__, "DB::remove");
1044                   err_ = true;
1045                 }
1046                 break;
1047               }
1048               case 3: {
1049                 kc::DB::Cursor* cur = db_->cursor();
1050                 if (cur->jump(kbuf, ksiz)) {
1051                   switch (myrand(8)) {
1052                     default: {
1053                       size_t rsiz;
1054                       char* rbuf = cur->get_key(&rsiz, myrand(10) == 0);
1055                       if (rbuf) {
1056                         delete[] rbuf;
1057                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1058                         dberrprint(db_, __LINE__, "Cursor::get_key");
1059                         err_ = true;
1060                       }
1061                       break;
1062                     }
1063                     case 1: {
1064                       size_t rsiz;
1065                       char* rbuf = cur->get_value(&rsiz, myrand(10) == 0);
1066                       if (rbuf) {
1067                         delete[] rbuf;
1068                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1069                         dberrprint(db_, __LINE__, "Cursor::get_value");
1070                         err_ = true;
1071                       }
1072                       break;
1073                     }
1074                     case 2: {
1075                       size_t rksiz;
1076                       const char* rvbuf;
1077                       size_t rvsiz;
1078                       char* rkbuf = cur->get(&rksiz, &rvbuf, &rvsiz, myrand(10) == 0);
1079                       if (rkbuf) {
1080                         delete[] rkbuf;
1081                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1082                         dberrprint(db_, __LINE__, "Cursor::get");
1083                         err_ = true;
1084                       }
1085                       break;
1086                     }
1087                     case 3: {
1088                       std::string key, value;
1089                       if (!cur->get(&key, &value, myrand(10) == 0) &&
1090                           db_->error() != kc::BasicDB::Error::NOREC) {
1091                         dberrprint(db_, __LINE__, "Cursor::get");
1092                         err_ = true;
1093                       }
1094                       break;
1095                     }
1096                     case 4: {
1097                       if (myrand(8) == 0 && !cur->remove() &&
1098                           db_->error() != kc::BasicDB::Error::NOPERM &&
1099                           db_->error() != kc::BasicDB::Error::NOREC) {
1100                         dberrprint(db_, __LINE__, "Cursor::remove");
1101                         err_ = true;
1102                       }
1103                       break;
1104                     }
1105                   }
1106                 } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1107                   dberrprint(db_, __LINE__, "Cursor::jump");
1108                   err_ = true;
1109                 }
1110                 delete cur;
1111                 break;
1112               }
1113               default: {
1114                 size_t vsiz;
1115                 char* vbuf = db_->get(kbuf, ksiz, &vsiz);
1116                 if (vbuf) {
1117                   delete[] vbuf;
1118                 } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1119                   dberrprint(db_, __LINE__, "DB::get");
1120                   err_ = true;
1121                 }
1122                 break;
1123               }
1124             }
1125           }
1126           if (tran_ && !db_->end_transaction(true)) {
1127             dberrprint(db_, __LINE__, "DB::end_transaction");
1128             err_ = true;
1129           }
1130           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
1131             oputchar('.');
1132             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
1133           }
1134         }
1135       }
1136      private:
1137       int32_t id_;
1138       kc::BasicDB* db_;
1139       int64_t rnum_;
1140       int32_t thnum_;
1141       bool err_;
1142       bool rnd_;
1143       bool tran_;
1144     };
1145     ThreadGet threadgets[THREADMAX];
1146     if (thnum < 2) {
1147       threadgets[0].setparams(0, &db, rnum, thnum, rnd, tran);
1148       threadgets[0].run();
1149       if (threadgets[0].error()) err = true;
1150     } else {
1151       for (int32_t i = 0; i < thnum; i++) {
1152         threadgets[i].setparams(i, &db, rnum, thnum, rnd, tran);
1153         threadgets[i].start();
1154       }
1155       for (int32_t i = 0; i < thnum; i++) {
1156         threadgets[i].join();
1157         if (threadgets[i].error()) err = true;
1158       }
1159     }
1160     etime = kc::time();
1161     dbmetaprint(&db, mode == 'g');
1162     oprintf("time: %.3f\n", etime - stime);
1163   }
1164   if (mode == 'w' || mode == 'e') {
1165     oprintf("getting records with a buffer:\n");
1166     stime = kc::time();
1167     class ThreadGetBuffer : public kc::Thread {
1168      public:
1169       void setparams(int32_t id, kc::BasicDB* db, int64_t rnum, int32_t thnum,
1170                      bool rnd, bool tran) {
1171         id_ = id;
1172         db_ = db;
1173         rnum_ = rnum;
1174         thnum_ = thnum;
1175         err_ = false;
1176         rnd_ = rnd;
1177         tran_ = tran;
1178       }
1179       bool error() {
1180         return err_;
1181       }
1182       void run() {
1183         int64_t base = id_ * rnum_;
1184         int64_t range = rnum_ * thnum_;
1185         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
1186           if (tran_ && !db_->begin_transaction(false)) {
1187             dberrprint(db_, __LINE__, "DB::begin_transaction");
1188             err_ = true;
1189           }
1190           char kbuf[RECBUFSIZ];
1191           size_t ksiz = std::sprintf(kbuf, "%08lld",
1192                                      (long long)(rnd_ ? myrand(range) + 1 : base + i));
1193           char vbuf[RECBUFSIZ];
1194           int32_t vsiz = db_->get(kbuf, ksiz, vbuf, sizeof(vbuf));
1195           if (vsiz >= 0) {
1196             if (vsiz < (int32_t)ksiz || std::memcmp(vbuf, kbuf, ksiz)) {
1197               dberrprint(db_, __LINE__, "DB::get");
1198               err_ = true;
1199             }
1200           } else if (!rnd_ || db_->error() != kc::BasicDB::Error::NOREC) {
1201             dberrprint(db_, __LINE__, "DB::get");
1202             err_ = true;
1203           }
1204           if (tran_ && !db_->end_transaction(true)) {
1205             dberrprint(db_, __LINE__, "DB::end_transaction");
1206             err_ = true;
1207           }
1208           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
1209             oputchar('.');
1210             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
1211           }
1212         }
1213       }
1214      private:
1215       int32_t id_;
1216       kc::BasicDB* db_;
1217       int64_t rnum_;
1218       int32_t thnum_;
1219       bool err_;
1220       bool rnd_;
1221       bool tran_;
1222     };
1223     ThreadGetBuffer threadgetbuffers[THREADMAX];
1224     if (thnum < 2) {
1225       threadgetbuffers[0].setparams(0, &db, rnum, thnum, rnd, tran);
1226       threadgetbuffers[0].run();
1227       if (threadgetbuffers[0].error()) err = true;
1228     } else {
1229       for (int32_t i = 0; i < thnum; i++) {
1230         threadgetbuffers[i].setparams(i, &db, rnum, thnum, rnd, tran);
1231         threadgetbuffers[i].start();
1232       }
1233       for (int32_t i = 0; i < thnum; i++) {
1234         threadgetbuffers[i].join();
1235         if (threadgetbuffers[i].error()) err = true;
1236       }
1237     }
1238     etime = kc::time();
1239     dbmetaprint(&db, mode == 'w');
1240     oprintf("time: %.3f\n", etime - stime);
1241   }
1242   if (mode == 'e') {
1243     oprintf("traversing the database by the inner iterator:\n");
1244     stime = kc::time();
1245     int64_t cnt = db.count();
1246     class VisitorIterator : public kc::DB::Visitor {
1247      public:
1248       explicit VisitorIterator(int64_t rnum, bool rnd) :
1249           rnum_(rnum), rnd_(rnd), cnt_(0), rbuf_() {
1250         std::memset(rbuf_, '+', sizeof(rbuf_));
1251       }
1252       int64_t cnt() {
1253         return cnt_;
1254       }
1255      private:
1256       const char* visit_full(const char* kbuf, size_t ksiz,
1257                              const char* vbuf, size_t vsiz, size_t* sp) {
1258         cnt_++;
1259         const char* rv = NOP;
1260         switch (rnd_ ? myrand(7) : cnt_ % 7) {
1261           case 0: {
1262             rv = rbuf_;
1263             *sp = rnd_ ? myrand(sizeof(rbuf_)) : sizeof(rbuf_) / (cnt_ % 5 + 1);
1264             break;
1265           }
1266           case 1: {
1267             rv = REMOVE;
1268             break;
1269           }
1270         }
1271         if (rnum_ > 250 && cnt_ % (rnum_ / 250) == 0) {
1272           oputchar('.');
1273           if (cnt_ == rnum_ || cnt_ % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)cnt_);
1274         }
1275         return rv;
1276       }
1277       int64_t rnum_;
1278       bool rnd_;
1279       int64_t cnt_;
1280       char rbuf_[RECBUFSIZ];
1281     } visitoriterator(rnum, rnd);
1282     if (tran && !db.begin_transaction(false)) {
1283       dberrprint(&db, __LINE__, "DB::begin_transaction");
1284       err = true;
1285     }
1286     if (!db.iterate(&visitoriterator, true)) {
1287       dberrprint(&db, __LINE__, "DB::iterate");
1288       err = true;
1289     }
1290     if (rnd) oprintf(" (end)\n");
1291     if (tran && !db.end_transaction(true)) {
1292       dberrprint(&db, __LINE__, "DB::end_transaction");
1293       err = true;
1294     }
1295     if (visitoriterator.cnt() != cnt) {
1296       dberrprint(&db, __LINE__, "DB::iterate");
1297       err = true;
1298     }
1299     etime = kc::time();
1300     dbmetaprint(&db, false);
1301     oprintf("time: %.3f\n", etime - stime);
1302   }
1303   if (mode == 'e') {
1304     oprintf("traversing the database by the outer cursor:\n");
1305     stime = kc::time();
1306     int64_t cnt = db.count();
1307     class VisitorCursor : public kc::DB::Visitor {
1308      public:
1309       explicit VisitorCursor(int64_t rnum, bool rnd) :
1310           rnum_(rnum), rnd_(rnd), cnt_(0), rbuf_() {
1311         std::memset(rbuf_, '-', sizeof(rbuf_));
1312       }
1313       int64_t cnt() {
1314         return cnt_;
1315       }
1316      private:
1317       const char* visit_full(const char* kbuf, size_t ksiz,
1318                              const char* vbuf, size_t vsiz, size_t* sp) {
1319         cnt_++;
1320         const char* rv = NOP;
1321         switch (rnd_ ? myrand(7) : cnt_ % 7) {
1322           case 0: {
1323             rv = rbuf_;
1324             *sp = rnd_ ? myrand(sizeof(rbuf_)) : sizeof(rbuf_) / (cnt_ % 5 + 1);
1325             break;
1326           }
1327           case 1: {
1328             rv = REMOVE;
1329             break;
1330           }
1331         }
1332         if (rnum_ > 250 && cnt_ % (rnum_ / 250) == 0) {
1333           oputchar('.');
1334           if (cnt_ == rnum_ || cnt_ % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)cnt_);
1335         }
1336         return rv;
1337       }
1338       int64_t rnum_;
1339       bool rnd_;
1340       int64_t cnt_;
1341       char rbuf_[RECBUFSIZ];
1342     } visitorcursor(rnum, rnd);
1343     if (tran && !db.begin_transaction(false)) {
1344       dberrprint(&db, __LINE__, "DB::begin_transaction");
1345       err = true;
1346     }
1347     kc::TreeDB::Cursor cur(&db);
1348     if (!cur.jump() && db.error() != kc::BasicDB::Error::NOREC) {
1349       dberrprint(&db, __LINE__, "Cursor::jump");
1350       err = true;
1351     }
1352     kc::DB::Cursor* paracur = db.cursor();
1353     int64_t range = rnum * thnum;
1354     while (!err && cur.accept(&visitorcursor, true, !rnd)) {
1355       if (rnd) {
1356         char kbuf[RECBUFSIZ];
1357         size_t ksiz = std::sprintf(kbuf, "%08lld", (long long)myrand(range));
1358         switch (myrand(3)) {
1359           case 0: {
1360             if (!db.remove(kbuf, ksiz) && db.error() != kc::BasicDB::Error::NOREC) {
1361               dberrprint(&db, __LINE__, "DB::remove");
1362               err = true;
1363             }
1364             break;
1365           }
1366           case 1: {
1367             if (!paracur->jump(kbuf, ksiz) && db.error() != kc::BasicDB::Error::NOREC) {
1368               dberrprint(&db, __LINE__, "Cursor::jump");
1369               err = true;
1370             }
1371             break;
1372           }
1373           default: {
1374             if (!cur.step() && db.error() != kc::BasicDB::Error::NOREC) {
1375               dberrprint(&db, __LINE__, "Cursor::step");
1376               err = true;
1377             }
1378             break;
1379           }
1380         }
1381       }
1382     }
1383     if (db.error() != kc::BasicDB::Error::NOREC) {
1384       dberrprint(&db, __LINE__, "Cursor::accept");
1385       err = true;
1386     }
1387     oprintf(" (end)\n");
1388     delete paracur;
1389     if (tran && !db.end_transaction(true)) {
1390       dberrprint(&db, __LINE__, "DB::end_transaction");
1391       err = true;
1392     }
1393     if (!rnd && visitorcursor.cnt() != cnt) {
1394       dberrprint(&db, __LINE__, "Cursor::accept");
1395       err = true;
1396     }
1397     etime = kc::time();
1398     dbmetaprint(&db, false);
1399     oprintf("time: %.3f\n", etime - stime);
1400   }
1401   if (mode == 'e') {
1402     oprintf("synchronizing the database:\n");
1403     stime = kc::time();
1404     if (!db.synchronize(false, NULL)) {
1405       dberrprint(&db, __LINE__, "DB::synchronize");
1406       err = true;
1407     }
1408     class SyncProcessor : public kc::BasicDB::FileProcessor {
1409      public:
1410       explicit SyncProcessor(int64_t rnum, bool rnd, int64_t size, int64_t msiz) :
1411           rnum_(rnum), rnd_(rnd), size_(size), msiz_(msiz) {}
1412      private:
1413       bool process(const std::string& path, int64_t count, int64_t size) {
1414         kc::File::Status sbuf;
1415         if (!kc::File::status(path, &sbuf)) return false;
1416         if (sbuf.size != size_ && sbuf.size != msiz_ &&
1417             sbuf.size % (1 << 20) != 0) return false;
1418         if (size != size_) return false;
1419         return true;
1420       }
1421       int64_t rnum_;
1422       bool rnd_;
1423       int64_t size_;
1424       int64_t msiz_;
1425     } syncprocessor(rnum, rnd, db.size(), msiz);
1426     if (!db.synchronize(false, &syncprocessor)) {
1427       dberrprint(&db, __LINE__, "DB::synchronize");
1428       err = true;
1429     }
1430     if (!db.occupy(rnd ? myrand(2) == 0 : true, &syncprocessor)) {
1431       dberrprint(&db, __LINE__, "DB::occupy");
1432       err = true;
1433     }
1434     etime = kc::time();
1435     dbmetaprint(&db, false);
1436     oprintf("time: %.3f\n", etime - stime);
1437   }
1438   if (mode == 'e' && db.size() < (256LL << 20)) {
1439     oprintf("dumping records into snapshot:\n");
1440     stime = kc::time();
1441     std::ostringstream ostrm;
1442     if (!db.dump_snapshot(&ostrm)) {
1443       dberrprint(&db, __LINE__, "DB::dump_snapshot");
1444       err = true;
1445     }
1446     etime = kc::time();
1447     dbmetaprint(&db, false);
1448     oprintf("time: %.3f\n", etime - stime);
1449     oprintf("loading records from snapshot:\n");
1450     stime = kc::time();
1451     int64_t cnt = db.count();
1452     if (rnd && myrand(2) == 0 && !db.clear()) {
1453       dberrprint(&db, __LINE__, "DB::clear");
1454       err = true;
1455     }
1456     const std::string& str = ostrm.str();
1457     std::istringstream istrm(str);
1458     if (!db.load_snapshot(&istrm) || db.count() != cnt) {
1459       dberrprint(&db, __LINE__, "DB::load_snapshot");
1460       err = true;
1461     }
1462     etime = kc::time();
1463     dbmetaprint(&db, false);
1464     oprintf("time: %.3f\n", etime - stime);
1465   }
1466   if (mode == 0 || mode == 'r' || mode == 'e') {
1467     oprintf("removing records:\n");
1468     stime = kc::time();
1469     class ThreadRemove : public kc::Thread {
1470      public:
1471       void setparams(int32_t id, kc::BasicDB* db, int64_t rnum, int32_t thnum,
1472                      bool rnd, int32_t mode, bool tran) {
1473         id_ = id;
1474         db_ = db;
1475         rnum_ = rnum;
1476         thnum_ = thnum;
1477         err_ = false;
1478         rnd_ = rnd;
1479         mode_ = mode;
1480         tran_ = tran;
1481       }
1482       bool error() {
1483         return err_;
1484       }
1485       void run() {
1486         int64_t base = id_ * rnum_;
1487         int64_t range = rnum_ * thnum_;
1488         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
1489           if (tran_ && !db_->begin_transaction(false)) {
1490             dberrprint(db_, __LINE__, "DB::begin_transaction");
1491             err_ = true;
1492           }
1493           char kbuf[RECBUFSIZ];
1494           size_t ksiz = std::sprintf(kbuf, "%08lld",
1495                                      (long long)(rnd_ ? myrand(range) + 1 : base + i));
1496           if (!db_->remove(kbuf, ksiz) &&
1497               ((!rnd_ && mode_ != 'e') || db_->error() != kc::BasicDB::Error::NOREC)) {
1498             dberrprint(db_, __LINE__, "DB::remove");
1499             err_ = true;
1500           }
1501           if (rnd_ && i % 8 == 0) {
1502             switch (myrand(8)) {
1503               case 0: {
1504                 if (!db_->set(kbuf, ksiz, kbuf, ksiz)) {
1505                   dberrprint(db_, __LINE__, "DB::set");
1506                   err_ = true;
1507                 }
1508                 break;
1509               }
1510               case 1: {
1511                 if (!db_->append(kbuf, ksiz, kbuf, ksiz)) {
1512                   dberrprint(db_, __LINE__, "DB::append");
1513                   err_ = true;
1514                 }
1515                 break;
1516               }
1517               case 2: {
1518                 if (!db_->remove(kbuf, ksiz) &&
1519                     db_->error() != kc::BasicDB::Error::NOREC) {
1520                   dberrprint(db_, __LINE__, "DB::remove");
1521                   err_ = true;
1522                 }
1523                 break;
1524               }
1525               case 3: {
1526                 kc::DB::Cursor* cur = db_->cursor();
1527                 if (cur->jump(kbuf, ksiz)) {
1528                   switch (myrand(8)) {
1529                     default: {
1530                       size_t rsiz;
1531                       char* rbuf = cur->get_key(&rsiz, myrand(10) == 0);
1532                       if (rbuf) {
1533                         delete[] rbuf;
1534                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1535                         dberrprint(db_, __LINE__, "Cursor::get_key");
1536                         err_ = true;
1537                       }
1538                       break;
1539                     }
1540                     case 1: {
1541                       size_t rsiz;
1542                       char* rbuf = cur->get_value(&rsiz, myrand(10) == 0);
1543                       if (rbuf) {
1544                         delete[] rbuf;
1545                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1546                         dberrprint(db_, __LINE__, "Cursor::get_value");
1547                         err_ = true;
1548                       }
1549                       break;
1550                     }
1551                     case 2: {
1552                       size_t rksiz;
1553                       const char* rvbuf;
1554                       size_t rvsiz;
1555                       char* rkbuf = cur->get(&rksiz, &rvbuf, &rvsiz, myrand(10) == 0);
1556                       if (rkbuf) {
1557                         delete[] rkbuf;
1558                       } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1559                         dberrprint(db_, __LINE__, "Cursor::get");
1560                         err_ = true;
1561                       }
1562                       break;
1563                     }
1564                     case 3: {
1565                       std::string key, value;
1566                       if (!cur->get(&key, &value, myrand(10) == 0) &&
1567                           db_->error() != kc::BasicDB::Error::NOREC) {
1568                         dberrprint(db_, __LINE__, "Cursor::get");
1569                         err_ = true;
1570                       }
1571                       break;
1572                     }
1573                     case 4: {
1574                       if (myrand(8) == 0 && !cur->remove() &&
1575                           db_->error() != kc::BasicDB::Error::NOREC) {
1576                         dberrprint(db_, __LINE__, "Cursor::remove");
1577                         err_ = true;
1578                       }
1579                       break;
1580                     }
1581                   }
1582                 } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1583                   dberrprint(db_, __LINE__, "Cursor::jump");
1584                   err_ = true;
1585                 }
1586                 delete cur;
1587                 break;
1588               }
1589               default: {
1590                 size_t vsiz;
1591                 char* vbuf = db_->get(kbuf, ksiz, &vsiz);
1592                 if (vbuf) {
1593                   delete[] vbuf;
1594                 } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1595                   dberrprint(db_, __LINE__, "DB::get");
1596                   err_ = true;
1597                 }
1598                 break;
1599               }
1600             }
1601           }
1602           if (tran_ && !db_->end_transaction(true)) {
1603             dberrprint(db_, __LINE__, "DB::end_transaction");
1604             err_ = true;
1605           }
1606           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
1607             oputchar('.');
1608             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
1609           }
1610         }
1611       }
1612      private:
1613       int32_t id_;
1614       kc::BasicDB* db_;
1615       int64_t rnum_;
1616       int32_t thnum_;
1617       bool err_;
1618       bool rnd_;
1619       int32_t mode_;
1620       bool tran_;
1621     };
1622     ThreadRemove threadremoves[THREADMAX];
1623     if (thnum < 2) {
1624       threadremoves[0].setparams(0, &db, rnum, thnum, rnd, mode, tran);
1625       threadremoves[0].run();
1626       if (threadremoves[0].error()) err = true;
1627     } else {
1628       for (int32_t i = 0; i < thnum; i++) {
1629         threadremoves[i].setparams(i, &db, rnum, thnum, rnd, mode, tran);
1630         threadremoves[i].start();
1631       }
1632       for (int32_t i = 0; i < thnum; i++) {
1633         threadremoves[i].join();
1634         if (threadremoves[i].error()) err = true;
1635       }
1636     }
1637     etime = kc::time();
1638     dbmetaprint(&db, mode == 'r' || mode == 'e');
1639     oprintf("time: %.3f\n", etime - stime);
1640   }
1641   oprintf("closing the database:\n");
1642   stime = kc::time();
1643   if (!db.close()) {
1644     dberrprint(&db, __LINE__, "DB::close");
1645     err = true;
1646   }
1647   etime = kc::time();
1648   oprintf("time: %.3f\n", etime - stime);
1649   oprintf("%s\n\n", err ? "error" : "ok");
1650   return err ? 1 : 0;
1651 }
1652 
1653 
1654 // perform queue command
procqueue(const char * path,int64_t rnum,int32_t thnum,int32_t itnum,bool rnd,int32_t oflags,int32_t apow,int32_t fpow,int32_t opts,int64_t bnum,int32_t psiz,int64_t msiz,int64_t dfunit,int64_t pccap,kc::Comparator * rcomp,bool lv)1655 static int32_t procqueue(const char* path, int64_t rnum, int32_t thnum, int32_t itnum, bool rnd,
1656                          int32_t oflags, int32_t apow, int32_t fpow, int32_t opts, int64_t bnum,
1657                          int32_t psiz, int64_t msiz, int64_t dfunit, int64_t pccap,
1658                          kc::Comparator* rcomp, bool lv) {
1659   oprintf("<Queue Test>\n  seed=%u  path=%s  rnum=%lld  thnum=%d  itnum=%d  rnd=%d"
1660           "  oflags=%d  apow=%d  fpow=%d  opts=%d  bnum=%lld  psiz=%d  msiz=%lld"
1661           "  dfunit=%lld  pccap=%lld  rcomp=%p  lv=%d\n\n",
1662           g_randseed, path, (long long)rnum, thnum, itnum, rnd, oflags, apow, fpow, opts,
1663           (long long)bnum, psiz, (long long)msiz, (long long)dfunit, (long long)pccap,
1664           rcomp, lv);
1665   bool err = false;
1666   kc::TreeDB db;
1667   db.tune_logger(stdlogger(g_progname, &std::cout),
1668                  lv ? kc::UINT32MAX : kc::BasicDB::Logger::WARN | kc::BasicDB::Logger::ERROR);
1669   if (apow >= 0) db.tune_alignment(apow);
1670   if (fpow >= 0) db.tune_fbp(fpow);
1671   if (opts > 0) db.tune_options(opts);
1672   if (bnum > 0) db.tune_buckets(bnum);
1673   if (psiz > 0) db.tune_page(psiz);
1674   if (msiz >= 0) db.tune_map(msiz);
1675   if (dfunit > 0) db.tune_defrag(dfunit);
1676   if (pccap > 0) db.tune_page_cache(pccap);
1677   if (rcomp) db.tune_comparator(rcomp);
1678   for (int32_t itcnt = 1; itcnt <= itnum; itcnt++) {
1679     if (itnum > 1) oprintf("iteration %d:\n", itcnt);
1680     double stime = kc::time();
1681     uint32_t omode = kc::TreeDB::OWRITER | kc::TreeDB::OCREATE;
1682     if (itcnt == 1) omode |= kc::TreeDB::OTRUNCATE;
1683     if (!db.open(path, omode | oflags)) {
1684       dberrprint(&db, __LINE__, "DB::open");
1685       err = true;
1686     }
1687     class ThreadQueue : public kc::Thread {
1688      public:
1689       void setparams(int32_t id, kc::TreeDB* db, int64_t rnum, int32_t thnum, bool rnd,
1690                      int64_t width) {
1691         id_ = id;
1692         db_ = db;
1693         rnum_ = rnum;
1694         thnum_ = thnum;
1695         rnd_ = rnd;
1696         width_ = width;
1697         err_ = false;
1698       }
1699       bool error() {
1700         return err_;
1701       }
1702       void run() {
1703         kc::DB::Cursor* cur = db_->cursor();
1704         int64_t base = id_ * rnum_;
1705         int64_t range = rnum_ * thnum_;
1706         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
1707           char kbuf[RECBUFSIZ];
1708           size_t ksiz = std::sprintf(kbuf, "%010lld", (long long)(base + i));
1709           if (!db_->set(kbuf, ksiz, kbuf, ksiz)) {
1710             dberrprint(db_, __LINE__, "DB::set");
1711             err_ = true;
1712           }
1713           if (rnd_) {
1714             if (myrand(width_ / 2) == 0) {
1715               if (!cur->jump() && db_->error() != kc::BasicDB::Error::NOREC) {
1716                 dberrprint(db_, __LINE__, "Cursor::jump");
1717                 err_ = true;
1718               }
1719               ksiz = std::sprintf(kbuf, "%010lld", (long long)myrand(range) + 1);
1720               switch (myrand(10)) {
1721                 case 0: {
1722                   if (!db_->set(kbuf, ksiz, kbuf, ksiz)) {
1723                     dberrprint(db_, __LINE__, "DB::set");
1724                     err_ = true;
1725                   }
1726                   break;
1727                 }
1728                 case 1: {
1729                   if (!db_->append(kbuf, ksiz, kbuf, ksiz)) {
1730                     dberrprint(db_, __LINE__, "DB::append");
1731                     err_ = true;
1732                   }
1733                   break;
1734                 }
1735                 case 2: {
1736                   if (!db_->remove(kbuf, ksiz) &&
1737                       db_->error() != kc::BasicDB::Error::NOREC) {
1738                     dberrprint(db_, __LINE__, "DB::remove");
1739                     err_ = true;
1740                   }
1741                   break;
1742                 }
1743               }
1744               int64_t dnum = myrand(width_) + 2;
1745               for (int64_t j = 0; j < dnum; j++) {
1746                 if (myrand(2) == 0) {
1747                   size_t rsiz;
1748                   char* rbuf = cur->get_key(&rsiz);
1749                   if (rbuf) {
1750                     if (myrand(10) == 0 && !db_->remove(rbuf, rsiz) &&
1751                         db_->error() != kc::BasicDB::Error::NOREC) {
1752                       dberrprint(db_, __LINE__, "DB::remove");
1753                       err_ = true;
1754                     }
1755                     if (myrand(2) == 0 && !cur->jump(rbuf, rsiz) &&
1756                         db_->error() != kc::BasicDB::Error::NOREC) {
1757                       dberrprint(db_, __LINE__, "Cursor::jump");
1758                       err_ = true;
1759                     }
1760                     if (myrand(10) == 0 && !db_->remove(rbuf, rsiz) &&
1761                         db_->error() != kc::BasicDB::Error::NOREC) {
1762                       dberrprint(db_, __LINE__, "DB::remove");
1763                       err_ = true;
1764                     }
1765                     delete[] rbuf;
1766                   } else if (db_->error() != kc::BasicDB::Error::NOREC) {
1767                     dberrprint(db_, __LINE__, "Cursor::get_key");
1768                     err_ = true;
1769                   }
1770                 }
1771                 if (!cur->remove() && db_->error() != kc::BasicDB::Error::NOREC) {
1772                   dberrprint(db_, __LINE__, "Cursor::remove");
1773                   err_ = true;
1774                 }
1775               }
1776             }
1777           } else {
1778             if (i > width_) {
1779               if (!cur->jump() && db_->error() != kc::BasicDB::Error::NOREC) {
1780                 dberrprint(db_, __LINE__, "Cursor::jump");
1781                 err_ = true;
1782               }
1783               if (!cur->remove() && db_->error() != kc::BasicDB::Error::NOREC) {
1784                 dberrprint(db_, __LINE__, "Cursor::remove");
1785                 err_ = true;
1786               }
1787             }
1788           }
1789           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
1790             oputchar('.');
1791             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
1792           }
1793         }
1794         delete cur;
1795       }
1796      private:
1797       int32_t id_;
1798       kc::TreeDB* db_;
1799       int64_t rnum_;
1800       int32_t thnum_;
1801       bool rnd_;
1802       int64_t width_;
1803       bool err_;
1804     };
1805     int64_t width = rnum / 10;
1806     ThreadQueue threads[THREADMAX];
1807     if (thnum < 2) {
1808       threads[0].setparams(0, &db, rnum, thnum, rnd, width);
1809       threads[0].run();
1810       if (threads[0].error()) err = true;
1811     } else {
1812       for (int32_t i = 0; i < thnum; i++) {
1813         threads[i].setparams(i, &db, rnum, thnum, rnd, width);
1814         threads[i].start();
1815       }
1816       for (int32_t i = 0; i < thnum; i++) {
1817         threads[i].join();
1818         if (threads[i].error()) err = true;
1819       }
1820     }
1821     int64_t count = db.count();
1822     if (!rnd && itcnt == 1 && count != width * thnum) {
1823       dberrprint(&db, __LINE__, "DB::count");
1824       err = true;
1825     }
1826     if ((rnd ? (myrand(2) == 0) : itcnt == itnum) && count > 0) {
1827       kc::DB::Cursor* cur = db.cursor();
1828       if (!cur->jump()) {
1829         dberrprint(&db, __LINE__, "Cursor::jump");
1830         err = true;
1831       }
1832       for (int64_t i = 1; i <= count; i++) {
1833         if (!cur->remove()) {
1834           dberrprint(&db, __LINE__, "Cursor::remove");
1835           err = true;
1836         }
1837         if (rnum > 250 && i % (rnum / 250) == 0) {
1838           oputchar('.');
1839           if (i == rnum || i % (rnum / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
1840         }
1841       }
1842       if (rnd) oprintf(" (end)\n");
1843       delete cur;
1844       if (db.count() != 0) {
1845         dberrprint(&db, __LINE__, "DB::count");
1846         err = true;
1847       }
1848     }
1849     dbmetaprint(&db, itcnt == itnum);
1850     if (!db.close()) {
1851       dberrprint(&db, __LINE__, "DB::close");
1852       err = true;
1853     }
1854     oprintf("time: %.3f\n", kc::time() - stime);
1855   }
1856   oprintf("%s\n\n", err ? "error" : "ok");
1857   return err ? 1 : 0;
1858 }
1859 
1860 
1861 // perform wicked command
procwicked(const char * path,int64_t rnum,int32_t thnum,int32_t itnum,int32_t oflags,int32_t apow,int32_t fpow,int32_t opts,int64_t bnum,int32_t psiz,int64_t msiz,int64_t dfunit,int64_t pccap,kc::Comparator * rcomp,bool lv)1862 static int32_t procwicked(const char* path, int64_t rnum, int32_t thnum, int32_t itnum,
1863                           int32_t oflags, int32_t apow, int32_t fpow, int32_t opts, int64_t bnum,
1864                           int32_t psiz, int64_t msiz, int64_t dfunit, int64_t pccap,
1865                           kc::Comparator* rcomp, bool lv) {
1866   oprintf("<Wicked Test>\n  seed=%u  path=%s  rnum=%lld  thnum=%d  itnum=%d"
1867           "  oflags=%d  apow=%d  fpow=%d  opts=%d  bnum=%lld  psiz=%d  msiz=%lld"
1868           "  dfunit=%lld  pccap=%lld  rcomp=%p  lv=%d\n\n",
1869           g_randseed, path, (long long)rnum, thnum, itnum, oflags, apow, fpow, opts,
1870           (long long)bnum, psiz, (long long)msiz, (long long)dfunit, (long long)pccap,
1871           rcomp, lv);
1872   bool err = false;
1873   kc::TreeDB db;
1874   db.tune_logger(stdlogger(g_progname, &std::cout),
1875                  lv ? kc::UINT32MAX : kc::BasicDB::Logger::WARN | kc::BasicDB::Logger::ERROR);
1876   if (apow >= 0) db.tune_alignment(apow);
1877   if (fpow >= 0) db.tune_fbp(fpow);
1878   if (opts > 0) db.tune_options(opts);
1879   if (bnum > 0) db.tune_buckets(bnum);
1880   if (psiz > 0) db.tune_page(psiz);
1881   if (msiz >= 0) db.tune_map(msiz);
1882   if (dfunit > 0) db.tune_defrag(dfunit);
1883   if (pccap > 0) db.tune_page_cache(pccap);
1884   if (rcomp) db.tune_comparator(rcomp);
1885   for (int32_t itcnt = 1; itcnt <= itnum; itcnt++) {
1886     if (itnum > 1) oprintf("iteration %d:\n", itcnt);
1887     double stime = kc::time();
1888     uint32_t omode = kc::TreeDB::OWRITER | kc::TreeDB::OCREATE;
1889     if (itcnt == 1) omode |= kc::TreeDB::OTRUNCATE;
1890     if (!db.open(path, omode | oflags)) {
1891       dberrprint(&db, __LINE__, "DB::open");
1892       err = true;
1893     }
1894     class ThreadWicked : public kc::Thread {
1895      public:
1896       void setparams(int32_t id, kc::TreeDB* db, int64_t rnum, int32_t thnum,
1897                      const char* lbuf) {
1898         id_ = id;
1899         db_ = db;
1900         rnum_ = rnum;
1901         thnum_ = thnum;
1902         lbuf_ = lbuf;
1903         err_ = false;
1904       }
1905       bool error() {
1906         return err_;
1907       }
1908       void run() {
1909         kc::DB::Cursor* cur = db_->cursor();
1910         int64_t range = rnum_ * thnum_ / 2;
1911         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
1912           bool tran = myrand(100) == 0;
1913           if (tran) {
1914             if (myrand(2) == 0) {
1915               if (!db_->begin_transaction(myrand(rnum_) == 0)) {
1916                 dberrprint(db_, __LINE__, "DB::begin_transaction");
1917                 tran = false;
1918                 err_ = true;
1919               }
1920             } else {
1921               if (!db_->begin_transaction_try(myrand(rnum_) == 0)) {
1922                 if (db_->error() != kc::BasicDB::Error::LOGIC) {
1923                   dberrprint(db_, __LINE__, "DB::begin_transaction_try");
1924                   err_ = true;
1925                 }
1926                 tran = false;
1927               }
1928             }
1929           }
1930           char kbuf[RECBUFSIZ];
1931           size_t ksiz = std::sprintf(kbuf, "%lld", (long long)(myrand(range) + 1));
1932           if (myrand(1000) == 0) {
1933             ksiz = myrand(RECBUFSIZ) + 1;
1934             if (myrand(2) == 0) {
1935               for (size_t j = 0; j < ksiz; j++) {
1936                 kbuf[j] = j;
1937               }
1938             } else {
1939               for (size_t j = 0; j < ksiz; j++) {
1940                 kbuf[j] = myrand(256);
1941               }
1942             }
1943           }
1944           const char* vbuf = kbuf;
1945           size_t vsiz = ksiz;
1946           if (myrand(10) == 0) {
1947             vbuf = lbuf_;
1948             vsiz = myrand(RECBUFSIZL) / (myrand(5) + 1);
1949           }
1950           do {
1951             switch (myrand(10)) {
1952               case 0: {
1953                 if (!db_->set(kbuf, ksiz, vbuf, vsiz)) {
1954                   dberrprint(db_, __LINE__, "DB::set");
1955                   err_ = true;
1956                 }
1957                 break;
1958               }
1959               case 1: {
1960                 if (!db_->add(kbuf, ksiz, vbuf, vsiz) &&
1961                     db_->error() != kc::BasicDB::Error::DUPREC) {
1962                   dberrprint(db_, __LINE__, "DB::add");
1963                   err_ = true;
1964                 }
1965                 break;
1966               }
1967               case 2: {
1968                 if (!db_->replace(kbuf, ksiz, vbuf, vsiz) &&
1969                     db_->error() != kc::BasicDB::Error::NOREC) {
1970                   dberrprint(db_, __LINE__, "DB::replace");
1971                   err_ = true;
1972                 }
1973                 break;
1974               }
1975               case 3: {
1976                 if (!db_->append(kbuf, ksiz, vbuf, vsiz)) {
1977                   dberrprint(db_, __LINE__, "DB::append");
1978                   err_ = true;
1979                 }
1980                 break;
1981               }
1982               case 4: {
1983                 if (myrand(2) == 0) {
1984                   int64_t num = myrand(rnum_);
1985                   int64_t orig = myrand(10) == 0 ? kc::INT64MIN : myrand(rnum_);
1986                   if (myrand(10) == 0) orig = orig == kc::INT64MIN ? kc::INT64MAX : -orig;
1987                   if (db_->increment(kbuf, ksiz, num, orig) == kc::INT64MIN &&
1988                       db_->error() != kc::BasicDB::Error::LOGIC) {
1989                     dberrprint(db_, __LINE__, "DB::increment");
1990                     err_ = true;
1991                   }
1992                 } else {
1993                   double num = myrand(rnum_ * 10) / (myrand(rnum_) + 1.0);
1994                   double orig = myrand(10) == 0 ? -kc::inf() : myrand(rnum_);
1995                   if (myrand(10) == 0) orig = -orig;
1996                   if (kc::chknan(db_->increment_double(kbuf, ksiz, num, orig)) &&
1997                       db_->error() != kc::BasicDB::Error::LOGIC) {
1998                     dberrprint(db_, __LINE__, "DB::increment_double");
1999                     err_ = true;
2000                   }
2001                 }
2002                 break;
2003               }
2004               case 5: {
2005                 if (!db_->cas(kbuf, ksiz, kbuf, ksiz, vbuf, vsiz) &&
2006                     db_->error() != kc::BasicDB::Error::LOGIC) {
2007                   dberrprint(db_, __LINE__, "DB::cas");
2008                   err_ = true;
2009                 }
2010                 break;
2011               }
2012               case 6: {
2013                 if (!db_->remove(kbuf, ksiz) &&
2014                     db_->error() != kc::BasicDB::Error::NOREC) {
2015                   dberrprint(db_, __LINE__, "DB::remove");
2016                   err_ = true;
2017                 }
2018                 break;
2019               }
2020               case 7: {
2021                 if (myrand(2) == 0) {
2022                   if (db_->check(kbuf, ksiz) < 0 && db_->error() != kc::BasicDB::Error::NOREC) {
2023                     dberrprint(db_, __LINE__, "DB::check");
2024                     err_ = true;
2025                   }
2026                 } else {
2027                   size_t rsiz;
2028                   char* rbuf = db_->seize(kbuf, ksiz, &rsiz);
2029                   if (rbuf) {
2030                     delete[] rbuf;
2031                   } else if (db_->error() != kc::BasicDB::Error::NOREC) {
2032                     dberrprint(db_, __LINE__, "DB::seize");
2033                     err_ = true;
2034                   }
2035                 }
2036                 break;
2037               }
2038               case 8: {
2039                 if (myrand(10) == 0) {
2040                   if (myrand(4) == 0) {
2041                     if (!cur->jump_back(kbuf, ksiz) &&
2042                         db_->error() != kc::BasicDB::Error::NOREC) {
2043                       dberrprint(db_, __LINE__, "Cursor::jump_back");
2044                       err_ = true;
2045                     }
2046                   } else {
2047                     if (!cur->jump(kbuf, ksiz) &&
2048                         db_->error() != kc::BasicDB::Error::NOREC) {
2049                       dberrprint(db_, __LINE__, "Cursor::jump");
2050                       err_ = true;
2051                     }
2052                   }
2053                 } else {
2054                   class VisitorImpl : public kc::DB::Visitor {
2055                    public:
2056                     explicit VisitorImpl(const char* lbuf) : lbuf_(lbuf) {}
2057                    private:
2058                     const char* visit_full(const char* kbuf, size_t ksiz,
2059                                            const char* vbuf, size_t vsiz, size_t* sp) {
2060                       const char* rv = NOP;
2061                       switch (myrand(3)) {
2062                         case 0: {
2063                           rv = lbuf_;
2064                           *sp = myrand(RECBUFSIZL) / (myrand(5) + 1);
2065                           break;
2066                         }
2067                         case 1: {
2068                           rv = REMOVE;
2069                           break;
2070                         }
2071                       }
2072                       return rv;
2073                     }
2074                     const char* lbuf_;
2075                   } visitor(lbuf_);
2076                   if (!cur->accept(&visitor, true, myrand(2) == 0) &&
2077                       db_->error() != kc::BasicDB::Error::NOREC) {
2078                     dberrprint(db_, __LINE__, "Cursor::accept");
2079                     err_ = true;
2080                   }
2081                   if (myrand(3) == 0 && !cur->step() &&
2082                       db_->error() != kc::BasicDB::Error::NOREC) {
2083                     dberrprint(db_, __LINE__, "Cursor::step");
2084                     err_ = true;
2085                   }
2086                   if (myrand(3) == 0 && !cur->step_back() &&
2087                       db_->error() != kc::BasicDB::Error::NOREC) {
2088                     dberrprint(db_, __LINE__, "Cursor::step");
2089                     err_ = true;
2090                   }
2091                 }
2092                 break;
2093               }
2094               default: {
2095                 size_t rsiz;
2096                 char* rbuf = db_->get(kbuf, ksiz, &rsiz);
2097                 if (rbuf) {
2098                   delete[] rbuf;
2099                 } else if (db_->error() != kc::BasicDB::Error::NOREC) {
2100                   dberrprint(db_, __LINE__, "DB::get");
2101                   err_ = true;
2102                 }
2103                 break;
2104               }
2105             }
2106           } while (myrand(100) == 0);
2107           if (myrand(100) == 0) {
2108             int32_t jnum = myrand(10);
2109             switch (myrand(4)) {
2110               case 0: {
2111                 std::map<std::string, std::string> recs;
2112                 for (int32_t j = 0; j < jnum; j++) {
2113                   char jbuf[RECBUFSIZ];
2114                   size_t jsiz = std::sprintf(jbuf, "%lld", (long long)(myrand(range) + 1));
2115                   recs[std::string(jbuf, jsiz)] = std::string(kbuf, ksiz);
2116                 }
2117                 if (db_->set_bulk(recs, myrand(4)) != (int64_t)recs.size()) {
2118                   dberrprint(db_, __LINE__, "DB::set_bulk");
2119                   err_ = true;
2120                 }
2121                 break;
2122               }
2123               case 1: {
2124                 std::vector<std::string> keys;
2125                 for (int32_t j = 0; j < jnum; j++) {
2126                   char jbuf[RECBUFSIZ];
2127                   size_t jsiz = std::sprintf(jbuf, "%lld", (long long)(myrand(range) + 1));
2128                   keys.push_back(std::string(jbuf, jsiz));
2129                 }
2130                 if (db_->remove_bulk(keys, myrand(4)) < 0) {
2131                   dberrprint(db_, __LINE__, "DB::remove_bulk");
2132                   err_ = true;
2133                 }
2134                 break;
2135               }
2136               default: {
2137                 std::vector<std::string> keys;
2138                 for (int32_t j = 0; j < jnum; j++) {
2139                   char jbuf[RECBUFSIZ];
2140                   size_t jsiz = std::sprintf(jbuf, "%lld", (long long)(myrand(range) + 1));
2141                   keys.push_back(std::string(jbuf, jsiz));
2142                 }
2143                 std::map<std::string, std::string> recs;
2144                 if (db_->get_bulk(keys, &recs, myrand(4)) < 0) {
2145                   dberrprint(db_, __LINE__, "DB::get_bulk");
2146                   err_ = true;
2147                 }
2148                 break;
2149               }
2150             }
2151           }
2152           if (i == rnum_ / 2) {
2153             if (myrand(thnum_ * 4) == 0) {
2154               if (myrand(2) == 0) {
2155                 if (!db_->defrag(0)) {
2156                   dberrprint(db_, __LINE__, "DB::defrag");
2157                   err_ = true;
2158                 }
2159               } else {
2160                 if (!db_->clear()) {
2161                   dberrprint(db_, __LINE__, "DB::clear");
2162                   err_ = true;
2163                 }
2164               }
2165             } else {
2166               class SyncProcessor : public kc::BasicDB::FileProcessor {
2167                private:
2168                 bool process(const std::string& path, int64_t count, int64_t size) {
2169                   yield();
2170                   return true;
2171                 }
2172               } syncprocessor;
2173               if (!db_->synchronize(false, &syncprocessor)) {
2174                 dberrprint(db_, __LINE__, "DB::synchronize");
2175                 err_ = true;
2176               }
2177             }
2178           }
2179           if (tran) {
2180             yield();
2181             if (!db_->end_transaction(myrand(10) > 0)) {
2182               dberrprint(db_, __LINE__, "DB::end_transactin");
2183               err_ = true;
2184             }
2185           }
2186           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
2187             oputchar('.');
2188             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
2189           }
2190         }
2191         delete cur;
2192       }
2193      private:
2194       int32_t id_;
2195       kc::TreeDB* db_;
2196       int64_t rnum_;
2197       int32_t thnum_;
2198       const char* lbuf_;
2199       bool err_;
2200     };
2201     char lbuf[RECBUFSIZL];
2202     std::memset(lbuf, '*', sizeof(lbuf));
2203     ThreadWicked threads[THREADMAX];
2204     if (thnum < 2) {
2205       threads[0].setparams(0, &db, rnum, thnum, lbuf);
2206       threads[0].run();
2207       if (threads[0].error()) err = true;
2208     } else {
2209       for (int32_t i = 0; i < thnum; i++) {
2210         threads[i].setparams(i, &db, rnum, thnum, lbuf);
2211         threads[i].start();
2212       }
2213       for (int32_t i = 0; i < thnum; i++) {
2214         threads[i].join();
2215         if (threads[i].error()) err = true;
2216       }
2217     }
2218     dbmetaprint(&db, itcnt == itnum);
2219     if (!db.close()) {
2220       dberrprint(&db, __LINE__, "DB::close");
2221       err = true;
2222     }
2223     oprintf("time: %.3f\n", kc::time() - stime);
2224   }
2225   oprintf("%s\n\n", err ? "error" : "ok");
2226   return err ? 1 : 0;
2227 }
2228 
2229 
2230 // perform tran command
proctran(const char * path,int64_t rnum,int32_t thnum,int32_t itnum,bool hard,int32_t oflags,int32_t apow,int32_t fpow,int32_t opts,int64_t bnum,int32_t psiz,int64_t msiz,int64_t dfunit,int64_t pccap,kc::Comparator * rcomp,bool lv)2231 static int32_t proctran(const char* path, int64_t rnum, int32_t thnum, int32_t itnum, bool hard,
2232                         int32_t oflags, int32_t apow, int32_t fpow, int32_t opts, int64_t bnum,
2233                         int32_t psiz, int64_t msiz, int64_t dfunit, int64_t pccap,
2234                         kc::Comparator* rcomp, bool lv) {
2235   oprintf("<Transaction Test>\n  seed=%u  path=%s  rnum=%lld  thnum=%d  itnum=%d  hard=%d"
2236           "  oflags=%d  apow=%d  fpow=%d  opts=%d  bnum=%lld  psiz=%d  msiz=%lld"
2237           "  dfunit=%lld  pccap=%lld  rcomp=%p  lv=%d\n\n",
2238           g_randseed, path, (long long)rnum, thnum, itnum, hard, oflags, apow, fpow, opts,
2239           (long long)bnum, psiz, (long long)msiz, (long long)dfunit, (long long)pccap,
2240           rcomp, lv);
2241   bool err = false;
2242   kc::TreeDB db;
2243   kc::TreeDB paradb;
2244   db.tune_logger(stdlogger(g_progname, &std::cout),
2245                  lv ? kc::UINT32MAX : kc::BasicDB::Logger::WARN | kc::BasicDB::Logger::ERROR);
2246   paradb.tune_logger(stdlogger(g_progname, &std::cout), lv ? kc::UINT32MAX :
2247                      kc::BasicDB::Logger::WARN | kc::BasicDB::Logger::ERROR);
2248   if (apow >= 0) db.tune_alignment(apow);
2249   if (fpow >= 0) db.tune_fbp(fpow);
2250   if (opts > 0) db.tune_options(opts);
2251   if (bnum > 0) db.tune_buckets(bnum);
2252   if (psiz > 0) db.tune_page(psiz);
2253   if (msiz >= 0) db.tune_map(msiz);
2254   if (dfunit > 0) db.tune_defrag(dfunit);
2255   if (pccap > 0) db.tune_page_cache(pccap);
2256   if (rcomp) db.tune_comparator(rcomp);
2257   for (int32_t itcnt = 1; itcnt <= itnum; itcnt++) {
2258     oprintf("iteration %d updating:\n", itcnt);
2259     double stime = kc::time();
2260     uint32_t omode = kc::TreeDB::OWRITER | kc::TreeDB::OCREATE;
2261     if (itcnt == 1) omode |= kc::TreeDB::OTRUNCATE;
2262     if (!db.open(path, omode | oflags)) {
2263       dberrprint(&db, __LINE__, "DB::open");
2264       err = true;
2265     }
2266     std::string parapath = db.path() + "-para";
2267     if (!paradb.open(parapath, omode)) {
2268       dberrprint(&paradb, __LINE__, "DB::open");
2269       err = true;
2270     }
2271     class ThreadTran : public kc::Thread {
2272      public:
2273       void setparams(int32_t id, kc::TreeDB* db, kc::TreeDB* paradb, int64_t rnum,
2274                      int32_t thnum, bool hard, const char* lbuf) {
2275         id_ = id;
2276         db_ = db;
2277         paradb_ = paradb;
2278         rnum_ = rnum;
2279         thnum_ = thnum;
2280         hard_ = hard;
2281         lbuf_ = lbuf;
2282         err_ = false;
2283       }
2284       bool error() {
2285         return err_;
2286       }
2287       void run() {
2288         kc::DB::Cursor* cur = db_->cursor();
2289         int64_t range = rnum_ * thnum_;
2290         char kbuf[RECBUFSIZ];
2291         size_t ksiz = std::sprintf(kbuf, "%lld", (long long)(myrand(range) + 1));
2292         if (!cur->jump(kbuf, ksiz) && db_->error() != kc::BasicDB::Error::NOREC) {
2293           dberrprint(db_, __LINE__, "Cursor::jump");
2294           err_ = true;
2295         }
2296         bool tran = true;
2297         if (!db_->begin_transaction(hard_)) {
2298           dberrprint(db_, __LINE__, "DB::begin_transaction");
2299           tran = false;
2300           err_ = true;
2301         }
2302         bool commit = myrand(10) > 0;
2303         for (int64_t i = 1; !err_ && i <= rnum_; i++) {
2304           ksiz = std::sprintf(kbuf, "%lld", (long long)(myrand(range) + 1));
2305           const char* vbuf = kbuf;
2306           size_t vsiz = ksiz;
2307           if (myrand(10) == 0) {
2308             vbuf = lbuf_;
2309             vsiz = myrand(RECBUFSIZL) / (myrand(5) + 1);
2310           }
2311           class VisitorImpl : public kc::DB::Visitor {
2312            public:
2313             explicit VisitorImpl(const char* vbuf, size_t vsiz, kc::BasicDB* paradb) :
2314                 vbuf_(vbuf), vsiz_(vsiz), paradb_(paradb) {}
2315            private:
2316             const char* visit_full(const char* kbuf, size_t ksiz,
2317                                    const char* vbuf, size_t vsiz, size_t* sp) {
2318               return visit_empty(kbuf, ksiz, sp);
2319             }
2320             const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
2321               const char* rv = NOP;
2322               switch (myrand(3)) {
2323                 case 0: {
2324                   rv = vbuf_;
2325                   *sp = vsiz_;
2326                   if (paradb_) paradb_->set(kbuf, ksiz, vbuf_, vsiz_);
2327                   break;
2328                 }
2329                 case 1: {
2330                   rv = REMOVE;
2331                   if (paradb_) paradb_->remove(kbuf, ksiz);
2332                   break;
2333                 }
2334               }
2335               return rv;
2336             }
2337             const char* vbuf_;
2338             size_t vsiz_;
2339             kc::BasicDB* paradb_;
2340           } visitor(vbuf, vsiz, !tran || commit ? paradb_ : NULL);
2341           if (myrand(4) == 0) {
2342             if (!cur->accept(&visitor, true, myrand(2) == 0) &&
2343                 db_->error() != kc::BasicDB::Error::NOREC) {
2344               dberrprint(db_, __LINE__, "Cursor::accept");
2345               err_ = true;
2346             }
2347           } else {
2348             if (!db_->accept(kbuf, ksiz, &visitor, true)) {
2349               dberrprint(db_, __LINE__, "DB::accept");
2350               err_ = true;
2351             }
2352           }
2353           if (myrand(1000) == 0) {
2354             ksiz = std::sprintf(kbuf, "%lld", (long long)(myrand(range) + 1));
2355             if (!cur->jump(kbuf, ksiz)) {
2356               if (db_->error() != kc::BasicDB::Error::NOREC) {
2357                 dberrprint(db_, __LINE__, "Cursor::jump");
2358                 err_ = true;
2359               } else if (!cur->jump() && db_->error() != kc::BasicDB::Error::NOREC) {
2360                 dberrprint(db_, __LINE__, "Cursor::jump");
2361                 err_ = true;
2362               }
2363             }
2364             std::vector<std::string> keys;
2365             keys.reserve(100);
2366             while (myrand(50) != 0) {
2367               std::string key;
2368               if (cur->get_key(&key)) {
2369                 keys.push_back(key);
2370                 if (!cur->get_value(&key) && db_->error() != kc::BasicDB::Error::NOREC) {
2371                   dberrprint(db_, __LINE__, "Cursor::get_value");
2372                   err_ = true;
2373                 }
2374               } else {
2375                 if (db_->error() != kc::BasicDB::Error::NOREC) {
2376                   dberrprint(db_, __LINE__, "Cursor::get_key");
2377                   err_ = true;
2378                 }
2379                 break;
2380               }
2381               if (!cur->step()) {
2382                 if (db_->error() != kc::BasicDB::Error::NOREC) {
2383                   dberrprint(db_, __LINE__, "Cursor::jump");
2384                   err_ = true;
2385                 }
2386                 break;
2387               }
2388             }
2389             class Remover : public kc::DB::Visitor {
2390              public:
2391               explicit Remover(kc::BasicDB* paradb) : paradb_(paradb) {}
2392              private:
2393               const char* visit_full(const char* kbuf, size_t ksiz,
2394                                      const char* vbuf, size_t vsiz, size_t* sp) {
2395                 if (myrand(200) == 0) return NOP;
2396                 if (paradb_) paradb_->remove(kbuf, ksiz);
2397                 return REMOVE;
2398               }
2399               kc::BasicDB* paradb_;
2400             } remover(!tran || commit ? paradb_ : NULL);
2401             std::vector<std::string>::iterator it = keys.begin();
2402             std::vector<std::string>::iterator end = keys.end();
2403             while (it != end) {
2404               if (myrand(50) == 0) {
2405                 if (!cur->accept(&remover, true, false) &&
2406                     db_->error() != kc::BasicDB::Error::NOREC) {
2407                   dberrprint(db_, __LINE__, "Cursor::accept");
2408                   err_ = true;
2409                 }
2410               } else {
2411                 if (!db_->accept(it->c_str(), it->size(), &remover, true)) {
2412                   dberrprint(db_, __LINE__, "DB::accept");
2413                   err_ = true;
2414                 }
2415               }
2416               ++it;
2417             }
2418           }
2419           if (tran && myrand(100) == 0) {
2420             if (db_->end_transaction(commit)) {
2421               yield();
2422               if (!db_->begin_transaction(hard_)) {
2423                 dberrprint(db_, __LINE__, "DB::begin_transaction");
2424                 tran = false;
2425                 err_ = true;
2426               }
2427             } else {
2428               dberrprint(db_, __LINE__, "DB::end_transaction");
2429               err_ = true;
2430             }
2431           }
2432           if (id_ < 1 && rnum_ > 250 && i % (rnum_ / 250) == 0) {
2433             oputchar('.');
2434             if (i == rnum_ || i % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)i);
2435           }
2436         }
2437         if (tran && !db_->end_transaction(commit)) {
2438           dberrprint(db_, __LINE__, "DB::end_transaction");
2439           err_ = true;
2440         }
2441         delete cur;
2442       }
2443      private:
2444       int32_t id_;
2445       kc::TreeDB* db_;
2446       kc::TreeDB* paradb_;
2447       int64_t rnum_;
2448       int32_t thnum_;
2449       bool hard_;
2450       const char* lbuf_;
2451       bool err_;
2452     };
2453     char lbuf[RECBUFSIZL];
2454     std::memset(lbuf, '*', sizeof(lbuf));
2455     ThreadTran threads[THREADMAX];
2456     if (thnum < 2) {
2457       threads[0].setparams(0, &db, &paradb, rnum, thnum, hard, lbuf);
2458       threads[0].run();
2459       if (threads[0].error()) err = true;
2460     } else {
2461       for (int32_t i = 0; i < thnum; i++) {
2462         threads[i].setparams(i, &db, &paradb, rnum, thnum, hard, lbuf);
2463         threads[i].start();
2464       }
2465       for (int32_t i = 0; i < thnum; i++) {
2466         threads[i].join();
2467         if (threads[i].error()) err = true;
2468       }
2469     }
2470     oprintf("iteration %d checking:\n", itcnt);
2471     if (db.count() != paradb.count()) {
2472       dberrprint(&db, __LINE__, "DB::count");
2473       err = true;
2474     }
2475     class VisitorImpl : public kc::DB::Visitor {
2476      public:
2477       explicit VisitorImpl(int64_t rnum, kc::BasicDB* paradb) :
2478           rnum_(rnum), paradb_(paradb), err_(false), cnt_(0) {}
2479       bool error() {
2480         return err_;
2481       }
2482      private:
2483       const char* visit_full(const char* kbuf, size_t ksiz,
2484                              const char* vbuf, size_t vsiz, size_t* sp) {
2485         cnt_++;
2486         size_t rsiz;
2487         char* rbuf = paradb_->get(kbuf, ksiz, &rsiz);
2488         if (rbuf) {
2489           delete[] rbuf;
2490         } else {
2491           dberrprint(paradb_, __LINE__, "DB::get");
2492           err_ = true;
2493         }
2494         if (rnum_ > 250 && cnt_ % (rnum_ / 250) == 0) {
2495           oputchar('.');
2496           if (cnt_ == rnum_ || cnt_ % (rnum_ / 10) == 0) oprintf(" (%08lld)\n", (long long)cnt_);
2497         }
2498         return NOP;
2499       }
2500       int64_t rnum_;
2501       kc::BasicDB* paradb_;
2502       bool err_;
2503       int64_t cnt_;
2504     } visitor(rnum, &paradb), paravisitor(rnum, &db);
2505     if (!db.iterate(&visitor, false)) {
2506       dberrprint(&db, __LINE__, "DB::iterate");
2507       err = true;
2508     }
2509     oprintf(" (end)\n");
2510     if (visitor.error()) err = true;
2511     if (!paradb.iterate(&paravisitor, false)) {
2512       dberrprint(&db, __LINE__, "DB::iterate");
2513       err = true;
2514     }
2515     oprintf(" (end)\n");
2516     if (paravisitor.error()) err = true;
2517     if (!paradb.close()) {
2518       dberrprint(&paradb, __LINE__, "DB::close");
2519       err = true;
2520     }
2521     dbmetaprint(&db, itcnt == itnum);
2522     if (!db.close()) {
2523       dberrprint(&db, __LINE__, "DB::close");
2524       err = true;
2525     }
2526     oprintf("time: %.3f\n", kc::time() - stime);
2527   }
2528   oprintf("%s\n\n", err ? "error" : "ok");
2529   return err ? 1 : 0;
2530 }
2531 
2532 
2533 
2534 // END OF FILE
2535