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