Cleanup of various buffer size entities.
[idzebra-moved-to-github.git] / index / kinput.c
1 /*
2  * Copyright (C) 1994-1996, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: kinput.c,v $
7  * Revision 1.17  1996-05-14 15:47:07  adam
8  * Cleanup of various buffer size entities.
9  *
10  * Revision 1.16  1996/04/09  10:05:20  adam
11  * Bug fix: prev_name buffer possibly too small; allocated in key_file_init.
12  *
13  * Revision 1.15  1996/03/21  14:50:09  adam
14  * File update uses modify-time instead of change-time.
15  *
16  * Revision 1.14  1996/02/07  14:06:37  adam
17  * Better progress report during register merge.
18  * New command: clean - removes temporary shadow files.
19  *
20  * Revision 1.13  1996/02/05  12:30:00  adam
21  * Logging reduced a bit.
22  * The remaining running time is estimated during register merge.
23  *
24  * Revision 1.12  1995/12/06  17:49:19  adam
25  * Uses dict_delete now.
26  *
27  * Revision 1.11  1995/12/06  16:06:43  adam
28  * Better diagnostics. Work on 'real' dictionary deletion.
29  *
30  * Revision 1.10  1995/12/06  12:41:22  adam
31  * New command 'stat' for the index program.
32  * Filenames can be read from stdin by specifying '-'.
33  * Bug fix/enhancement of the transformation from terms to regular
34  * expressons in the search engine.
35  *
36  * Revision 1.9  1995/10/10  12:24:39  adam
37  * Temporary sort files are compressed.
38  *
39  * Revision 1.8  1995/10/04  16:57:19  adam
40  * Key input and merge sort in one pass.
41  *
42  * Revision 1.7  1995/10/02  15:18:52  adam
43  * New member in recRetrieveCtrl: diagnostic.
44  *
45  * Revision 1.6  1995/09/29  15:51:56  adam
46  * First work on multi-way read.
47  *
48  * Revision 1.5  1995/09/29  14:01:43  adam
49  * Bug fixes.
50  *
51  * Revision 1.4  1995/09/28  14:22:57  adam
52  * Sort uses smaller temporary files.
53  *
54  * Revision 1.3  1995/09/06  16:11:17  adam
55  * Option: only one word key per file.
56  *
57  * Revision 1.2  1995/09/04  12:33:42  adam
58  * Various cleanup. YAZ util used instead.
59  *
60  * Revision 1.1  1995/09/04  09:10:37  adam
61  * More work on index add/del/update.
62  * Merge sort implemented.
63  * Initial work on z39 server.
64  *
65  */
66
67 #include <fcntl.h>
68 #include <unistd.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <stdio.h>
72 #include <ctype.h>
73 #include <assert.h>
74
75 #include "index.h"
76
77 #define KEY_SIZE (1+sizeof(struct it_key))
78 #define INP_NAME_MAX 768
79 #define INP_BUF_START 60000
80 #define INP_BUF_ADD  400000
81
82 static int no_diffs   = 0;
83 static int no_updates = 0;
84 static int no_deletions = 0;
85 static int no_insertions = 0;
86 static int no_iterations = 0;
87
88 struct key_file {
89     int   no;            /* file no */
90     off_t offset;        /* file offset */
91     unsigned char *buf;  /* buffer block */
92     size_t buf_size;     /* number of read bytes in block */
93     size_t chunk;        /* number of bytes allocated */
94     size_t buf_ptr;      /* current position in buffer */
95     char *prev_name;     /* last word read */
96     int   sysno;         /* last sysno */
97     int   seqno;         /* last seqno */
98     off_t length;        /* length of file */
99                          /* handler invoked in each read */
100     void (*readHandler)(struct key_file *keyp, void *rinfo);
101     void *readInfo;
102 };
103
104 void getFnameTmp (char *fname, int no)
105 {
106     sprintf (fname, TEMP_FNAME, no);
107 }
108
109 void key_file_chunk_read (struct key_file *f)
110 {
111     int nr = 0, r, fd;
112     char fname[1024];
113     getFnameTmp (fname, f->no);
114     fd = open (fname, O_RDONLY);
115     if (fd == -1)
116     {
117         logf (LOG_FATAL|LOG_ERRNO, "cannot open %s", fname);
118         exit (1);
119     }
120     if (!f->length)
121     {
122         if ((f->length = lseek (fd, 0L, SEEK_END)) == (off_t) -1)
123         {
124             logf (LOG_FATAL|LOG_ERRNO, "cannot seek %s", fname);
125             exit (1);
126         }
127     }
128     if (lseek (fd, f->offset, SEEK_SET) == -1)
129     {
130         logf (LOG_FATAL|LOG_ERRNO, "cannot seek %s", fname);
131         exit (1);
132     }
133     while (f->chunk - nr > 0)
134     {
135         r = read (fd, f->buf + nr, f->chunk - nr);
136         if (r <= 0)
137             break;
138         nr += r;
139     }
140     if (r == -1)
141     {
142         logf (LOG_FATAL|LOG_ERRNO, "read of %s", fname);
143         exit (1);
144     }
145     f->buf_size = nr;
146     f->buf_ptr = 0;
147     if (f->readHandler)
148         (*f->readHandler)(f, f->readInfo);
149     close (fd);
150 }
151
152 struct key_file *key_file_init (int no, int chunk)
153 {
154     struct key_file *f;
155
156     f = xmalloc (sizeof(*f));
157     f->sysno = 0;
158     f->seqno = 0;
159     f->no = no;
160     f->chunk = chunk;
161     f->offset = 0;
162     f->length = 0;
163     f->readHandler = NULL;
164     f->buf = xmalloc (f->chunk);
165     f->prev_name = xmalloc (INP_NAME_MAX);
166     *f->prev_name = '\0';
167     key_file_chunk_read (f);
168     return f;
169 }
170
171 int key_file_getc (struct key_file *f)
172 {
173     if (f->buf_ptr < f->buf_size)
174         return f->buf[(f->buf_ptr)++];
175     if (f->buf_size < f->chunk)
176         return EOF;
177     f->offset += f->buf_size;
178     key_file_chunk_read (f);
179     if (f->buf_ptr < f->buf_size)
180         return f->buf[(f->buf_ptr)++];
181     else
182         return EOF;
183 }
184
185 int key_file_decode (struct key_file *f)
186 {
187     int c, d;
188
189     c = key_file_getc (f);
190     switch (c & 192) 
191     {
192     case 0:
193         d = c;
194         break;
195     case 64:
196         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
197         break;
198     case 128:
199         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
200         d = (d << 8) + (key_file_getc (f) & 0xff);
201         break;
202     case 192:
203         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
204         d = (d << 8) + (key_file_getc (f) & 0xff);
205         d = (d << 8) + (key_file_getc (f) & 0xff);
206         break;
207     }
208     return d;
209 }
210
211 int key_file_read (struct key_file *f, char *key)
212 {
213     int i, d, c;
214     struct it_key itkey;
215
216     c = key_file_getc (f);
217     if (c == 0)
218     {
219         strcpy (key, f->prev_name);
220         i = 1+strlen (key);
221     }
222     else if (c == EOF)
223         return 0;
224     else
225     {
226         i = 0;
227         key[i++] = c;
228         while ((key[i++] = key_file_getc (f)))
229             ;
230         strcpy (f->prev_name, key);
231         f->sysno = 0;
232     }
233     d = key_file_decode (f);
234     key[i++] = d & 1;
235     d = d >> 1;
236     itkey.sysno = d + f->sysno;
237     if (d) 
238     {
239         f->sysno = itkey.sysno;
240         f->seqno = 0;
241     }
242     d = key_file_decode (f);
243     itkey.seqno = d + f->seqno;
244     f->seqno = itkey.seqno;
245     memcpy (key + i, &itkey, sizeof(struct it_key));
246     return i + sizeof (struct it_key);
247 }
248
249 struct heap_info {
250     struct {
251         struct key_file **file;
252         char   **buf;
253     } info;
254     int    heapnum;
255     int    *ptr;
256     int    (*cmp)(const void *p1, const void *p2);
257 };
258
259 struct heap_info *key_heap_init (int nkeys,
260                                  int (*cmp)(const void *p1, const void *p2))
261 {
262     struct heap_info *hi;
263     int i;
264
265     hi = xmalloc (sizeof(*hi));
266     hi->info.file = xmalloc (sizeof(*hi->info.file) * (1+nkeys));
267     hi->info.buf = xmalloc (sizeof(*hi->info.buf) * (1+nkeys));
268     hi->heapnum = 0;
269     hi->ptr = xmalloc (sizeof(*hi->ptr) * (1+nkeys));
270     hi->cmp = cmp;
271     for (i = 0; i<= nkeys; i++)
272     {
273         hi->ptr[i] = i;
274         hi->info.buf[i] = xmalloc (INP_NAME_MAX);
275     }
276     return hi;
277 }
278
279 static void key_heap_swap (struct heap_info *hi, int i1, int i2)
280 {
281     int swap;
282
283     swap = hi->ptr[i1];
284     hi->ptr[i1] = hi->ptr[i2];
285     hi->ptr[i2] = swap;
286 }
287
288
289 static void key_heap_delete (struct heap_info *hi)
290 {
291     int cur = 1, child = 2;
292
293     assert (hi->heapnum > 0);
294
295     key_heap_swap (hi, 1, hi->heapnum);
296     hi->heapnum--;
297     while (child <= hi->heapnum) {
298         if (child < hi->heapnum &&
299             (*hi->cmp)(&hi->info.buf[hi->ptr[child]],
300                        &hi->info.buf[hi->ptr[child+1]]) > 0)
301             child++;
302         if ((*hi->cmp)(&hi->info.buf[hi->ptr[cur]],
303                        &hi->info.buf[hi->ptr[child]]) > 0)
304         {            
305             key_heap_swap (hi, cur, child);
306             cur = child;
307             child = 2*cur;
308         }
309         else
310             break;
311     }
312 }
313
314 static void key_heap_insert (struct heap_info *hi, const char *buf, int nbytes,
315                              struct key_file *kf)
316 {
317     int cur, parent;
318
319     cur = ++(hi->heapnum);
320     memcpy (hi->info.buf[hi->ptr[cur]], buf, nbytes);
321     hi->info.file[hi->ptr[cur]] = kf;
322
323     parent = cur/2;
324     while (parent && (*hi->cmp)(&hi->info.buf[hi->ptr[parent]],
325                                 &hi->info.buf[hi->ptr[cur]]) > 0)
326     {
327         key_heap_swap (hi, cur, parent);
328         cur = parent;
329         parent = cur/2;
330     }
331 }
332
333 static int heap_read_one (struct heap_info *hi, char *name, char *key)
334 {
335     int n, r;
336     char rbuf[INP_NAME_MAX];
337     struct key_file *kf;
338
339     if (!hi->heapnum)
340         return 0;
341     n = hi->ptr[1];
342     strcpy (name, hi->info.buf[n]);
343     kf = hi->info.file[n];
344     r = strlen(name);
345     memcpy (key, hi->info.buf[n] + r+1, KEY_SIZE);
346     key_heap_delete (hi);
347     if ((r = key_file_read (kf, rbuf)))
348         key_heap_insert (hi, rbuf, r, kf);
349     no_iterations++;
350     return 1;
351 }
352
353 int heap_inp (Dict dict, ISAM isam, struct heap_info *hi)
354 {
355     char *info;
356     char next_name[INP_NAME_MAX];
357     char cur_name[INP_NAME_MAX];
358     int key_buf_size = INP_BUF_START;
359     int key_buf_ptr;
360     char *next_key;
361     char *key_buf;
362     int more;
363     
364     next_key = xmalloc (KEY_SIZE);
365     key_buf = xmalloc (key_buf_size);
366     more = heap_read_one (hi, cur_name, key_buf);
367     while (more)                   /* EOF ? */
368     {
369         int nmemb;
370         key_buf_ptr = KEY_SIZE;
371         while (1)
372         {
373             if (!(more = heap_read_one (hi, next_name, next_key)))
374                 break;
375             if (*next_name && strcmp (next_name, cur_name))
376                 break;
377             memcpy (key_buf + key_buf_ptr, next_key, KEY_SIZE);
378             key_buf_ptr += KEY_SIZE;
379             if (key_buf_ptr+KEY_SIZE >= key_buf_size)
380             {
381                 char *new_key_buf;
382                 new_key_buf = xmalloc (key_buf_size + INP_BUF_ADD);
383                 memcpy (new_key_buf, key_buf, key_buf_size);
384                 key_buf_size += INP_BUF_ADD;
385                 xfree (key_buf);
386                 key_buf = new_key_buf;
387             }
388         }
389         no_diffs++;
390         nmemb = key_buf_ptr / KEY_SIZE;
391         assert (nmemb*KEY_SIZE == key_buf_ptr);
392         if ((info = dict_lookup (dict, cur_name)))
393         {
394             ISAM_P isam_p, isam_p2;
395             logf (LOG_DEBUG, "updating %s", cur_name);
396             memcpy (&isam_p, info+1, sizeof(ISAM_P));
397             isam_p2 = is_merge (isam, isam_p, nmemb, key_buf);
398             if (!isam_p2)
399             {
400                 no_deletions++;
401                 if (!dict_delete (dict, cur_name))
402                     abort ();
403             }
404             else 
405             {
406                 no_updates++;
407                 if (isam_p2 != isam_p)
408                     dict_insert (dict, cur_name, sizeof(ISAM_P), &isam_p2);
409             }
410         }
411         else
412         {
413             ISAM_P isam_p;
414             logf (LOG_DEBUG, "inserting %s", cur_name);
415             no_insertions++;
416             isam_p = is_merge (isam, 0, nmemb, key_buf);
417             dict_insert (dict, cur_name, sizeof(ISAM_P), &isam_p);
418         }
419         memcpy (key_buf, next_key, KEY_SIZE);
420         strcpy (cur_name, next_name);
421     }
422     return 0;
423 }
424
425 struct progressInfo {
426     time_t   startTime;
427     time_t   lastTime;
428     off_t    totalBytes;
429     off_t    totalOffset;
430 };
431
432 void progressFunc (struct key_file *keyp, void *info)
433 {
434     struct progressInfo *p = info;
435     time_t now, remaining;
436
437     if (keyp->buf_size <= 0 || p->totalBytes <= 0)
438         return ;
439     time (&now);
440
441     if (now >= p->lastTime+10)
442     {
443         p->lastTime = now;
444         remaining = (now - p->startTime)*
445             ((double) p->totalBytes/p->totalOffset - 1.0);
446         if (remaining <= 130)
447             logf (LOG_LOG, "Merge %2.1f%% completed; %ld seconds remaining",
448                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining);
449         else
450             logf (LOG_LOG, "Merge %2.1f%% completed; %ld minutes remaining",
451                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining/60);
452     }
453     p->totalOffset += keyp->buf_size;
454 }
455
456 void key_input (const char *dict_fname, const char *isam_fname,
457                 int nkeys, int cache)
458                 
459 {
460     Dict dict;
461     ISAM isam;
462     struct key_file **kf;
463     char rbuf[1024];
464     int i, r;
465     struct heap_info *hi;
466     struct progressInfo progressInfo;
467
468     if (nkeys < 0)
469     {
470         char fname[1024];
471         nkeys = 0;
472         while (1)
473         {
474             getFnameTmp (fname, nkeys+1);
475             if (access (fname, R_OK) == -1)
476                 break;
477             nkeys++;
478         }
479         if (!nkeys)
480             return ;
481     }
482     dict = dict_open (dict_fname, cache, 1);
483     if (!dict)
484     {
485         logf (LOG_FATAL, "dict_open fail of `%s'", dict_fname);
486         exit (1);
487     }
488     isam = is_open (isam_fname, key_compare, 1, sizeof(struct it_key));
489     if (!isam)
490     {
491         logf (LOG_FATAL, "is_open fail of `%s'", isam_fname);
492         exit (1);
493     }
494
495     kf = xmalloc ((1+nkeys) * sizeof(*kf));
496     progressInfo.totalBytes = 0;
497     progressInfo.totalOffset = 0;
498     time (&progressInfo.startTime);
499     time (&progressInfo.lastTime);
500     for (i = 1; i<=nkeys; i++)
501     {
502         kf[i] = key_file_init (i, 32768);
503         kf[i]->readHandler = progressFunc;
504         kf[i]->readInfo = &progressInfo;
505         progressInfo.totalBytes += kf[i]->length;
506         progressInfo.totalOffset += kf[i]->buf_size;
507     }
508     hi = key_heap_init (nkeys, key_qsort_compare);
509     for (i = 1; i<=nkeys; i++)
510         if ((r = key_file_read (kf[i], rbuf)))
511             key_heap_insert (hi, rbuf, r, kf[i]);
512     heap_inp (dict, isam, hi);
513
514     dict_close (dict);
515     is_close (isam);
516     
517     for (i = 1; i<=nkeys; i++)
518     {
519         getFnameTmp (rbuf, i);
520         unlink (rbuf);
521     }
522     logf (LOG_LOG, "Iterations . . .%7d", no_iterations);
523     logf (LOG_LOG, "Distinct words .%7d", no_diffs);
524     logf (LOG_LOG, "Updates. . . . .%7d", no_updates);
525     logf (LOG_LOG, "Deletions. . . .%7d", no_deletions);
526     logf (LOG_LOG, "Insertions . . .%7d", no_insertions);
527 }
528
529