Update copyright year + FSF address
[idzebra-moved-to-github.git] / isamb / tstisamb.c
1 /* $Id: tstisamb.c,v 1.24 2006-08-14 10:40:17 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <yaz/log.h>
26 #include <yaz/xmalloc.h>
27 #include <idzebra/isamb.h>
28 #include <assert.h>
29
30 static void log_item(int level, const void *b, const char *txt)
31 {
32     int x;
33     memcpy(&x, b, sizeof(int));
34     yaz_log(YLOG_DEBUG, "%s %d", txt, x);
35 }
36
37 static void log_pr(const char *txt)
38 {
39     yaz_log(YLOG_DEBUG, "%s", txt);
40 }
41
42 int compare_item(const void *a, const void *b)
43 {
44     int ia, ib;
45
46     memcpy(&ia, a, sizeof(int));
47     memcpy(&ib, b, sizeof(int));
48     if (ia > ib)
49         return 1;
50     if (ia < ib)
51         return -1;
52    return 0;
53 }
54
55 void *code_start()
56 {
57     return 0;
58 }
59
60 void code_item(void *p, char **dst, const char **src)
61 {
62     memcpy (*dst, *src, sizeof(int));
63     (*dst) += sizeof(int);
64     (*src) += sizeof(int);
65 }
66
67 void code_reset(void *p)
68 {
69 }
70 void code_stop(void *p)
71 {
72 }
73
74 struct read_info {
75     int val;
76     int step;
77
78     int no;
79     int max;
80     int insertMode;
81 };
82
83 int code_read(void *vp, char **dst, int *insertMode)
84 {
85     struct read_info *ri = (struct read_info *)vp;
86     int x;
87
88     if (ri->no >= ri->max)
89         return 0;
90     ri->no++;
91
92     x = ri->val;
93     memcpy (*dst, &x, sizeof(int));
94     (*dst)+=sizeof(int);
95
96     ri->val = ri->val + ri->step;
97     *insertMode = ri->insertMode;
98
99 #if 1
100     yaz_log(YLOG_DEBUG, "%d %5d", ri->insertMode, x);
101 #endif
102     return 1;
103 }
104
105 void tst_insert(ISAMB isb, int n)
106 {
107     ISAMC_I isamc_i;
108     ISAM_P isamc_p;
109     struct read_info ri;
110     ISAMB_PP pp;
111     char key_buf[20];
112     int nerrs = 0;
113
114     /* insert a number of entries */
115     ri.no = 0;
116     ri.max = n;
117
118     ri.val = 0;
119     ri.step = 1;
120     ri.insertMode = 1;
121
122     isamc_i.clientData = &ri;
123     isamc_i.read_item = code_read;
124     
125     isamc_p = 0; /* new list */
126     isamb_merge (isb, &isamc_p , &isamc_i);
127
128     /* read the entries */
129     pp = isamb_pp_open (isb, isamc_p, 1);
130
131     ri.val = 0;
132     while(isamb_pp_read (pp, key_buf))
133     {
134         int x;
135         memcpy (&x, key_buf, sizeof(int));
136         if (x != ri.val)
137         {
138             yaz_log(YLOG_WARN, "isamb_pp_read. n=%d Got %d (expected %d)",
139                     n, x, ri.val);
140             nerrs++;
141         }
142         else if (nerrs)
143             yaz_log(YLOG_LOG, "isamb_pp_read. n=%d Got %d",
144                     n, x);
145
146         ri.val++;
147     }
148     if (ri.val != ri.max)
149     {
150         yaz_log(YLOG_WARN, "ri.max != ri.max (%d != %d)", ri.val, ri.max);
151         nerrs++;
152     }
153     isamb_dump(isb, isamc_p, log_pr);
154     isamb_pp_close(pp);
155
156     if (nerrs)
157         exit(3);
158     /* delete a number of entries (even ones) */
159     ri.no = 0;
160     ri.max = n - n/2;
161
162     ri.val = 0;
163     ri.step = 2;
164     ri.insertMode = 0;
165
166     isamc_i.clientData = &ri;
167     isamc_i.read_item = code_read;
168     
169     isamb_merge (isb, &isamc_p , &isamc_i);
170
171     /* delete a number of entries (odd ones) */
172     ri.no = 0;
173     ri.max = n/2;
174
175     ri.val = 1;
176     ri.step = 2;
177     ri.insertMode = 0;
178
179     isamc_i.clientData = &ri;
180     isamc_i.read_item = code_read;
181     
182     isamb_merge (isb, &isamc_p, &isamc_i);
183
184     if (isamc_p)
185     {
186         yaz_log(YLOG_WARN, "isamb_merge did not return empty list n=%d",
187                 n);
188         exit(3);
189     }
190 }
191
192 void tst_forward(ISAMB isb, int n)
193 {
194     ISAMC_I isamc_i;
195     ISAM_P isamc_p;
196     struct read_info ri;
197     int i;
198     ISAMB_PP pp;
199
200     /* insert a number of entries */
201     ri.val = 0;
202     ri.max = n;
203
204     ri.no = 0;
205     ri.step = 1;
206     ri.insertMode = 1;
207
208     isamc_i.clientData = &ri;
209     isamc_i.read_item = code_read;
210     
211     isamc_p = 0;
212     isamb_merge (isb, &isamc_p, &isamc_i);
213
214     /* read the entries */
215     pp = isamb_pp_open (isb, isamc_p, 1);
216     
217     for (i = 0; i<ri.max; i +=2 )
218     {
219         int x = -1;
220         int xu = i;
221         isamb_pp_forward(pp, &x, &xu);
222         if (x != xu && xu != x+1)
223         {
224             yaz_log(YLOG_WARN, "isamb_pp_forward (1). Got %d (expected %d)",
225                     x, xu);
226             exit(4);
227         }
228         ri.no++;
229     }
230     isamb_pp_close(pp);
231     
232     pp = isamb_pp_open (isb, isamc_p, 1);
233     for (i = 0; i<ri.max; i += 100)
234     {
235         int x = -1;
236         int xu = i;
237         isamb_pp_forward(pp, &x, &xu);
238         if (x != xu && xu != x+1)
239         {
240             yaz_log(YLOG_WARN, "isamb_pp_forward (2). Got %d (expected %d)",
241                     x, xu);
242             exit(4);
243         }
244         ri.no++;
245     }
246     isamb_pp_close(pp);
247
248     isamb_unlink(isb, isamc_p);
249 }
250
251 void tst_x(ISAMB isb)
252 {
253     ISAMC_I isamc_i;
254     ISAM_P isamb_p = 0;
255     struct read_info ri;
256
257     isamc_i.clientData = &ri;
258     isamc_i.read_item = code_read;
259     ri.no = 0;
260     ri.max = 500;
261
262     ri.val = 1000;
263     ri.step = 1;
264     ri.insertMode = 1;
265
266     isamb_merge (isb, &isamb_p , &isamc_i);
267
268     ri.no = 0;
269     ri.max = 500;
270
271     ri.val = 1;
272     ri.step = 1;
273     ri.insertMode = 1;
274
275     isamb_merge (isb, &isamb_p , &isamc_i);
276 }
277
278 void tst_append(ISAMB isb, int n)
279 {
280     ISAMC_I isamc_i;
281     ISAM_P isamb_p = 0;
282     struct read_info ri;
283     int i;
284     int chunk = 10;
285
286     for (i = 0; i < n; i += chunk)
287     {
288         /* insert a number of entries */
289         ri.no = 0;
290         ri.max = i + chunk;
291
292         ri.val = 0;
293         ri.step = 1;
294         ri.insertMode = 1;
295         
296         isamc_i.clientData = &ri;
297         isamc_i.read_item = code_read;
298         
299         isamb_merge (isb, &isamb_p , &isamc_i);
300     }
301 }
302
303
304 struct random_read_info {
305     int max;
306     int idx;
307     int level;
308     int *delta;
309 };
310
311 int tst_random_read(void *vp, char **dst, int *insertMode)
312 {
313     struct random_read_info *ri = (struct random_read_info *)vp;
314     int x;
315
316     while(ri->idx < ri->max && ri->delta[ri->idx] == ri->level)
317     {
318         ri->idx++;
319         ri->level = 0;
320     }
321     if (ri->idx >= ri->max)
322         return 0;
323     
324     if (ri->delta[ri->idx] > 0)
325     {
326         ri->level++;
327         *insertMode = 1;
328     }
329     else
330     {
331         ri->level--;
332         *insertMode = 0;
333     }
334     x = ri->idx;
335     memcpy (*dst, &x, sizeof(int));
336     (*dst)+=sizeof(int);
337
338     yaz_log(YLOG_DEBUG, "%d %5d", *insertMode, x);
339     return 1;
340 }
341
342 void tst_random(ISAMB isb, int n, int rounds, int max_dups)
343 {
344     ISAM_P isamb_p = 0;
345
346     int *freq = malloc(sizeof(int) * n);
347     int *delta = malloc(sizeof(int) * n);
348     int i, j;
349     for (i = 0; i<n; i++)
350         freq[i] = 0;
351     
352     for (j = 0; j<rounds; j++)
353     {
354         yaz_log(YLOG_DEBUG, "round %d", j);
355         for (i = 0; i<n; i++)
356         {
357             if (rand() & 1)
358                 delta[i] = (rand() % (1+max_dups)) - freq[i];
359             else
360                 delta[i] = 0;
361         }
362         if (n)
363         {
364             ISAMC_I isamc_i;
365             struct random_read_info ri;
366             
367             ri.delta = delta;
368             ri.idx = 0;
369             ri.max = n;
370             ri.level = 0;
371             
372             isamc_i.clientData = &ri;
373             isamc_i.read_item = tst_random_read;
374
375             isamb_merge (isb, &isamb_p , &isamc_i);
376         }
377         
378         yaz_log(YLOG_DEBUG, "dump %d", j);
379         isamb_dump(isb, isamb_p, log_pr);
380
381         yaz_log(YLOG_DEBUG, "----------------------------");
382         for (i = 0; i<n; i++)
383             freq[i] += delta[i];
384
385         if (!isamb_p)
386         {
387             for (i = 0; i<n; i++)
388                 if (freq[i])
389                 {
390                     yaz_log(YLOG_WARN, "isamb_merge returned 0, but "
391                             "freq is non-empty");
392                     exit(1);
393                 }
394         }
395         else
396         {
397             int level = 0;
398             int idx = 0;
399             char key_buf[20];
400             ISAMB_PP pp = isamb_pp_open (isb, isamb_p, 1);
401
402             yaz_log(YLOG_DEBUG, "test %d", j);
403
404             while(isamb_pp_read (pp, key_buf))
405             {
406                 int x;
407                 memcpy (&x, key_buf, sizeof(int));
408                 yaz_log(YLOG_DEBUG, "Got %d", x);
409                 while (idx < n && freq[idx] == level)
410                 {
411                     idx++;
412                     level = 0;
413                 }
414                 if (idx == n)
415                 {
416                     yaz_log(YLOG_WARN, "tst_random: Extra item: %d", x);
417                     exit(1);
418                 }
419                 if (idx != x)
420                 {
421                     yaz_log(YLOG_WARN, "tst_random: Mismatch %d != %d",
422                             x, idx);
423                     exit(1);
424                 }
425                 level++;
426             }
427             while (idx < n && freq[idx] == level)
428             {
429                 idx++;
430                 level = 0;
431             }
432             if (idx != n)
433             {
434                 yaz_log(YLOG_WARN, "tst_random: Missing item: %d", idx);
435                 exit(1);
436             }
437             isamb_pp_close(pp);
438         }
439     }
440     free(freq);
441     free(delta);
442 }
443
444 /* \fn void tst_minsert(ISAMB isb, int n)
445    \brief insert inserts n identical keys, removes n/2, then n-n/2 ..
446    \param isb ISAMB handle
447    \param n number of keys
448 */
449 void tst_minsert(ISAMB isb, int n)
450 {
451     ISAMC_I isamc_i;
452     ISAM_P isamb_p = 0;
453     struct read_info ri;
454
455     isamc_i.clientData = &ri;
456
457     /* all have same value = 1 */
458     ri.val = 1;  
459     ri.step = 0;
460
461     isamc_i.read_item = code_read;
462
463     ri.no = 0;
464     ri.max = n;
465
466     ri.insertMode = 1;
467
468     isamb_merge (isb, &isamb_p , &isamc_i);
469
470     isamb_dump(isb, isamb_p, log_pr);
471     
472     ri.no = 0;
473     ri.max = n - n/2;
474
475     ri.insertMode = 0;
476
477     isamb_merge (isb, &isamb_p , &isamc_i);
478
479     ri.no = 0;
480     ri.max = n/2;
481
482     ri.insertMode = 0;
483
484     isamb_merge (isb, &isamb_p , &isamc_i);
485     if (isamb_p)
486     {
487         yaz_log(YLOG_WARN, "tst_minsert: isamb_merge should be empty n=%d",
488                 n);
489         exit(1);
490     }
491 }
492
493 /* tests for identical keys.. ISAMB does not handle that, so some of the
494    tests below fails
495 */
496 static void identical_keys_tests(ISAMB isb)
497 {
498 #if 1
499     tst_minsert(isb, 10);
500 #endif
501 #if 0
502     tst_minsert(isb, 600);  /* still fails */
503 #endif
504 #if 1
505     tst_random(isb, 20, 200, 1);
506 #endif
507 #if 1
508     tst_random(isb, 5, 200, 2);
509 #endif
510
511 #if 1
512     tst_random(isb, 250, 10, 4);
513 #endif
514 #if 1
515     /* fails if both are executed */
516     tst_random(isb, 20000, 10, 4);
517     tst_random(isb, 20000, 10, 10);
518 #endif
519 #if 1
520     tst_random(isb, 250, 100, 10);
521 #endif
522 }
523
524 int main(int argc, char **argv)
525 {
526     BFiles bfs;
527     ISAMB isb;
528     ISAMC_M method;
529     
530     if (argc == 2)
531         yaz_log_init_level(YLOG_ALL);
532         
533     /* setup method (attributes) */
534     method.compare_item = compare_item;
535     method.log_item = log_item;
536     method.codec.start = code_start;
537     method.codec.encode = code_item;
538     method.codec.decode = code_item;
539     method.codec.reset = code_reset;
540     method.codec.stop = code_stop;
541
542     /* create block system */
543     bfs = bfs_create(0, 0);
544     if (!bfs)
545     {
546         yaz_log(YLOG_WARN, "bfs_create failed");
547         exit(1);
548     }
549
550     bf_reset(bfs);
551
552     /* create isam handle */
553     isb = isamb_open (bfs, "isamb", 1, &method, 0);
554     if (!isb)
555     {
556         yaz_log(YLOG_WARN, "isamb_open failed");
557         exit(2);
558     }
559 #if 1
560     tst_insert(isb, 1);
561     tst_insert(isb, 2);
562     tst_insert(isb, 20);
563     tst_insert(isb, 100);
564     tst_insert(isb, 500);
565     tst_insert(isb, 10000);
566
567     tst_forward(isb, 10000);
568
569     tst_x(isb);
570
571     tst_append(isb, 1000);
572 #endif
573
574     if (0)
575         identical_keys_tests(isb);
576     
577     isamb_close(isb);
578
579     /* exit block system */
580     bfs_destroy(bfs);
581     exit(0);
582     return 0;
583 }
584 /*
585  * Local variables:
586  * c-basic-offset: 4
587  * indent-tabs-mode: nil
588  * End:
589  * vim: shiftwidth=4 tabstop=8 expandtab
590  */
591