Happy new year.
[idzebra-moved-to-github.git] / isamb / benchindex1.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1994-2011 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #include <yaz/options.h>
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <yaz/log.h>
25 #include <yaz/nmem.h>
26 #include <yaz/xmalloc.h>
27 #include <yaz/marcdisp.h>
28 #include <yaz/timing.h>
29 #include <it_key.h>
30 #include <idzebra/isamb.h>
31 #include <idzebra/dict.h>
32 #include <assert.h>
33
34 struct index_block {
35     NMEM nmem;
36     int no_entries;
37     size_t current_entry;
38     size_t current_max;
39     struct index_term *terms;
40     struct index_term **ar;
41     int round;
42 };
43
44 struct index_term {
45     const char *term;
46     zint docid;
47     zint seqno;
48     int word_id;
49     struct index_term *next;
50 };
51
52 struct index_block *index_block_new(int memory)
53 {
54     struct index_block *b = xmalloc(sizeof(*b));
55     b->no_entries = 0;
56     b->current_max = memory * 1024 * 1024;
57     b->terms = 0;
58     b->nmem = nmem_create();
59     b->round = 0;
60     return b;
61 }
62
63 void index_block_destroy(struct index_block **bp)
64 {
65     if (*bp)
66     {
67         nmem_destroy((*bp)->nmem);
68         xfree(*bp);
69         *bp = 0;
70     }
71 }
72
73 static int cmp_ar(const void *p1, const void *p2)
74 {
75     struct index_term *t1 = *(struct index_term **) p1;
76     struct index_term *t2 = *(struct index_term **) p2;
77     int d = strcmp(t1->term, t2->term);
78     if (d)
79         return d;
80
81     if (t1->docid > t2->docid)
82         return 1;
83     else if (t1->docid < t2->docid)
84         return -1;
85     if (t1->seqno > t2->seqno)
86         return 1;
87     else if (t1->seqno < t2->seqno)
88         return -1;
89     return 0;
90 }
91
92
93 int code_read(void *vp, char **dst, int *insertMode)
94 {
95     struct index_block *b = (struct index_block *)vp;
96     struct index_term *t;
97     struct it_key key;
98
99     if (b->current_entry >= b->no_entries)
100         return 0;
101     
102     t = b->ar[b->current_entry];
103     b->current_entry++;
104     
105     key.len = 3;
106     key.mem[0] = t->word_id;
107     key.mem[1] = t->docid;
108     key.mem[2] = t->seqno;
109     key.mem[3] = 0;
110
111     memcpy(*dst, &key, sizeof(key));
112
113     (*dst) += sizeof(key);
114     *insertMode = 1;
115 #if 0
116     yaz_log(YLOG_LOG, "returning " ZINT_FORMAT " " ZINT_FORMAT "\n",
117             key.mem[0], key.mem[1]);
118 #endif
119     return 1;
120 }
121
122 void index_block_flush(struct index_block *b, ISAMB isb, Dict dict,
123                        int no_docs)
124 {
125     struct index_term *t = b->terms;
126     int i;
127     int word_id_seq = 0;
128     int no_words = 0, no_new_words = 0;
129     const char *dict_info = 0;
130     ISAM_P isamc_p = 0;
131     yaz_timing_t tim_dict = 0;
132     yaz_timing_t tim_isamb = 0;
133     zint number_of_int_splits = isamb_get_int_splits(isb);
134     zint number_of_leaf_splits = isamb_get_leaf_splits(isb);
135     zint number_of_dict_splits = dict_get_no_split(dict);
136     
137     b->ar = xmalloc(sizeof(*b->ar) * b->no_entries);
138     for (i = 0; i < b->no_entries; i++, t = t->next)
139     {
140         assert(t);
141         b->ar[i] = t;
142     }
143     assert(!t);
144     
145     qsort(b->ar, b->no_entries, sizeof(*b->ar), cmp_ar);
146     tim_dict = yaz_timing_create();
147 #if 0
148     for (i = 0; i < b->no_entries; i++)
149     {
150         printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n",
151                ar[i]->term, ar[i]->docid, ar[i]->seqno);
152     }
153 #endif
154     dict_info = dict_lookup(dict, "_w");
155     if (dict_info)
156     {
157         assert(*dict_info == sizeof(word_id_seq));
158         memcpy(&word_id_seq, dict_info+1, sizeof(word_id_seq));
159     }
160
161     dict_info = dict_lookup(dict, "_i");
162     if (dict_info)
163     {
164         assert(*dict_info == sizeof(isamc_p));
165         memcpy(&isamc_p, dict_info+1, sizeof(isamc_p));
166     }
167
168     for (i = 0; i < b->no_entries; i++)
169     {
170         if (i > 0 && strcmp(b->ar[i-1]->term, b->ar[i]->term) == 0)
171             b->ar[i]->word_id = b->ar[i-1]->word_id;
172         else
173         {
174             const char *dict_info = dict_lookup(dict, b->ar[i]->term);
175             if (dict_info)
176             {
177                 memcpy(&b->ar[i]->word_id, dict_info+1, sizeof(int));
178             }
179             else
180             {
181                 word_id_seq++;
182                 no_new_words++;
183                 dict_insert(dict, b->ar[i]->term, sizeof(int), &word_id_seq);
184                 b->ar[i]->word_id = word_id_seq;
185             }
186             no_words++;
187         }
188     }
189     dict_insert(dict, "_w", sizeof(word_id_seq), &word_id_seq);
190     
191     yaz_timing_stop(tim_dict);
192     tim_isamb = yaz_timing_create();
193
194     b->current_entry = 0;
195
196     if (b->no_entries)
197     {
198         ISAMC_I isamc_i;
199         
200         isamc_i.clientData = b;
201         isamc_i.read_item = code_read;
202
203         isamb_merge (isb, &isamc_p, &isamc_i);
204
205         assert(isamc_p);
206         dict_insert(dict, "_i", sizeof(isamc_p), &isamc_p);
207     }
208
209     yaz_timing_stop(tim_isamb);
210
211     number_of_int_splits = isamb_get_int_splits(isb) - number_of_int_splits;
212     number_of_leaf_splits = isamb_get_leaf_splits(isb) - number_of_leaf_splits;
213     number_of_dict_splits = dict_get_no_split(dict) - number_of_dict_splits;
214
215     if (b->round == 0)
216     {
217         printf("# run     total dict-real  user   sys isam-real  user   sys "
218                " intsp leafsp     docs postings  words    new d-spl\n");
219     }
220     b->round++;
221     printf("%5d %9.6f %9.6f %5.2f %5.2f %9.6f %5.2f %5.2f "
222            "%6" ZINT_FORMAT0 " %6" ZINT_FORMAT0 
223            " %8d %8d %6d %6d" " %5" ZINT_FORMAT0 "\n",
224            b->round,
225            yaz_timing_get_real(tim_dict) + yaz_timing_get_real(tim_isamb),
226            yaz_timing_get_real(tim_dict),
227            yaz_timing_get_user(tim_dict),
228            yaz_timing_get_sys(tim_dict),
229            yaz_timing_get_real(tim_isamb),
230            yaz_timing_get_user(tim_isamb),
231            yaz_timing_get_sys(tim_isamb),
232            number_of_int_splits,
233            number_of_leaf_splits,
234            no_docs,
235            b->no_entries,
236            no_words,
237            no_new_words,
238            number_of_dict_splits
239         );
240     fflush(stdout);
241
242     xfree(b->ar);
243     b->ar = 0;
244     nmem_reset(b->nmem);
245     b->no_entries = 0;
246     b->terms = 0;
247
248     yaz_timing_destroy(&tim_isamb);
249     yaz_timing_destroy(&tim_dict);
250 }
251
252 void index_block_check_flush(struct index_block *b, ISAMB isb, Dict dict,
253                              int no_docs)
254 {
255     int total = nmem_total(b->nmem);
256     int max = b->current_max;
257     if (total > max)
258     {
259         index_block_flush(b, isb, dict, no_docs);
260     }
261 }
262
263 void index_block_add(struct index_block *b,
264                      const char *term, zint docid, zint seqno)
265 {
266     struct index_term *t = nmem_malloc(b->nmem, sizeof(*t));
267     t->term = nmem_strdup(b->nmem, term);
268     t->docid = docid;
269     t->seqno = seqno;
270     t->next = b->terms;
271     b->terms = t;
272     b->no_entries++;
273 }
274
275 void index_term(struct index_block *b, const char *term,
276                 zint docid, zint *seqno)
277 {
278 #if 0
279     printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n", term,
280            docid, *seqno);
281 #endif
282     index_block_add(b, term, docid, *seqno);
283     (*seqno)++;
284 }
285
286 void index_wrbuf(struct index_block *b, WRBUF wrbuf, zint docid,
287                  int subfield_char)
288 {
289     int nl = 1;
290     const char *cp = wrbuf_buf(wrbuf);
291     char term[4096];
292     size_t sz = 0;
293     zint seqno = 0;
294
295     while (*cp)
296     {
297         if (nl)
298         {
299             int i;
300             if (cp[0] != ' ')
301             {   /* skip field+indicator (e.g. 245 00) */
302                 for (i = 0; i<6 && *cp; i++, cp++)
303                     ;
304             }
305             else
306             {  /* continuation line */
307                 for (i = 0; i<4 && *cp; i++, cp++)
308                     ;
309             }   
310         }
311         nl = 0;
312         if (*cp == '\n')
313         {
314             if (sz)
315             {
316                 index_term(b, term, docid, &seqno);
317                 sz = 0;
318             }
319             nl = 1;
320             cp++;
321         }
322         else if (*cp == subfield_char && cp[1])
323         {
324             if (sz)
325             {
326                 index_term(b, term, docid, &seqno);
327                 sz = 0;
328             }
329             cp += 2;
330         }
331         else if (strchr("$*/-;,.:[]\"&(){} ", *cp))
332         {
333             if (sz)
334             {
335                 index_term(b, term, docid, &seqno);
336                 sz = 0;
337             }
338             cp++;
339         }
340         else
341         {
342             unsigned ch = *(const unsigned char *)cp;
343             if (sz < sizeof(term))
344             {
345                 term[sz] = tolower(ch);
346                 term[sz+1] = '\0';
347                 sz++;
348             }
349             cp++;
350         }            
351     }
352     if (sz)
353         index_term(b, term, docid, &seqno);
354 }
355
356 void index_marc_line_records(ISAMB isb,
357                              Dict dict,
358                              zint *docid_seq,
359                              FILE *inf,
360                              int memory)
361 {
362     WRBUF wrbuf = wrbuf_alloc();
363     int no_docs = 0;
364     int new_rec = 1;
365     char line[4096];
366     struct index_block *b = index_block_new(memory);
367     while(fgets(line, sizeof(line)-1, inf))
368     {
369         if (line[0] == '$')
370         {
371             if (!new_rec)
372                 new_rec = 1;
373             else
374                 new_rec = 0;
375             continue;
376         }
377         if (new_rec)
378         {
379             (*docid_seq)++;
380             no_docs++;
381             index_block_check_flush(b, isb, dict, no_docs);
382             new_rec = 0;
383         }
384
385         if (line[0] == ' ')
386         {
387             /* continuation */
388             wrbuf_puts(wrbuf, line);
389             continue;
390         }
391         else
392         {
393             /* index existing buffer (if any) */
394             if (wrbuf_len(wrbuf))
395             {
396                 index_wrbuf(b, wrbuf, *docid_seq, '*');
397                 wrbuf_rewind(wrbuf);
398             }
399             if (line[0] != ' ' && line[1] != ' ' && line[2] != ' ' &&
400                 line[3] == ' ')
401             {
402                 /* normal field+indicator line */
403                 wrbuf_puts(wrbuf, line);
404             }
405         }
406     }
407     if (wrbuf_len(wrbuf))
408     {
409         index_wrbuf(b, wrbuf, *docid_seq, '*');
410         wrbuf_rewind(wrbuf);
411     }
412     (*docid_seq)++;
413     no_docs++;
414     index_block_flush(b, isb, dict, no_docs);
415     index_block_destroy(&b);
416 }
417
418 void index_marc_from_file(ISAMB isb,
419                           Dict dict,
420                           zint *docid_seq,
421                           FILE *inf,
422                           int memory,
423                           int verbose, int print_offset)
424 {
425     yaz_marc_t mt = yaz_marc_create();
426     WRBUF wrbuf = wrbuf_alloc();
427     struct index_block *b = index_block_new(memory);
428     int no_docs = 0;
429
430     while (1)
431     {
432         size_t r;
433         char buf[100001];
434         int len, rlen;
435
436         r = fread (buf, 1, 5, inf);
437         if (r < 5)
438         {
439             if (r && print_offset && verbose)
440                 printf ("<!-- Extra %ld bytes at end of file -->\n",
441                         (long) r);
442             break;
443         }
444         while (*buf < '0' || *buf > '9')
445         {
446             int i;
447             long off = ftell(inf) - 5;
448             if (verbose || print_offset)
449                 printf("<!-- Skipping bad byte %d (0x%02X) at offset "
450                        "%ld (0x%lx) -->\n", 
451                        *buf & 0xff, *buf & 0xff,
452                        off, off);
453             for (i = 0; i<4; i++)
454                 buf[i] = buf[i+1];
455             r = fread(buf+4, 1, 1, inf);
456             if (r < 1)
457                 break;
458         }
459         if (r < 1)
460         {
461             if (verbose || print_offset)
462                 printf ("<!-- End of file with data -->\n");
463             break;
464         }
465         len = atoi_n(buf, 5);
466         if (len < 25 || len > 100000)
467         {
468             long off = ftell(inf) - 5;
469             printf("Bad Length %ld read at offset %ld (%lx)\n",
470                    (long)len, (long) off, (long) off);
471             break;
472         }
473         rlen = len - 5;
474         r = fread (buf + 5, 1, rlen, inf);
475         if (r < rlen)
476             break;
477         yaz_marc_read_iso2709(mt, buf, len);
478         
479         if (yaz_marc_write_line(mt, wrbuf))
480             break;
481
482         index_wrbuf(b, wrbuf, *docid_seq, '$');
483         wrbuf_rewind(wrbuf);
484         (*docid_seq)++;
485
486         no_docs++;
487         index_block_check_flush(b, isb, dict, no_docs);
488     }
489     index_block_flush(b, isb, dict, no_docs);
490     wrbuf_destroy(wrbuf);
491     yaz_marc_destroy(mt);
492     index_block_destroy(&b);
493 }
494
495 void exit_usage(void)
496 {
497     fprintf(stderr, "benchindex1 [-t type] [-c d:i] [-m mem] [-i] [inputfile]\n");
498     exit(1);
499 }
500
501 int main(int argc, char **argv)
502 {
503     BFiles bfs;
504     ISAMB isb_postings;
505     ISAMC_M method_postings;
506     Dict dict;
507     int ret;
508     int reset = 0;
509     char *arg;
510     int memory = 5;
511     int isam_cache_size = 40;
512     int dict_cache_size = 50;
513     const char *fname = 0;
514     FILE *inf = stdin;
515     yaz_timing_t tim = 0;
516     zint docid_seq = 1;
517     const char *dict_info;
518     const char *type = "iso2709";
519     int int_count_enable = 1;
520
521     while ((ret = options("im:t:c:N", argv, argc, &arg)) != -2)
522     {
523         switch(ret)
524         {
525         case 'm':
526             memory = atoi(arg);
527             break;
528         case 'i':
529             reset = 1;
530             break;
531         case 't':
532             if (!strcmp(arg, "iso2709"))
533                 type = "iso2709";
534             else if (!strcmp(arg, "line"))
535                 type = "line";
536             else
537             {
538                 fprintf(stderr, "bad type: %s.\n", arg);
539                 exit_usage();
540             }
541             break;
542         case 'c':
543             if (sscanf(arg, "%d:%d", &dict_cache_size, &isam_cache_size) 
544                 != 2)
545             {
546                 fprintf(stderr, "bad cache sizes for -c\n");
547                 exit_usage();
548             }
549             break;
550         case 0:
551             fname = arg;
552             break;
553         case 'N':
554             int_count_enable = 0;
555             break;
556         default:
557             fprintf(stderr, "bad option.\n");
558             exit_usage();
559         }
560     }
561         
562     if (fname)
563     {
564         inf = fopen(fname, "rb");
565         if (!inf)
566         {
567             fprintf(stderr, "Cannot open %s\n", fname);
568             exit(1);
569         }
570     }
571     printf("# benchindex1 %s %s\n", __DATE__, __TIME__);
572     printf("# isam_cache_size = %d\n", isam_cache_size);
573     printf("# dict_cache_size = %d\n", dict_cache_size);
574     printf("# int_count_enable = %d\n", int_count_enable);
575     printf("# memory = %d\n", memory);
576
577     /* setup postings isamb attributes */
578     method_postings.compare_item = key_compare;
579     method_postings.log_item = key_logdump_txt;
580
581     method_postings.codec.start = iscz1_start;
582     method_postings.codec.decode = iscz1_decode;
583     method_postings.codec.encode = iscz1_encode;
584     method_postings.codec.stop = iscz1_stop;
585     method_postings.codec.reset = iscz1_reset;
586
587     method_postings.debug = 0;
588
589     /* create block system */
590     bfs = bfs_create(0, 0);
591     if (!bfs)
592     {
593         yaz_log(YLOG_WARN, "bfs_create failed");
594         exit(1);
595     }
596
597     if (reset)
598         bf_reset(bfs);
599
600     tim = yaz_timing_create();
601     /* create isam handle */
602     isb_postings = isamb_open (bfs, "isamb", isam_cache_size ? 1 : 0,
603                                &method_postings, 0);
604     if (!isb_postings)
605     {
606         yaz_log(YLOG_WARN, "isamb_open failed");
607         exit(2);
608     }
609     isamb_set_cache_size(isb_postings, isam_cache_size);
610     isamb_set_int_count(isb_postings, int_count_enable);
611     dict = dict_open(bfs, "dict", dict_cache_size, 1, 0, 4096);
612
613     dict_info = dict_lookup(dict, "_s");
614     if (dict_info)
615     {
616         assert(*dict_info == sizeof(docid_seq));
617         memcpy(&docid_seq, dict_info+1, sizeof(docid_seq));
618     }
619
620     if (!strcmp(type, "iso2709"))
621         index_marc_from_file(isb_postings, dict, &docid_seq, inf, memory, 
622                              0 /* verbose */ , 0 /* print_offset */);
623     else if (!strcmp(type, "line"))
624         index_marc_line_records(isb_postings, dict, &docid_seq, inf, memory);
625
626     printf("# Total " ZINT_FORMAT " documents\n", docid_seq);
627     dict_insert(dict, "_s", sizeof(docid_seq), &docid_seq);
628
629     dict_close(dict);
630     isamb_close(isb_postings);
631
632     if (fname)
633         fclose(inf);
634     /* exit block system */
635     bfs_destroy(bfs);
636     yaz_timing_stop(tim);
637
638     printf("# Total timings real=%8.6f user=%3.2f system=%3.2f\n",
639             yaz_timing_get_real(tim),
640             yaz_timing_get_user(tim),
641             yaz_timing_get_sys(tim));
642     
643     yaz_timing_destroy(&tim);
644
645     exit(0);
646     return 0;
647 }
648 /*
649  * Local variables:
650  * c-basic-offset: 4
651  * c-file-style: "Stroustrup"
652  * indent-tabs-mode: nil
653  * End:
654  * vim: shiftwidth=4 tabstop=8 expandtab
655  */
656