1b4d561578e61880a496cf8ce2a9cdf0312b67ec
[idzebra-moved-to-github.git] / index / recindex.c
1 /*
2  * Copyright (C) 1994-1999, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recindex.c,v $
7  * Revision 1.28  1999-12-08 22:44:45  adam
8  * Zebra/Z'mbol dependencies added.
9  *
10  * Revision 1.27  1999/10/29 10:02:33  adam
11  * Fixed decompression buffer overflow.
12  *
13  * Revision 1.26  1999/07/06 13:34:57  adam
14  * Fixed bug (introduced by previous commit).
15  *
16  * Revision 1.25  1999/07/06 12:28:04  adam
17  * Updated record index structure. Format includes version ID. Compression
18  * algorithm ID is stored for each record block.
19  *
20  * Revision 1.24  1999/06/25 13:48:02  adam
21  * Updated MSVC project files.
22  * Added BZIP2 record compression (not very well tested).
23  *
24  * Revision 1.23  1999/05/26 07:49:13  adam
25  * C++ compilation.
26  *
27  * Revision 1.22  1999/02/18 12:49:34  adam
28  * Changed file naming scheme for register files as well as record
29  * store/index files.
30  *
31  * Revision 1.21  1999/02/02 14:51:03  adam
32  * Updated WIN32 code specific sections. Changed header.
33  *
34  * Revision 1.20  1998/01/12 15:04:08  adam
35  * The test option (-s) only uses read-lock (and not write lock).
36  *
37  * Revision 1.19  1997/09/17 12:19:16  adam
38  * Zebra version corresponds to YAZ version 1.4.
39  * Changed Zebra server so that it doesn't depend on global common_resource.
40  *
41  * Revision 1.18  1997/07/15 16:28:42  adam
42  * Bug fix: storeData didn't work with files with multiple records.
43  * Bug fix: fixed memory management with records; not really well
44  *  thought through.
45  *
46  * Revision 1.17  1997/02/12 20:39:46  adam
47  * Implemented options -f <n> that limits the log to the first <n>
48  * records.
49  * Changed some log messages also.
50  *
51  * Revision 1.16  1996/06/04 10:19:00  adam
52  * Minor changes - removed include of ctype.h.
53  *
54  * Revision 1.15  1996/05/13  14:23:06  adam
55  * Work on compaction of set/use bytes in dictionary.
56  *
57  * Revision 1.14  1996/02/01  20:48:15  adam
58  * The total size of records are always checked in rec_cache_insert to
59  * reduce memory usage.
60  *
61  * Revision 1.13  1995/12/11  09:12:49  adam
62  * The rec_get function returns NULL if record doesn't exist - will
63  * happen in the server if the result set records have been deleted since
64  * the creation of the set (i.e. the search).
65  * The server saves a result temporarily if it is 'volatile', i.e. the
66  * set is register dependent.
67  *
68  * Revision 1.12  1995/12/07  17:38:47  adam
69  * Work locking mechanisms for concurrent updates/commit.
70  *
71  * Revision 1.11  1995/12/06  13:58:26  adam
72  * Improved flushing of records - all flushes except the last one
73  * don't write the last accessed. Also flush takes place if record
74  * info occupy more than about 256k.
75  *
76  * Revision 1.10  1995/12/06  12:41:24  adam
77  * New command 'stat' for the index program.
78  * Filenames can be read from stdin by specifying '-'.
79  * Bug fix/enhancement of the transformation from terms to regular
80  * expressons in the search engine.
81  *
82  * Revision 1.9  1995/11/30  08:34:33  adam
83  * Started work on commit facility.
84  * Changed a few malloc/free to xmalloc/xfree.
85  *
86  * Revision 1.8  1995/11/28  14:26:21  adam
87  * Bug fix: recordId with constant wasn't right.
88  * Bug fix: recordId dictionary entry wasn't deleted when needed.
89  *
90  * Revision 1.7  1995/11/28  09:09:43  adam
91  * Zebra config renamed.
92  * Use setting 'recordId' to identify record now.
93  * Bug fix in recindex.c: rec_release_blocks was invokeded even
94  * though the blocks were already released.
95  * File traversal properly deletes records when needed.
96  *
97  * Revision 1.6  1995/11/25  10:24:06  adam
98  * More record fields - they are enumerated now.
99  * New options: flagStoreData flagStoreKey.
100  *
101  * Revision 1.5  1995/11/22  17:19:18  adam
102  * Record management uses the bfile system.
103  *
104  * Revision 1.4  1995/11/20  16:59:46  adam
105  * New update method: the 'old' keys are saved for each records.
106  *
107  * Revision 1.3  1995/11/16  15:34:55  adam
108  * Uses new record management system in both indexer and server.
109  *
110  * Revision 1.2  1995/11/15  19:13:08  adam
111  * Work on record management.
112  *
113  * Revision 1.1  1995/11/15  14:46:20  adam
114  * Started work on better record management system.
115  *
116  */
117
118
119 /*
120  *  Format of first block
121  *      next       (4 bytes)
122  *      ref_count  (4 bytes)
123  *      block      (504 bytes)
124  *
125  *  Format of subsequent blocks 
126  *      next  (4 bytes)
127  *      block (508 bytes)
128  *
129  *  Format of each record
130  *      sysno
131  *      (length, data) - pairs
132  *      length = 0 if same as previous
133  */
134 #include <stdio.h>
135 #include <assert.h>
136 #include <string.h>
137
138 #include "recindxp.h"
139
140 #if HAVE_BZLIB_H
141 #include <bzlib.h>
142 #endif
143 static void rec_write_head (Records p)
144 {
145     int r;
146
147     assert (p);
148     assert (p->index_BFile);
149
150     r = bf_write (p->index_BFile, 0, 0, sizeof(p->head), &p->head);    
151     if (r)
152     {
153         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
154         exit (1);
155     }
156 }
157
158 static void rec_tmp_expand (Records p, int size)
159 {
160     if (p->tmp_size < size + 2048 ||
161         p->tmp_size < p->head.block_size[REC_BLOCK_TYPES-1]*2)
162     {
163         xfree (p->tmp_buf);
164         p->tmp_size = size + p->head.block_size[REC_BLOCK_TYPES-1]*2 + 2048;
165         p->tmp_buf = (char *) xmalloc (p->tmp_size);
166     }
167 }
168
169 static int read_indx (Records p, int sysno, void *buf, int itemsize, 
170                       int ignoreError)
171 {
172     int r;
173     int pos = (sysno-1)*itemsize;
174
175     r = bf_read (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
176     if (r != 1 && !ignoreError)
177     {
178         logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
179               p->index_fname, (long) pos);
180         exit (1);
181     }
182     return r;
183 }
184
185 static void write_indx (Records p, int sysno, void *buf, int itemsize)
186 {
187     int pos = (sysno-1)*itemsize;
188
189     bf_write (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
190 }
191
192 static void rec_release_blocks (Records p, int sysno)
193 {
194     struct record_index_entry entry;
195     int freeblock;
196     char block_and_ref[sizeof(short) + sizeof(int)];
197     int dst_type;
198     int first = 1;
199
200     if (read_indx (p, sysno, &entry, sizeof(entry), 1) != 1)
201         return ;
202
203     freeblock = entry.next;
204     assert (freeblock > 0);
205     dst_type = freeblock & 7;
206     assert (dst_type < REC_BLOCK_TYPES);
207     freeblock = freeblock / 8;
208     while (freeblock)
209     {
210         if (bf_read (p->data_BFile[dst_type], freeblock, 0,
211                      sizeof(block_and_ref), block_and_ref) != 1)
212         {
213             logf (LOG_FATAL|LOG_ERRNO, "read in rec_del_single");
214             exit (1);
215         }
216         if (first)
217         {
218             short ref;
219             memcpy (&ref, block_and_ref + sizeof(int), sizeof(ref));
220             --ref;
221             memcpy (block_and_ref + sizeof(int), &ref, sizeof(ref));
222             if (ref)
223             {
224                 if (bf_write (p->data_BFile[dst_type], freeblock, 0,
225                               sizeof(block_and_ref), block_and_ref))
226                 {
227                     logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
228                     exit (1);
229                 }
230                 return;
231             }
232             first = 0;
233         }
234         
235         if (bf_write (p->data_BFile[dst_type], freeblock, 0, sizeof(freeblock),
236                       &p->head.block_free[dst_type]))
237         {
238             logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
239             exit (1);
240         }
241         p->head.block_free[dst_type] = freeblock;
242         memcpy (&freeblock, block_and_ref, sizeof(int));
243
244         p->head.block_used[dst_type]--;
245     }
246     p->head.total_bytes -= entry.size;
247 }
248
249 static void rec_delete_single (Records p, Record rec)
250 {
251     struct record_index_entry entry;
252
253     rec_release_blocks (p, rec->sysno);
254
255     entry.next = p->head.index_free;
256     entry.size = 0;
257     p->head.index_free = rec->sysno;
258     write_indx (p, rec->sysno, &entry, sizeof(entry));
259 }
260
261 static void rec_write_tmp_buf (Records p, int size, int *sysnos)
262 {
263     struct record_index_entry entry;
264     int no_written = 0;
265     char *cptr = p->tmp_buf;
266     int block_prev = -1, block_free;
267     int dst_type = 0;
268     int i;
269
270     for (i = 1; i<REC_BLOCK_TYPES; i++)
271         if (size >= p->head.block_move[i])
272             dst_type = i;
273     while (no_written < size)
274     {
275         block_free = p->head.block_free[dst_type];
276         if (block_free)
277         {
278             if (bf_read (p->data_BFile[dst_type],
279                          block_free, 0, sizeof(*p->head.block_free),
280                          &p->head.block_free[dst_type]) != 1)
281             {
282                 logf (LOG_FATAL|LOG_ERRNO, "read in %s at free block %d",
283                       p->data_fname[dst_type], block_free);
284                 exit (1);
285             }
286         }
287         else
288             block_free = p->head.block_last[dst_type]++;
289         if (block_prev == -1)
290         {
291             entry.next = block_free*8 + dst_type;
292             entry.size = size;
293             p->head.total_bytes += size;
294             while (*sysnos > 0)
295             {
296                 write_indx (p, *sysnos, &entry, sizeof(entry));
297                 sysnos++;
298             }
299         }
300         else
301         {
302             memcpy (cptr, &block_free, sizeof(int));
303             bf_write (p->data_BFile[dst_type], block_prev, 0, 0, cptr);
304             cptr = p->tmp_buf + no_written;
305         }
306         block_prev = block_free;
307         no_written += p->head.block_size[dst_type] - sizeof(int);
308         p->head.block_used[dst_type]++;
309     }
310     assert (block_prev != -1);
311     block_free = 0;
312     memcpy (cptr, &block_free, sizeof(int));
313     bf_write (p->data_BFile[dst_type], block_prev, 0,
314               sizeof(int) + (p->tmp_buf+size) - cptr, cptr);
315 }
316
317 Records rec_open (BFiles bfs, int rw, int compression_method)
318 {
319     Records p;
320     int i, r;
321     int version;
322
323     p = (Records) xmalloc (sizeof(*p));
324     p->compression_method = compression_method;
325     p->rw = rw;
326     p->tmp_size = 1024;
327     p->tmp_buf = (char *) xmalloc (p->tmp_size);
328     p->index_fname = "reci";
329     p->index_BFile = bf_open (bfs, p->index_fname, 128, rw);
330     if (p->index_BFile == NULL)
331     {
332         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
333         exit (1);
334     }
335     r = bf_read (p->index_BFile, 0, 0, 0, p->tmp_buf);
336     switch (r)
337     {
338     case 0:
339         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
340         sprintf (p->head.version, "%3d", REC_VERSION);
341         p->head.index_free = 0;
342         p->head.index_last = 1;
343         p->head.no_records = 0;
344         p->head.total_bytes = 0;
345         for (i = 0; i<REC_BLOCK_TYPES; i++)
346         {
347             p->head.block_free[i] = 0;
348             p->head.block_last[i] = 1;
349             p->head.block_used[i] = 0;
350         }
351         p->head.block_size[0] = 128;
352         p->head.block_move[0] = 0;
353         for (i = 1; i<REC_BLOCK_TYPES; i++)
354         {
355             p->head.block_size[i] = p->head.block_size[i-1] * 4;
356             p->head.block_move[i] = p->head.block_size[i] * 24;
357         }
358         if (rw)
359             rec_write_head (p);
360         break;
361     case 1:
362         memcpy (&p->head, p->tmp_buf, sizeof(p->head));
363         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
364         {
365             logf (LOG_FATAL, "file %s has bad format", p->index_fname);
366             exit (1);
367         }
368         version = atoi (p->head.version);
369         if (version != REC_VERSION)
370         {
371             logf (LOG_FATAL, "file %s is version %d, but version"
372                   " %d is required", p->index_fname, version, REC_VERSION);
373             exit (1);
374         }
375         break;
376     }
377     for (i = 0; i<REC_BLOCK_TYPES; i++)
378     {
379         char str[80];
380         sprintf (str, "recd%c", i + 'A');
381         p->data_fname[i] = (char *) xmalloc (strlen(str)+1);
382         strcpy (p->data_fname[i], str);
383         p->data_BFile[i] = NULL;
384     }
385     for (i = 0; i<REC_BLOCK_TYPES; i++)
386     {
387         if (!(p->data_BFile[i] = bf_open (bfs, p->data_fname[i],
388                                           p->head.block_size[i],
389                                           rw)))
390         {
391             logf (LOG_FATAL|LOG_ERRNO, "bf_open %s", p->data_fname[i]);
392             exit (1);
393         }
394     }
395     p->cache_max = 400;
396     p->cache_cur = 0;
397     p->record_cache = (struct record_cache_entry *)
398         xmalloc (sizeof(*p->record_cache)*p->cache_max);
399     return p;
400 }
401
402 static void rec_encode_unsigned (unsigned n, unsigned char *buf, int *len)
403 {
404     (*len) = 0;
405     while (n > 127)
406     {
407         buf[*len] = 128 + (n & 127);
408         n = n >> 7;
409         (*len)++;
410     }
411     buf[*len] = n;
412     (*len)++;
413 }
414
415 static void rec_decode_unsigned(unsigned *np, unsigned char *buf, int *len)
416 {
417     unsigned n = 0;
418     unsigned w = 1;
419     (*len) = 0;
420
421     while (buf[*len] > 127)
422     {
423         n += w*(buf[*len] & 127);
424         w = w << 7;
425         (*len)++;
426     }
427     n += w * buf[*len];
428     (*len)++;
429     *np = n;
430 }
431
432 static void rec_cache_flush_block1 (Records p, Record rec, Record last_rec,
433                                     char **out_buf, int *out_size,
434                                     int *out_offset)
435 {
436     int i;
437     int len;
438
439     for (i = 0; i<REC_NO_INFO; i++)
440     {
441         if (*out_offset + (int) rec->size[i] + 20 > *out_size)
442         {
443             int new_size = *out_offset + rec->size[i] + 65536;
444             char *np = (char *) xmalloc (new_size);
445             if (*out_offset)
446                 memcpy (np, *out_buf, *out_offset);
447             xfree (*out_buf);
448             *out_size = new_size;
449             *out_buf = np;
450         }
451         if (i == 0)
452         {
453             rec_encode_unsigned (rec->sysno, *out_buf + *out_offset, &len);
454             (*out_offset) += len;
455         }
456         if (rec->size[i] == 0)
457         {
458             rec_encode_unsigned (1, *out_buf + *out_offset, &len);
459             (*out_offset) += len;
460         }
461         else if (last_rec && rec->size[i] == last_rec->size[i] &&
462                  !memcmp (rec->info[i], last_rec->info[i], rec->size[i]))
463         {
464             rec_encode_unsigned (0, *out_buf + *out_offset, &len);
465             (*out_offset) += len;
466         }
467         else
468         {
469             rec_encode_unsigned (rec->size[i]+1, *out_buf + *out_offset, &len);
470             (*out_offset) += len;
471             memcpy (*out_buf + *out_offset, rec->info[i], rec->size[i]);
472             (*out_offset) += rec->size[i];
473         }
474     }
475 }
476
477 static void rec_write_multiple (Records p, int saveCount)
478 {
479     int i;
480     short ref_count = 0;
481     char compression_method;
482     Record last_rec = 0;
483     int out_size = 1000;
484     int out_offset = 0;
485     char *out_buf = (char *) xmalloc (out_size);
486     int *sysnos = (int *) xmalloc (sizeof(*sysnos) * (p->cache_cur + 1));
487     int *sysnop = sysnos;
488
489     for (i = 0; i<p->cache_cur - saveCount; i++)
490     {
491         struct record_cache_entry *e = p->record_cache + i;
492         switch (e->flag)
493         {
494         case recordFlagNew:
495             rec_cache_flush_block1 (p, e->rec, last_rec, &out_buf,
496                                     &out_size, &out_offset);
497             *sysnop++ = e->rec->sysno;
498             ref_count++;
499             e->flag = recordFlagNop;
500             last_rec = e->rec;
501             break;
502         case recordFlagWrite:
503             rec_release_blocks (p, e->rec->sysno);
504             rec_cache_flush_block1 (p, e->rec, last_rec, &out_buf,
505                                     &out_size, &out_offset);
506             *sysnop++ = e->rec->sysno;
507             ref_count++;
508             e->flag = recordFlagNop;
509             last_rec = e->rec;
510             break;
511         case recordFlagDelete:
512             rec_delete_single (p, e->rec);
513             e->flag = recordFlagNop;
514             break;
515         default:
516             break;
517         }
518     }
519
520     *sysnop = -1;
521     if (ref_count)
522     {
523         int csize = 0;  /* indicate compression "not performed yet" */
524         compression_method = p->compression_method;
525         switch (compression_method)
526         {
527         case REC_COMPRESS_BZIP2:
528 #if HAVE_BZLIB_H        
529             csize = out_offset + (out_offset >> 6) + 620;
530             rec_tmp_expand (p, csize);
531             i = bzBuffToBuffCompress (p->tmp_buf+sizeof(int)+sizeof(short)+
532                                       sizeof(char),
533                                       &csize, out_buf, out_offset, 1, 0, 30);
534             if (i != BZ_OK)
535             {
536                 logf (LOG_WARN, "bzBuffToBuffCompress error code=%d", i);
537                 csize = 0;
538             }
539             logf (LOG_LOG, "compress %4d %5d %5d", ref_count, out_offset,
540                   csize);
541 #endif
542             break;
543         case REC_COMPRESS_NONE:
544             break;
545         }
546         if (!csize)  
547         {
548             /* either no compression or compression not supported ... */
549             csize = out_offset;
550             rec_tmp_expand (p, csize);
551             memcpy (p->tmp_buf + sizeof(int) + sizeof(short) + sizeof(char),
552                     out_buf, out_offset);
553             csize = out_offset;
554             compression_method = REC_COMPRESS_NONE;
555         }
556         memcpy (p->tmp_buf + sizeof(int), &ref_count, sizeof(ref_count));
557         memcpy (p->tmp_buf + sizeof(int)+sizeof(short),
558                 &compression_method, sizeof(compression_method));
559                 
560         /* -------- compression */
561         rec_write_tmp_buf (p, csize + sizeof(short) + sizeof(char), sysnos);
562     }
563     xfree (out_buf);
564     xfree (sysnos);
565 }
566
567 static void rec_cache_flush (Records p, int saveCount)
568 {
569     int i, j;
570
571     if (saveCount >= p->cache_cur)
572         saveCount = 0;
573
574     rec_write_multiple (p, saveCount);
575
576     for (i = 0; i<p->cache_cur - saveCount; i++)
577     {
578         struct record_cache_entry *e = p->record_cache + i;
579         rec_rm (&e->rec);
580     } 
581     /* i still being used ... */
582     for (j = 0; j<saveCount; j++, i++)
583         memcpy (p->record_cache+j, p->record_cache+i,
584                 sizeof(*p->record_cache));
585     p->cache_cur = saveCount;
586 }
587
588 static Record *rec_cache_lookup (Records p, int sysno,
589                                  enum recordCacheFlag flag)
590 {
591     int i;
592     for (i = 0; i<p->cache_cur; i++)
593     {
594         struct record_cache_entry *e = p->record_cache + i;
595         if (e->rec->sysno == sysno)
596         {
597             if (flag != recordFlagNop && e->flag == recordFlagNop)
598                 e->flag = flag;
599             return &e->rec;
600         }
601     }
602     return NULL;
603 }
604
605 static void rec_cache_insert (Records p, Record rec, enum recordCacheFlag flag)
606 {
607     struct record_cache_entry *e;
608
609     if (p->cache_cur == p->cache_max)
610         rec_cache_flush (p, 1);
611     else if (p->cache_cur > 0)
612     {
613         int i, j;
614         int used = 0;
615         for (i = 0; i<p->cache_cur; i++)
616         {
617             Record r = (p->record_cache + i)->rec;
618             for (j = 0; j<REC_NO_INFO; j++)
619                 used += r->size[j];
620         }
621         if (used > 90000)
622             rec_cache_flush (p, 1);
623     }
624     assert (p->cache_cur < p->cache_max);
625
626     e = p->record_cache + (p->cache_cur)++;
627     e->flag = flag;
628     e->rec = rec_cp (rec);
629 }
630
631 void rec_close (Records *pp)
632 {
633     Records p = *pp;
634     int i;
635
636     assert (p);
637
638     rec_cache_flush (p, 0);
639     xfree (p->record_cache);
640
641     if (p->rw)
642         rec_write_head (p);
643
644     if (p->index_BFile)
645         bf_close (p->index_BFile);
646
647     for (i = 0; i<REC_BLOCK_TYPES; i++)
648     {
649         if (p->data_BFile[i])
650             bf_close (p->data_BFile[i]);
651         xfree (p->data_fname[i]);
652     }
653     xfree (p->tmp_buf);
654     xfree (p);
655     *pp = NULL;
656 }
657
658
659 Record rec_get (Records p, int sysno)
660 {
661     int i, in_size, r;
662     Record rec, *recp;
663     struct record_index_entry entry;
664     int freeblock, dst_type;
665     char *nptr, *cptr;
666     char *in_buf = 0;
667     char *bz_buf = 0;
668     int bz_size;
669     char compression_method;
670
671     assert (sysno > 0);
672     assert (p);
673
674     if ((recp = rec_cache_lookup (p, sysno, recordFlagNop)))
675         return rec_cp (*recp);
676
677     if (read_indx (p, sysno, &entry, sizeof(entry), 1) < 1)
678         return NULL;       /* record is not there! */
679
680     if (!entry.size)
681         return NULL;       /* record is deleted */
682
683     dst_type = entry.next & 7;
684     assert (dst_type < REC_BLOCK_TYPES);
685     freeblock = entry.next / 8;
686
687     assert (freeblock > 0);
688     
689     rec_tmp_expand (p, entry.size);
690
691     cptr = p->tmp_buf;
692     r = bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
693     if (r < 0)
694         return 0;
695     memcpy (&freeblock, cptr, sizeof(freeblock));
696
697     while (freeblock)
698     {
699         int tmp;
700
701         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
702         
703         memcpy (&tmp, cptr, sizeof(tmp));
704         r = bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
705         if (r < 0)
706             return 0;
707         memcpy (&freeblock, cptr, sizeof(freeblock));
708         memcpy (cptr, &tmp, sizeof(tmp));
709     }
710
711     rec = (Record) xmalloc (sizeof(*rec));
712     rec->sysno = sysno;
713     memcpy (&compression_method, p->tmp_buf + sizeof(int) + sizeof(short),
714             sizeof(compression_method));
715     in_buf = p->tmp_buf + sizeof(int) + sizeof(short) + sizeof(char);
716     in_size = entry.size - sizeof(short) - sizeof(char);
717     switch (compression_method)
718     {
719     case REC_COMPRESS_BZIP2:
720 #if HAVE_BZLIB_H
721         bz_size = entry.size * 20 + 100;
722         while (1)
723         {
724             bz_buf = (char *) xmalloc (bz_size);
725             i = bzBuffToBuffDecompress (bz_buf, &bz_size, in_buf, in_size, 0, 0);
726             logf (LOG_LOG, "decompress %5d %5d", in_size, bz_size);
727             if (i == BZ_OK)
728                 break;
729             logf (LOG_LOG, "failed");
730             xfree (bz_buf);
731             bz_size *= 2;
732         }
733         in_buf = bz_buf;
734         in_size = bz_size;
735 #else
736         logf (LOG_FATAL, "cannot decompress record(s) in BZIP2 format");
737         exit (1);
738 #endif
739         break;
740     case REC_COMPRESS_NONE:
741         break;
742     }
743     for (i = 0; i<REC_NO_INFO; i++)
744         rec->info[i] = 0;
745
746     nptr = in_buf;                /* skip ref count */
747     while (nptr < in_buf + in_size)
748     {
749         int this_sysno;
750         int len;
751         rec_decode_unsigned (&this_sysno, nptr, &len);
752         nptr += len;
753
754         for (i = 0; i < REC_NO_INFO; i++)
755         {
756             int this_size;
757             rec_decode_unsigned (&this_size, nptr, &len);
758             nptr += len;
759
760             if (this_size == 0)
761                 continue;
762             rec->size[i] = this_size-1;
763
764             if (rec->size[i])
765             {
766                 rec->info[i] = nptr;
767                 nptr += rec->size[i];
768             }
769             else
770                 rec->info[i] = NULL;
771         }
772         if (this_sysno == sysno)
773             break;
774     }
775     for (i = 0; i<REC_NO_INFO; i++)
776     {
777         if (rec->info[i] && rec->size[i])
778         {
779             char *np = xmalloc (rec->size[i]);
780             memcpy (np, rec->info[i], rec->size[i]);
781             rec->info[i] = np;
782         }
783         else
784         {
785             assert (rec->info[i] == 0);
786             assert (rec->size[i] == 0);
787         }
788     }
789     xfree (bz_buf);
790     rec_cache_insert (p, rec, recordFlagNop);
791     return rec;
792 }
793
794 Record rec_new (Records p)
795 {
796     int sysno, i;
797     Record rec;
798
799     assert (p);
800     rec = (Record) xmalloc (sizeof(*rec));
801     if (1 || p->head.index_free == 0)
802         sysno = (p->head.index_last)++;
803     else
804     {
805         struct record_index_entry entry;
806
807         read_indx (p, p->head.index_free, &entry, sizeof(entry), 0);
808         sysno = p->head.index_free;
809         p->head.index_free = entry.next;
810     }
811 #if ZMBOL
812 #else
813     if (sysno > 100000)
814     {
815         logf (LOG_FATAL, "100,000 record limit reached");
816         exit (1);
817     }
818 #endif
819     (p->head.no_records)++;
820     rec->sysno = sysno;
821     for (i = 0; i < REC_NO_INFO; i++)
822     {
823         rec->info[i] = NULL;
824         rec->size[i] = 0;
825     }
826     rec_cache_insert (p, rec, recordFlagNew);
827     return rec;
828 }
829
830 void rec_del (Records p, Record *recpp)
831 {
832     Record *recp;
833
834     (p->head.no_records)--;
835     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagDelete)))
836     {
837         rec_rm (recp);
838         *recp = *recpp;
839     }
840     else
841     {
842         rec_cache_insert (p, *recpp, recordFlagDelete);
843         rec_rm (recpp);
844     }
845     *recpp = NULL;
846 }
847
848 void rec_put (Records p, Record *recpp)
849 {
850     Record *recp;
851
852     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagWrite)))
853     {
854         rec_rm (recp);
855         *recp = *recpp;
856     }
857     else
858     {
859         rec_cache_insert (p, *recpp, recordFlagWrite);
860         rec_rm (recpp);
861     }
862     *recpp = NULL;
863 }
864
865 void rec_rm (Record *recpp)
866 {
867     int i;
868
869     if (!*recpp)
870         return ;
871     for (i = 0; i < REC_NO_INFO; i++)
872         xfree ((*recpp)->info[i]);
873     xfree (*recpp);
874     *recpp = NULL;
875 }
876
877 Record rec_cp (Record rec)
878 {
879     Record n;
880     int i;
881
882     n = (Record) xmalloc (sizeof(*n));
883     n->sysno = rec->sysno;
884     for (i = 0; i < REC_NO_INFO; i++)
885         if (!rec->info[i])
886         {
887             n->info[i] = NULL;
888             n->size[i] = 0;
889         }
890         else
891         {
892             n->size[i] = rec->size[i];
893             n->info[i] = (char *) xmalloc (rec->size[i]);
894             memcpy (n->info[i], rec->info[i], rec->size[i]);
895         }
896     return n;
897 }
898
899
900 char *rec_strdup (const char *s, size_t *len)
901 {
902     char *p;
903
904     if (!s)
905     {
906         *len = 0;
907         return NULL;
908     }
909     *len = strlen(s)+1;
910     p = (char *) xmalloc (*len);
911     strcpy (p, s);
912     return p;
913 }
914