1 /*************************************************************************************************
2  * The test cases of the core API
3  *                                                               Copyright (C) 2007-2010 FAL Labs
4  * This file is part of Tokyo Dystopia.
5  * Tokyo Dystopia is free software; you can redistribute it and/or modify it under the terms of
6  * the GNU Lesser General Public License as published by the Free Software Foundation; either
7  * version 2.1 of the License or any later version.  Tokyo Dystopia is distributed in the hope
8  * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
10  * License for more details.
11  * You should have received a copy of the GNU Lesser General Public License along with Tokyo
12  * Dystopia; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13  * Boston, MA 02111-1307 USA.
14  *************************************************************************************************/
15 
16 
17 #include <dystopia.h>
18 #include "myconf.h"
19 
20 #define TEXTBUFSIZ     65536             // buffer for texts
21 #define DEFAVGLEN      16                // default average text length
22 
23 
24 /* global variables */
25 const char *g_progname;                  // program name
26 int g_dbgfd;                             // debugging output
27 
28 
29 /* function prototypes */
30 int main(int argc, char **argv);
31 static void usage(void);
32 static void iprintf(const char *format, ...);
33 static void eprint(TCIDB *idb, const char *func);
34 static void sysprint(void);
35 static int myrand(int range);
36 static bool mysynccb(int total, int current, const char *msg, void *opq);
37 static void setrndtext(char *buf, int avg, int min, bool en);
38 static void setidtext(char *buf, uint64_t id, bool en);
39 static int runwrite(int argc, char **argv);
40 static int runread(int argc, char **argv);
41 static int runwicked(int argc, char **argv);
42 static int procwrite(const char *path, int rnum, int opts,
43                      int64_t ernum, int64_t etnum, int64_t iusiz, int64_t icsiz, int exopts,
44                      int omode, int alen, bool en);
45 static int procread(const char *path, int rnum, int omode, int alen, int mlen, bool en,
46                     int smode);
47 static int procwicked(const char *path, int rnum, int opts,
48                       int64_t ernum, int64_t etnum, int64_t iusiz, int64_t icsiz,
49                       int omode, int alen, bool en);
50 
51 
52 /* main routine */
main(int argc,char ** argv)53 int main(int argc, char **argv){
54   g_progname = argv[0];
55   g_dbgfd = -1;
56   const char *ebuf = getenv("TCDBGFD");
57   if(ebuf) g_dbgfd = tcatoi(ebuf);
58   srand((unsigned int)(tctime() * 1000) % UINT_MAX);
59   if(argc < 2) usage();
60   int rv = 0;
61   if(!strcmp(argv[1], "write")){
62     rv = runwrite(argc, argv);
63   } else if(!strcmp(argv[1], "read")){
64     rv = runread(argc, argv);
65   } else if(!strcmp(argv[1], "wicked")){
66     rv = runwicked(argc, argv);
67   } else {
68     usage();
69   }
70   return rv;
71 }
72 
73 
74 /* print the usage and exit */
usage(void)75 static void usage(void){
76   fprintf(stderr, "%s: test cases of the core API of Tokyo Dystopia\n", g_progname);
77   fprintf(stderr, "\n");
78   fprintf(stderr, "usage:\n");
79   fprintf(stderr, "  %s write [-tl] [-td|-tb|-tt] [-er num] [-et num] [-iu num] [-ic num] [-xnt]"
80           " [-nl|-nb] [-la num] [-en] path rnum\n", g_progname);
81   fprintf(stderr, "  %s read [-nl|-nb] [-la num] [-lm num] [-en] [-sp|-ss|-sf|-st|-stp|-sts]"
82           " path rnum\n", g_progname);
83   fprintf(stderr, "  %s wicked [-tl] [-td|-tb|-tt] [-er num] [-et num] [-iu num] [-ic num]"
84           " [-nl|-nb] [-la num] [-en] path rnum\n", g_progname);
85   fprintf(stderr, "\n");
86   exit(1);
87 }
88 
89 
90 /* print formatted information string and flush the buffer */
iprintf(const char * format,...)91 static void iprintf(const char *format, ...){
92   va_list ap;
93   va_start(ap, format);
94   vprintf(format, ap);
95   fflush(stdout);
96   va_end(ap);
97 }
98 
99 
100 /* print error message of hash database */
eprint(TCIDB * idb,const char * func)101 static void eprint(TCIDB *idb, const char *func){
102   const char *path = tcidbpath(idb);
103   int ecode = tcidbecode(idb);
104   fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
105           g_progname, path ? path : "-", func, ecode, tcidberrmsg(ecode));
106 }
107 
108 
109 /* print system information */
sysprint(void)110 static void sysprint(void){
111   TCMAP *info = tcsysinfo();
112   if(info){
113     tcmapiterinit(info);
114     const char *kbuf;
115     while((kbuf = tcmapiternext2(info)) != NULL){
116       iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf));
117     }
118     tcmapdel(info);
119   }
120 }
121 
122 
123 /* get a random number */
myrand(int range)124 static int myrand(int range){
125   return (int)((double)range * rand() / (RAND_MAX + 1.0));
126 }
127 
128 
129 /* callback function for sync progression */
mysynccb(int total,int current,const char * msg,void * opq)130 static bool mysynccb(int total, int current, const char *msg, void *opq){
131   if(total < 10 || current % (total / 10) == 0) printf("[sync:%d:%d:%s]\n", total, current, msg);
132   return true;
133 }
134 
135 
136 /* fill a record buffer */
setrndtext(char * buf,int avg,int min,bool en)137 static void setrndtext(char *buf, int avg, int min, bool en){
138   int len = myrand(avg * 2);
139   if(len < 3 || len > myrand(avg * 2) + 1) len = myrand(avg + 1);
140   if(len < 2) len = myrand(avg + 1);
141   len = tclmax(min, tclmin(len, TEXTBUFSIZ - 1));
142   if(en){
143     char *wp = buf;
144     if(len > 0){
145       if(myrand(4) == 0){
146         *(wp++) = 'A' + myrand('Z' - 'A' + 1);
147       } else {
148         *(wp++) = 'a' + myrand('z' - 'a' + 1);
149       }
150       len--;
151     }
152     for(int i = 0; i < len; i++){
153       if(myrand(8) == 0){
154         *(wp++) = ' ';
155       } else if(myrand(avg + 1) == 0){
156         if(myrand(2) == 0){
157           *(wp++) = 'A' + myrand('Z' - 'A' + 1);
158         } else {
159           *(wp++) = '0' + myrand('9' - '0' + 1);
160         }
161       } else {
162         *(wp++) = 'a' + myrand('z' - 'a' + 1);
163       }
164     }
165     *wp = '\0';
166   } else {
167     uint16_t ary[TEXTBUFSIZ];
168     len = tclmin(len, TEXTBUFSIZ / 4 - 1);
169     for(int i = 0; i < len; i++){
170       ary[i] = myrand(UINT16_MAX) / (myrand(UINT8_MAX) + 1);
171     }
172     tcstrucstoutf(ary, len, buf);
173   }
174 }
175 
176 
177 /* fill a record buffer corresponding to an ID number */
setidtext(char * buf,uint64_t id,bool en)178 static void setidtext(char *buf, uint64_t id, bool en){
179   unsigned int tid = (unsigned int)id;
180   id += rand_r(&tid);
181   int len = (((rand_r(&tid) ^ rand_r(&tid)) >> 4) & 0x1f) + 1;
182   if(en){
183     char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+ ";
184     char *wp = buf;
185     for(int i = 0; i < len; i++){
186       *(wp++) = tbl[id&0x3f];
187       id >>= 4;
188       if(id < 1) id = rand_r(&tid);
189     }
190     *wp = '\0';
191   } else {
192     uint16_t ary[TEXTBUFSIZ];
193     len = tclmin(len, TEXTBUFSIZ / 4 - 1);
194     for(int i = 0; i < len; i++){
195       ary[i] = (rand_r(&tid) >> 4) & UINT16_MAX;
196     }
197     tcstrucstoutf(ary, len, buf);
198   }
199 }
200 
201 
202 /* parse arguments of write command */
runwrite(int argc,char ** argv)203 static int runwrite(int argc, char **argv){
204   char *path = NULL;
205   char *rstr = NULL;
206   int opts = 0;
207   int64_t ernum = 0;
208   int64_t etnum = 0;
209   int64_t iusiz = 0;
210   int64_t icsiz = 0;
211   int exopts = 0;
212   int omode = 0;
213   int alen = DEFAVGLEN;
214   bool en = false;
215   for(int i = 2; i < argc; i++){
216     if(!path && argv[i][0] == '-'){
217       if(!strcmp(argv[i], "-tl")){
218         opts |= IDBTLARGE;
219       } else if(!strcmp(argv[i], "-td")){
220         opts |= IDBTDEFLATE;
221       } else if(!strcmp(argv[i], "-tb")){
222         opts |= IDBTBZIP;
223       } else if(!strcmp(argv[i], "-tt")){
224         opts |= IDBTTCBS;
225       } else if(!strcmp(argv[i], "-er")){
226         if(++i >= argc) usage();
227         ernum = tcatoix(argv[i]);
228       } else if(!strcmp(argv[i], "-et")){
229         if(++i >= argc) usage();
230         etnum = tcatoix(argv[i]);
231       } else if(!strcmp(argv[i], "-iu")){
232         if(++i >= argc) usage();
233         iusiz = tcatoix(argv[i]);
234       } else if(!strcmp(argv[i], "-ic")){
235         if(++i >= argc) usage();
236         icsiz = tcatoix(argv[i]);
237       } else if(!strcmp(argv[i], "-xnt")){
238         exopts |= IDBXNOTXT;
239       } else if(!strcmp(argv[i], "-nl")){
240         omode |= IDBONOLCK;
241       } else if(!strcmp(argv[i], "-nb")){
242         omode |= IDBOLCKNB;
243       } else if(!strcmp(argv[i], "-la")){
244         if(++i >= argc) usage();
245         alen = tcatoi(argv[i]);
246       } else if(!strcmp(argv[i], "-en")){
247         en = true;
248       } else {
249         usage();
250       }
251     } else if(!path){
252       path = argv[i];
253     } else if(!rstr){
254       rstr = argv[i];
255     } else {
256       usage();
257     }
258   }
259   if(!path || !rstr) usage();
260   int rnum = tcatoi(rstr);
261   if(rnum < 1 || alen < 1) usage();
262   int rv = procwrite(path, rnum, opts, ernum, etnum, iusiz, icsiz, exopts, omode, alen, en);
263   return rv;
264 }
265 
266 
267 /* parse arguments of read command */
runread(int argc,char ** argv)268 static int runread(int argc, char **argv){
269   char *path = NULL;
270   char *rstr = NULL;
271   int omode = 0;
272   int alen = DEFAVGLEN;
273   int mlen = 1;
274   bool en = false;
275   int smode = IDBSSUBSTR;
276   for(int i = 2; i < argc; i++){
277     if(!path && argv[i][0] == '-'){
278       if(!strcmp(argv[i], "-nl")){
279         omode |= IDBONOLCK;
280       } else if(!strcmp(argv[i], "-nb")){
281         omode |= IDBOLCKNB;
282       } else if(!strcmp(argv[i], "-la")){
283         if(++i >= argc) usage();
284         alen = tcatoi(argv[i]);
285       } else if(!strcmp(argv[i], "-lm")){
286         if(++i >= argc) usage();
287         mlen = tcatoi(argv[i]);
288       } else if(!strcmp(argv[i], "-en")){
289         en = true;
290       } else if(!strcmp(argv[i], "-sp")){
291         smode = IDBSPREFIX;
292       } else if(!strcmp(argv[i], "-ss")){
293         smode = IDBSSUFFIX;
294       } else if(!strcmp(argv[i], "-sf")){
295         smode = IDBSFULL;
296       } else if(!strcmp(argv[i], "-st")){
297         smode = IDBSTOKEN;
298       } else if(!strcmp(argv[i], "-stp")){
299         smode = IDBSTOKPRE;
300       } else if(!strcmp(argv[i], "-sts")){
301         smode = IDBSTOKSUF;
302       } else {
303         usage();
304       }
305     } else if(!path){
306       path = argv[i];
307     } else if(!rstr){
308       rstr = argv[i];
309     } else {
310       usage();
311     }
312   }
313   if(!path || !rstr) usage();
314   int rnum = tcatoi(rstr);
315   if(rnum < 1 || alen < 1 || mlen < 0) usage();
316   int rv = procread(path, rnum, omode, alen, mlen, en, smode);
317   return rv;
318 }
319 
320 
321 /* parse arguments of wicked command */
runwicked(int argc,char ** argv)322 static int runwicked(int argc, char **argv){
323   char *path = NULL;
324   char *rstr = NULL;
325   int opts = 0;
326   int64_t ernum = 0;
327   int64_t etnum = 0;
328   int64_t iusiz = 0;
329   int64_t icsiz = 0;
330   int omode = 0;
331   int alen = DEFAVGLEN;
332   bool en = false;
333   for(int i = 2; i < argc; i++){
334     if(!path && argv[i][0] == '-'){
335       if(!strcmp(argv[i], "-tl")){
336         opts |= IDBTLARGE;
337       } else if(!strcmp(argv[i], "-td")){
338         opts |= IDBTDEFLATE;
339       } else if(!strcmp(argv[i], "-tb")){
340         opts |= IDBTBZIP;
341       } else if(!strcmp(argv[i], "-tt")){
342         opts |= IDBTTCBS;
343       } else if(!strcmp(argv[i], "-er")){
344         if(++i >= argc) usage();
345         ernum = tcatoix(argv[i]);
346       } else if(!strcmp(argv[i], "-et")){
347         if(++i >= argc) usage();
348         etnum = tcatoix(argv[i]);
349       } else if(!strcmp(argv[i], "-iu")){
350         if(++i >= argc) usage();
351         iusiz = tcatoix(argv[i]);
352       } else if(!strcmp(argv[i], "-ic")){
353         if(++i >= argc) usage();
354         icsiz = tcatoix(argv[i]);
355       } else if(!strcmp(argv[i], "-nl")){
356         omode |= IDBONOLCK;
357       } else if(!strcmp(argv[i], "-nb")){
358         omode |= IDBOLCKNB;
359       } else if(!strcmp(argv[i], "-la")){
360         if(++i >= argc) usage();
361         alen = tcatoi(argv[i]);
362       } else if(!strcmp(argv[i], "-en")){
363         en = true;
364       } else {
365         usage();
366       }
367     } else if(!path){
368       path = argv[i];
369     } else if(!rstr){
370       rstr = argv[i];
371     } else {
372       usage();
373     }
374   }
375   if(!path || !rstr) usage();
376   int rnum = tcatoi(rstr);
377   if(rnum < 1 || alen < 1) usage();
378   int rv = procwicked(path, rnum, opts, ernum, etnum, iusiz, icsiz, omode, alen, en);
379   return rv;
380 }
381 
382 
383 /* perform write command */
procwrite(const char * path,int rnum,int opts,int64_t ernum,int64_t etnum,int64_t iusiz,int64_t icsiz,int exopts,int omode,int alen,bool en)384 static int procwrite(const char *path, int rnum, int opts,
385                      int64_t ernum, int64_t etnum, int64_t iusiz, int64_t icsiz, int exopts,
386                      int omode, int alen, bool en){
387   iprintf("<Writing Test>\n  path=%s  rnum=%d  opts=%d  ernum=%lld  etnum=%lld  iusiz=%lld"
388           "  icsiz=%lld  exopts=%d  omode=%d  alen=%d  en=%d\n\n", path, rnum, opts,
389           (long long)ernum, (long long)etnum, (long long)iusiz, (long long)icsiz,
390           exopts, omode, alen, en);
391   bool err = false;
392   double stime = tctime();
393   TCIDB *idb = tcidbnew();
394   if(g_dbgfd >= 0) tcidbsetdbgfd(idb, g_dbgfd);
395   tcidbsetsynccb(idb, mysynccb, NULL);
396   tcidbsetexopts(idb, exopts);
397   if(!tcidbsetcache(idb, icsiz, -1)){
398     eprint(idb, "tcidbsetcache");
399     err = true;
400   }
401   if(!tcidbtune(idb, ernum, etnum, iusiz, opts)){
402     eprint(idb, "tcidbtune");
403     err = true;
404   }
405   if(!tcidbopen(idb, path, IDBOWRITER | IDBOCREAT | IDBOTRUNC | omode)){
406     eprint(idb, "tcidbopen");
407     err = true;
408   }
409   for(int i = 1; i <= rnum; i++){
410     char text[TEXTBUFSIZ];
411     setrndtext(text, alen, 1, en);
412     if(!tcidbput(idb, i, text)){
413       eprint(idb, "tcidbput");
414       err = true;
415       break;
416     }
417     if(rnum > 250 && i % (rnum / 250) == 0){
418       putchar('.');
419       fflush(stdout);
420       if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
421     }
422   }
423   iprintf("record number: %llu\n", (unsigned long long)tcidbrnum(idb));
424   iprintf("size: %llu\n", (unsigned long long)tcidbfsiz(idb));
425   sysprint();
426   if(!tcidbclose(idb)){
427     eprint(idb, "tcidbclose");
428     err = true;
429   }
430   tcidbdel(idb);
431   iprintf("time: %.3f\n", tctime() - stime);
432   iprintf("%s\n\n", err ? "error" : "ok");
433   return err ? 1 : 0;
434 }
435 
436 
437 /* perform read command */
procread(const char * path,int rnum,int omode,int alen,int mlen,bool en,int smode)438 static int procread(const char *path, int rnum, int omode, int alen, int mlen, bool en,
439                     int smode){
440   iprintf("<Reading Test>\n  path=%s  rnum=%d  omode=%d  alen=%d  mlen=%d  en=%d"
441           "  smode=%d\n\n", path, rnum, omode, alen, mlen, en, smode);
442   bool err = false;
443   double stime = tctime();
444   TCIDB *idb = tcidbnew();
445   if(g_dbgfd >= 0) tcidbsetdbgfd(idb, g_dbgfd);
446   if(!tcidbopen(idb, path, IDBOREADER | omode)){
447     eprint(idb, "tcidbopen");
448     err = true;
449   }
450   for(int i = 1; i <= rnum; i++){
451     char text[TEXTBUFSIZ];
452     setrndtext(text, alen, mlen, en);
453     int num;
454     uint64_t *res = tcidbsearch(idb, text, smode, &num);
455     if(res){
456       tcfree(res);
457     } else {
458       eprint(idb, "tcidbsearch");
459       err = true;
460       break;
461     }
462     if(rnum > 250 && i % (rnum / 250) == 0){
463       putchar('.');
464       fflush(stdout);
465       if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
466     }
467   }
468   iprintf("record number: %llu\n", (unsigned long long)tcidbrnum(idb));
469   iprintf("size: %llu\n", (unsigned long long)tcidbfsiz(idb));
470   sysprint();
471   if(!tcidbclose(idb)){
472     eprint(idb, "tcidbclose");
473     err = true;
474   }
475   tcidbdel(idb);
476   iprintf("time: %.3f\n", tctime() - stime);
477   iprintf("%s\n\n", err ? "error" : "ok");
478   return err ? 1 : 0;
479 }
480 
481 
482 /* perform wicked command */
procwicked(const char * path,int rnum,int opts,int64_t ernum,int64_t etnum,int64_t iusiz,int64_t icsiz,int omode,int alen,bool en)483 static int procwicked(const char *path, int rnum, int opts,
484                       int64_t ernum, int64_t etnum, int64_t iusiz, int64_t icsiz,
485                       int omode, int alen, bool en){
486   iprintf("<Wicked Writing Test>\n  path=%s  rnum=%d  opts=%d  ernum=%lld  etnum=%lld"
487           "  iusiz=%lld  icsiz=%lld  omode=%d  alen=%d  en=%d\n\n", path, rnum, opts,
488           (long long)ernum, (long long)etnum, (long long)iusiz, (long long)icsiz,
489           omode, alen, en);
490   bool err = false;
491   double stime = tctime();
492   TCIDB *idb = tcidbnew();
493   if(g_dbgfd >= 0) tcidbsetdbgfd(idb, g_dbgfd);
494   if(!tcidbtune(idb, ernum, etnum, iusiz, opts)){
495     eprint(idb, "tcidbtune");
496     err = true;
497   }
498   if(!tcidbsetcache(idb, icsiz, -1)){
499     eprint(idb, "tcidbsetcache");
500     err = true;
501   }
502   if(!tcidbopen(idb, path, IDBOWRITER | IDBOCREAT | IDBOTRUNC | omode)){
503     eprint(idb, "tcidbopen");
504     err = true;
505   }
506   int rnd = 0;
507   for(int i = 1; i <= rnum && !err; i++){
508     char text[TEXTBUFSIZ];
509     setidtext(text, i, en);
510     if(myrand(5) == 0) rnd = myrand(100);
511     if(rnd < 90){
512       putchar('P');
513       if(!tcidbput(idb, i, text)){
514         eprint(idb, "tcidbput");
515         err = true;
516       }
517     } else if(rnd < 95){
518       putchar('O');
519       if(!tcidbout(idb, i) && tcidbecode(idb) != TCENOREC){
520         eprint(idb, "tcidbout");
521         err = true;
522       }
523     } else {
524       putchar('S');
525       int num;
526       uint64_t *res = tcidbsearch(idb, text, IDBSSUBSTR, &num);
527       if(res){
528         tcfree(res);
529       } else {
530         eprint(idb, "tcidbsearch");
531         err = true;
532       }
533     }
534     if(i % 50 == 0) iprintf(" (%08d)\n", i);
535   }
536   if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
537   iprintf("record number: %llu\n", (unsigned long long)tcidbrnum(idb));
538   iprintf("size: %llu\n", (unsigned long long)tcidbfsiz(idb));
539   sysprint();
540   if(!tcidbclose(idb)){
541     eprint(idb, "tcidbclose");
542     err = true;
543   }
544   tcidbdel(idb);
545   iprintf("time: %.3f\n", tctime() - stime);
546   iprintf("%s\n\n", err ? "error" : "ok");
547   return err ? 1 : 0;
548 }
549 
550 
551 
552 // END OF FILE
553