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