Allow record subsystem to return system numbers (document ID) with a fake
[idzebra-moved-to-github.git] / index / recindex.c
1 /* $Id: recindex.c,v 1.45 2005-08-09 12:30:46 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #define RIDX_CHUNK 128
24
25 /*
26  *  Format of first block
27  *      next       (8 bytes)
28  *      ref_count  (2 bytes)
29  *      block      (500 bytes)
30  *
31  *  Format of subsequent blocks 
32  *      next  (8 bytes)
33  *      block (502 bytes)
34  *
35  *  Format of each record
36  *      sysno
37  *      (length, data) - pairs
38  *      length = 0 if same as previous
39  */
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <assert.h>
43 #include <string.h>
44
45 #include <yaz/yaz-util.h>
46 #include "recindxp.h"
47
48 #if HAVE_BZLIB_H
49 #include <bzlib.h>
50 #endif
51
52 /* Modify argument to if below: 1=normal, 0=sysno testing */
53 #if 1
54 /* If this is used sysno are not converted (no testing) */
55 #define FAKE_OFFSET 0
56 #define USUAL_RANGE 6000000000LL
57
58 #else
59 /* Use a fake > 2^32 offset so we can test for proper 64-bit handling */
60 #define FAKE_OFFSET 6000000000LL
61 #define USUAL_RANGE 2000000000LL
62 #endif
63
64 static SYSNO rec_sysno_to_ext(SYSNO sysno)
65 {
66     assert(sysno >= 0 && sysno <= USUAL_RANGE);
67     return sysno + FAKE_OFFSET;
68 }
69
70 SYSNO rec_sysno_to_int(SYSNO sysno)
71 {
72     assert(sysno >= FAKE_OFFSET && sysno <= FAKE_OFFSET + USUAL_RANGE);
73     return sysno - FAKE_OFFSET;
74 }
75
76 static void rec_write_head(Records p)
77 {
78     int r;
79
80     assert(p);
81     assert(p->index_BFile);
82
83     r = bf_write(p->index_BFile, 0, 0, sizeof(p->head), &p->head);    
84     if (r)
85     {
86         yaz_log(YLOG_FATAL|YLOG_ERRNO, "write head of %s", p->index_fname);
87         exit(1);
88     }
89 }
90
91 static void rec_tmp_expand(Records p, int size)
92 {
93     if (p->tmp_size < size + 2048 ||
94         p->tmp_size < p->head.block_size[REC_BLOCK_TYPES-1]*2)
95     {
96         xfree(p->tmp_buf);
97         p->tmp_size = size + (int)
98                         (p->head.block_size[REC_BLOCK_TYPES-1])*2 + 2048;
99         p->tmp_buf = (char *) xmalloc(p->tmp_size);
100     }
101 }
102
103 static int read_indx(Records p, SYSNO sysno, void *buf, int itemsize, 
104                       int ignoreError)
105 {
106     int r;
107     zint pos = (sysno-1)*itemsize;
108     int off = (int) (pos%RIDX_CHUNK);
109     int sz1 = RIDX_CHUNK - off;    /* sz1 is size of buffer to read.. */
110
111     if (sz1 > itemsize)
112         sz1 = itemsize;  /* no more than itemsize bytes */
113
114     r = bf_read(p->index_BFile, 1+pos/RIDX_CHUNK, off, sz1, buf);
115     if (r == 1 && sz1 < itemsize) /* boundary? - must read second part */
116         r = bf_read(p->index_BFile, 2+pos/RIDX_CHUNK, 0, itemsize - sz1,
117                         (char*) buf + sz1);
118     if (r != 1 && !ignoreError)
119     {
120         yaz_log(YLOG_FATAL|YLOG_ERRNO, "read in %s at pos %ld",
121               p->index_fname, (long) pos);
122         exit(1);
123     }
124     return r;
125 }
126
127 static void write_indx(Records p, SYSNO sysno, void *buf, int itemsize)
128 {
129     zint pos = (sysno-1)*itemsize;
130     int off = (int) (pos%RIDX_CHUNK);
131     int sz1 = RIDX_CHUNK - off;    /* sz1 is size of buffer to read.. */
132
133     if (sz1 > itemsize)
134         sz1 = itemsize;  /* no more than itemsize bytes */
135
136     bf_write(p->index_BFile, 1+pos/RIDX_CHUNK, off, sz1, buf);
137     if (sz1 < itemsize)   /* boundary? must write second part */
138         bf_write(p->index_BFile, 2+pos/RIDX_CHUNK, 0, itemsize - sz1,
139                 (char*) buf + sz1);
140 }
141
142 static void rec_release_blocks(Records p, SYSNO sysno)
143 {
144     struct record_index_entry entry;
145     zint freeblock;
146     char block_and_ref[sizeof(zint) + sizeof(short)];
147     int dst_type;
148     int first = 1;
149
150     if (read_indx(p, sysno, &entry, sizeof(entry), 1) != 1)
151         return ;
152
153     freeblock = entry.next;
154     assert(freeblock > 0);
155     dst_type = (int) (freeblock & 7);
156     assert(dst_type < REC_BLOCK_TYPES);
157     freeblock = freeblock / 8;
158     while (freeblock)
159     {
160         if (bf_read(p->data_BFile[dst_type], freeblock, 0,
161                      first ? sizeof(block_and_ref) : sizeof(zint),
162                      block_and_ref) != 1)
163         {
164             yaz_log(YLOG_FATAL|YLOG_ERRNO, "read in rec_del_single");
165             exit(1);
166         }
167         if (first)
168         {
169             short ref;
170             memcpy(&ref, block_and_ref + sizeof(freeblock), sizeof(ref));
171             --ref;
172             memcpy(block_and_ref + sizeof(freeblock), &ref, sizeof(ref));
173             if (ref)
174             {
175                 if (bf_write(p->data_BFile[dst_type], freeblock, 0,
176                               sizeof(block_and_ref), block_and_ref))
177                 {
178                     yaz_log(YLOG_FATAL|YLOG_ERRNO, "write in rec_del_single");
179                     exit(1);
180                 }
181                 return;
182             }
183             first = 0;
184         }
185         
186         if (bf_write(p->data_BFile[dst_type], freeblock, 0, sizeof(freeblock),
187                       &p->head.block_free[dst_type]))
188         {
189             yaz_log(YLOG_FATAL|YLOG_ERRNO, "write in rec_del_single");
190             exit(1);
191         }
192         p->head.block_free[dst_type] = freeblock;
193         memcpy(&freeblock, block_and_ref, sizeof(freeblock));
194
195         p->head.block_used[dst_type]--;
196     }
197     p->head.total_bytes -= entry.size;
198 }
199
200 static void rec_delete_single(Records p, Record rec)
201 {
202     struct record_index_entry entry;
203
204     rec_release_blocks(p, rec_sysno_to_int(rec->sysno));
205
206     entry.next = p->head.index_free;
207     entry.size = 0;
208     p->head.index_free = rec_sysno_to_int(rec->sysno);
209     write_indx(p, rec_sysno_to_int(rec->sysno), &entry, sizeof(entry));
210 }
211
212 static void rec_write_tmp_buf(Records p, int size, SYSNO *sysnos)
213 {
214     struct record_index_entry entry;
215     int no_written = 0;
216     char *cptr = p->tmp_buf;
217     zint block_prev = -1, block_free;
218     int dst_type = 0;
219     int i;
220
221     for (i = 1; i<REC_BLOCK_TYPES; i++)
222         if (size >= p->head.block_move[i])
223             dst_type = i;
224     while (no_written < size)
225     {
226         block_free = p->head.block_free[dst_type];
227         if (block_free)
228         {
229             if (bf_read(p->data_BFile[dst_type],
230                          block_free, 0, sizeof(*p->head.block_free),
231                          &p->head.block_free[dst_type]) != 1)
232             {
233                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read in %s at free block "
234                          ZINT_FORMAT,
235                          p->data_fname[dst_type], block_free);
236                 exit(1);
237             }
238         }
239         else
240             block_free = p->head.block_last[dst_type]++;
241         if (block_prev == -1)
242         {
243             entry.next = block_free*8 + dst_type;
244             entry.size = size;
245             p->head.total_bytes += size;
246             while (*sysnos > 0)
247             {
248                 write_indx(p, *sysnos, &entry, sizeof(entry));
249                 sysnos++;
250             }
251         }
252         else
253         {
254             memcpy(cptr, &block_free, sizeof(block_free));
255             bf_write(p->data_BFile[dst_type], block_prev, 0, 0, cptr);
256             cptr = p->tmp_buf + no_written;
257         }
258         block_prev = block_free;
259         no_written += (int)(p->head.block_size[dst_type]) - sizeof(zint);
260         p->head.block_used[dst_type]++;
261     }
262     assert(block_prev != -1);
263     block_free = 0;
264     memcpy(cptr, &block_free, sizeof(block_free));
265     bf_write(p->data_BFile[dst_type], block_prev, 0,
266               sizeof(block_free) + (p->tmp_buf+size) - cptr, cptr);
267 }
268
269 Records rec_open(BFiles bfs, int rw, int compression_method)
270 {
271     Records p;
272     int i, r;
273     int version;
274
275     p = (Records) xmalloc(sizeof(*p));
276     p->compression_method = compression_method;
277     p->rw = rw;
278     p->tmp_size = 1024;
279     p->tmp_buf = (char *) xmalloc(p->tmp_size);
280     p->index_fname = "reci";
281     p->index_BFile = bf_open(bfs, p->index_fname, RIDX_CHUNK, rw);
282     if (p->index_BFile == NULL)
283     {
284         yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", p->index_fname);
285         exit(1);
286     }
287     r = bf_read(p->index_BFile, 0, 0, 0, p->tmp_buf);
288     switch (r)
289     {
290     case 0:
291         memcpy(p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
292         sprintf(p->head.version, "%3d", REC_VERSION);
293         p->head.index_free = 0;
294         p->head.index_last = 1;
295         p->head.no_records = 0;
296         p->head.total_bytes = 0;
297         for (i = 0; i<REC_BLOCK_TYPES; i++)
298         {
299             p->head.block_free[i] = 0;
300             p->head.block_last[i] = 1;
301             p->head.block_used[i] = 0;
302         }
303         p->head.block_size[0] = 128;
304         p->head.block_move[0] = 0;
305         for (i = 1; i<REC_BLOCK_TYPES; i++)
306         {
307             p->head.block_size[i] = p->head.block_size[i-1] * 4;
308             p->head.block_move[i] = p->head.block_size[i] * 24;
309         }
310         if (rw)
311             rec_write_head(p);
312         break;
313     case 1:
314         memcpy(&p->head, p->tmp_buf, sizeof(p->head));
315         if (memcmp(p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
316         {
317             yaz_log(YLOG_FATAL, "file %s has bad format", p->index_fname);
318             exit(1);
319         }
320         version = atoi(p->head.version);
321         if (version != REC_VERSION)
322         {
323             yaz_log(YLOG_FATAL, "file %s is version %d, but version"
324                   " %d is required", p->index_fname, version, REC_VERSION);
325             exit(1);
326         }
327         break;
328     }
329     for (i = 0; i<REC_BLOCK_TYPES; i++)
330     {
331         char str[80];
332         sprintf(str, "recd%c", i + 'A');
333         p->data_fname[i] = (char *) xmalloc(strlen(str)+1);
334         strcpy(p->data_fname[i], str);
335         p->data_BFile[i] = NULL;
336     }
337     for (i = 0; i<REC_BLOCK_TYPES; i++)
338     {
339         if (!(p->data_BFile[i] = bf_open(bfs, p->data_fname[i],
340                                          (int) (p->head.block_size[i]),
341                                          rw)))
342         {
343             yaz_log(YLOG_FATAL|YLOG_ERRNO, "bf_open %s", p->data_fname[i]);
344             exit(1);
345         }
346     }
347     p->cache_max = 400;
348     p->cache_cur = 0;
349     p->record_cache = (struct record_cache_entry *)
350         xmalloc(sizeof(*p->record_cache)*p->cache_max);
351     zebra_mutex_init(&p->mutex);
352     return p;
353 }
354
355 static void rec_encode_unsigned(unsigned n, unsigned char *buf, int *len)
356 {
357     (*len) = 0;
358     while (n > 127)
359     {
360         buf[*len] = 128 + (n & 127);
361         n = n >> 7;
362         (*len)++;
363     }
364     buf[*len] = n;
365     (*len)++;
366 }
367
368 static void rec_decode_unsigned(unsigned *np, unsigned char *buf, int *len)
369 {
370     unsigned n = 0;
371     unsigned w = 1;
372     (*len) = 0;
373
374     while (buf[*len] > 127)
375     {
376         n += w*(buf[*len] & 127);
377         w = w << 7;
378         (*len)++;
379     }
380     n += w * buf[*len];
381     (*len)++;
382     *np = n;
383 }
384
385 static void rec_encode_zint(zint n, unsigned char *buf, int *len)
386 {
387     (*len) = 0;
388     while (n > 127)
389     {
390         buf[*len] = (unsigned) (128 + (n & 127));
391         n = n >> 7;
392         (*len)++;
393     }
394     buf[*len] = (unsigned) n;
395     (*len)++;
396 }
397
398 static void rec_decode_zint(zint *np, unsigned char *buf, int *len)
399 {
400     zint  n = 0;
401     zint w = 1;
402     (*len) = 0;
403
404     while (buf[*len] > 127)
405     {
406         n += w*(buf[*len] & 127);
407         w = w << 7;
408         (*len)++;
409     }
410     n += w * buf[*len];
411     (*len)++;
412     *np = n;
413 }
414
415 static void rec_cache_flush_block1(Records p, Record rec, Record last_rec,
416                                    char **out_buf, int *out_size,
417                                    int *out_offset)
418 {
419     int i;
420     int len;
421
422     for (i = 0; i<REC_NO_INFO; i++)
423     {
424         if (*out_offset + (int) rec->size[i] + 20 > *out_size)
425         {
426             int new_size = *out_offset + rec->size[i] + 65536;
427             char *np = (char *) xmalloc(new_size);
428             if (*out_offset)
429                 memcpy(np, *out_buf, *out_offset);
430             xfree(*out_buf);
431             *out_size = new_size;
432             *out_buf = np;
433         }
434         if (i == 0)
435         {
436             rec_encode_zint(rec_sysno_to_int(rec->sysno), 
437                             *out_buf + *out_offset, &len);
438             (*out_offset) += len;
439         }
440         if (rec->size[i] == 0)
441         {
442             rec_encode_unsigned(1, *out_buf + *out_offset, &len);
443             (*out_offset) += len;
444         }
445         else if (last_rec && rec->size[i] == last_rec->size[i] &&
446                  !memcmp(rec->info[i], last_rec->info[i], rec->size[i]))
447         {
448             rec_encode_unsigned(0, *out_buf + *out_offset, &len);
449             (*out_offset) += len;
450         }
451         else
452         {
453             rec_encode_unsigned(rec->size[i]+1, *out_buf + *out_offset, &len);
454             (*out_offset) += len;
455             memcpy(*out_buf + *out_offset, rec->info[i], rec->size[i]);
456             (*out_offset) += rec->size[i];
457         }
458     }
459 }
460
461 static void rec_write_multiple(Records p, int saveCount)
462 {
463     int i;
464     short ref_count = 0;
465     char compression_method;
466     Record last_rec = 0;
467     int out_size = 1000;
468     int out_offset = 0;
469     char *out_buf = (char *) xmalloc(out_size);
470     SYSNO *sysnos = (SYSNO *) xmalloc(sizeof(*sysnos) * (p->cache_cur + 1));
471     SYSNO *sysnop = sysnos;
472
473     for (i = 0; i<p->cache_cur - saveCount; i++)
474     {
475         struct record_cache_entry *e = p->record_cache + i;
476         switch (e->flag)
477         {
478         case recordFlagNew:
479             rec_cache_flush_block1(p, e->rec, last_rec, &out_buf,
480                                     &out_size, &out_offset);
481             *sysnop++ = rec_sysno_to_int(e->rec->sysno);
482             ref_count++;
483             e->flag = recordFlagNop;
484             last_rec = e->rec;
485             break;
486         case recordFlagWrite:
487             rec_release_blocks(p, rec_sysno_to_int(e->rec->sysno));
488             rec_cache_flush_block1(p, e->rec, last_rec, &out_buf,
489                                     &out_size, &out_offset);
490             *sysnop++ = rec_sysno_to_int(e->rec->sysno);
491             ref_count++;
492             e->flag = recordFlagNop;
493             last_rec = e->rec;
494             break;
495         case recordFlagDelete:
496             rec_delete_single(p, e->rec);
497             e->flag = recordFlagNop;
498             break;
499         default:
500             break;
501         }
502     }
503
504     *sysnop = -1;
505     if (ref_count)
506     {
507         int csize = 0;  /* indicate compression "not performed yet" */
508         compression_method = p->compression_method;
509         switch (compression_method)
510         {
511         case REC_COMPRESS_BZIP2:
512 #if HAVE_BZLIB_H        
513             csize = out_offset + (out_offset >> 6) + 620;
514             rec_tmp_expand(p, csize);
515 #ifdef BZ_CONFIG_ERROR
516             i = BZ2_bzBuffToBuffCompress 
517 #else
518             i = bzBuffToBuffCompress 
519 #endif
520                                     (p->tmp_buf+sizeof(zint)+sizeof(short)+
521                                       sizeof(char),
522                                       &csize, out_buf, out_offset, 1, 0, 30);
523             if (i != BZ_OK)
524             {
525                 yaz_log(YLOG_WARN, "bzBuffToBuffCompress error code=%d", i);
526                 csize = 0;
527             }
528             yaz_log(YLOG_LOG, "compress %4d %5d %5d", ref_count, out_offset,
529                   csize);
530 #endif
531             break;
532         case REC_COMPRESS_NONE:
533             break;
534         }
535         if (!csize)  
536         {
537             /* either no compression or compression not supported ... */
538             csize = out_offset;
539             rec_tmp_expand(p, csize);
540             memcpy(p->tmp_buf + sizeof(zint) + sizeof(short) + sizeof(char),
541                     out_buf, out_offset);
542             csize = out_offset;
543             compression_method = REC_COMPRESS_NONE;
544         }
545         memcpy(p->tmp_buf + sizeof(zint), &ref_count, sizeof(ref_count));
546         memcpy(p->tmp_buf + sizeof(zint)+sizeof(short),
547                 &compression_method, sizeof(compression_method));
548                 
549         /* -------- compression */
550         rec_write_tmp_buf(p, csize + sizeof(short) + sizeof(char), sysnos);
551     }
552     xfree(out_buf);
553     xfree(sysnos);
554 }
555
556 static void rec_cache_flush(Records p, int saveCount)
557 {
558     int i, j;
559
560     if (saveCount >= p->cache_cur)
561         saveCount = 0;
562
563     rec_write_multiple(p, saveCount);
564
565     for (i = 0; i<p->cache_cur - saveCount; i++)
566     {
567         struct record_cache_entry *e = p->record_cache + i;
568         rec_rm(&e->rec);
569     } 
570     /* i still being used ... */
571     for (j = 0; j<saveCount; j++, i++)
572         memcpy(p->record_cache+j, p->record_cache+i,
573                 sizeof(*p->record_cache));
574     p->cache_cur = saveCount;
575 }
576
577 static Record *rec_cache_lookup(Records p, SYSNO sysno,
578                                 enum recordCacheFlag flag)
579 {
580     int i;
581     for (i = 0; i<p->cache_cur; i++)
582     {
583         struct record_cache_entry *e = p->record_cache + i;
584         if (e->rec->sysno == sysno)
585         {
586             if (flag != recordFlagNop && e->flag == recordFlagNop)
587                 e->flag = flag;
588             return &e->rec;
589         }
590     }
591     return NULL;
592 }
593
594 static void rec_cache_insert(Records p, Record rec, enum recordCacheFlag flag)
595 {
596     struct record_cache_entry *e;
597
598     if (p->cache_cur == p->cache_max)
599         rec_cache_flush(p, 1);
600     else if (p->cache_cur > 0)
601     {
602         int i, j;
603         int used = 0;
604         for (i = 0; i<p->cache_cur; i++)
605         {
606             Record r = (p->record_cache + i)->rec;
607             for (j = 0; j<REC_NO_INFO; j++)
608                 used += r->size[j];
609         }
610         if (used > 90000)
611             rec_cache_flush(p, 1);
612     }
613     assert(p->cache_cur < p->cache_max);
614
615     e = p->record_cache + (p->cache_cur)++;
616     e->flag = flag;
617     e->rec = rec_cp(rec);
618 }
619
620 void rec_close(Records *pp)
621 {
622     Records p = *pp;
623     int i;
624
625     assert(p);
626
627     zebra_mutex_destroy(&p->mutex);
628     rec_cache_flush(p, 0);
629     xfree(p->record_cache);
630
631     if (p->rw)
632         rec_write_head(p);
633
634     if (p->index_BFile)
635         bf_close(p->index_BFile);
636
637     for (i = 0; i<REC_BLOCK_TYPES; i++)
638     {
639         if (p->data_BFile[i])
640             bf_close(p->data_BFile[i]);
641         xfree(p->data_fname[i]);
642     }
643     xfree(p->tmp_buf);
644     xfree(p);
645     *pp = NULL;
646 }
647
648 static Record rec_get_int(Records p, SYSNO sysno)
649 {
650     int i, in_size, r;
651     Record rec, *recp;
652     struct record_index_entry entry;
653     zint freeblock;
654     int dst_type;
655     char *nptr, *cptr;
656     char *in_buf = 0;
657     char *bz_buf = 0;
658 #if HAVE_BZLIB_H
659     int bz_size;
660 #endif
661     char compression_method;
662
663     assert(sysno > 0);
664     assert(p);
665
666     if ((recp = rec_cache_lookup(p, sysno, recordFlagNop)))
667         return rec_cp(*recp);
668
669     if (read_indx(p, rec_sysno_to_int(sysno), &entry, sizeof(entry), 1) < 1)
670         return NULL;       /* record is not there! */
671
672     if (!entry.size)
673         return NULL;       /* record is deleted */
674
675     dst_type = (int) (entry.next & 7);
676     assert(dst_type < REC_BLOCK_TYPES);
677     freeblock = entry.next / 8;
678
679     assert(freeblock > 0);
680     
681     rec_tmp_expand(p, entry.size);
682
683     cptr = p->tmp_buf;
684     r = bf_read(p->data_BFile[dst_type], freeblock, 0, 0, cptr);
685     if (r < 0)
686         return 0;
687     memcpy(&freeblock, cptr, sizeof(freeblock));
688
689     while (freeblock)
690     {
691         zint tmp;
692
693         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
694         
695         memcpy(&tmp, cptr, sizeof(tmp));
696         r = bf_read(p->data_BFile[dst_type], freeblock, 0, 0, cptr);
697         if (r < 0)
698             return 0;
699         memcpy(&freeblock, cptr, sizeof(freeblock));
700         memcpy(cptr, &tmp, sizeof(tmp));
701     }
702
703     rec = (Record) xmalloc(sizeof(*rec));
704     rec->sysno = sysno;
705     memcpy(&compression_method, p->tmp_buf + sizeof(zint) + sizeof(short),
706             sizeof(compression_method));
707     in_buf = p->tmp_buf + sizeof(zint) + sizeof(short) + sizeof(char);
708     in_size = entry.size - sizeof(short) - sizeof(char);
709     switch (compression_method)
710     {
711     case REC_COMPRESS_BZIP2:
712 #if HAVE_BZLIB_H
713         bz_size = entry.size * 20 + 100;
714         while (1)
715         {
716             bz_buf = (char *) xmalloc(bz_size);
717 #ifdef BZ_CONFIG_ERROR
718             i = BZ2_bzBuffToBuffDecompress
719 #else
720             i = bzBuffToBuffDecompress
721 #endif
722                 (bz_buf, &bz_size, in_buf, in_size, 0, 0);
723             yaz_log(YLOG_LOG, "decompress %5d %5d", in_size, bz_size);
724             if (i == BZ_OK)
725                 break;
726             yaz_log(YLOG_LOG, "failed");
727             xfree(bz_buf);
728             bz_size *= 2;
729         }
730         in_buf = bz_buf;
731         in_size = bz_size;
732 #else
733         yaz_log(YLOG_FATAL, "cannot decompress record(s) in BZIP2 format");
734         exit(1);
735 #endif
736         break;
737     case REC_COMPRESS_NONE:
738         break;
739     }
740     for (i = 0; i<REC_NO_INFO; i++)
741         rec->info[i] = 0;
742
743     nptr = in_buf;                /* skip ref count */
744     while (nptr < in_buf + in_size)
745     {
746         zint this_sysno;
747         int len;
748         rec_decode_zint(&this_sysno, nptr, &len);
749         nptr += len;
750
751         for (i = 0; i < REC_NO_INFO; i++)
752         {
753             int this_size;
754             rec_decode_unsigned(&this_size, nptr, &len);
755             nptr += len;
756
757             if (this_size == 0)
758                 continue;
759             rec->size[i] = this_size-1;
760
761             if (rec->size[i])
762             {
763                 rec->info[i] = nptr;
764                 nptr += rec->size[i];
765             }
766             else
767                 rec->info[i] = NULL;
768         }
769         if (this_sysno == rec_sysno_to_int(sysno))
770             break;
771     }
772     for (i = 0; i<REC_NO_INFO; i++)
773     {
774         if (rec->info[i] && rec->size[i])
775         {
776             char *np = xmalloc(rec->size[i]+1);
777             memcpy(np, rec->info[i], rec->size[i]);
778             np[rec->size[i]] = '\0';
779             rec->info[i] = np;
780         }
781         else
782         {
783             assert(rec->info[i] == 0);
784             assert(rec->size[i] == 0);
785         }
786     }
787     xfree(bz_buf);
788     rec_cache_insert(p, rec, recordFlagNop);
789     return rec;
790 }
791
792 Record rec_get(Records p, SYSNO sysno)
793 {
794     Record rec;
795     zebra_mutex_lock(&p->mutex);
796
797     rec = rec_get_int(p, sysno);
798     zebra_mutex_unlock(&p->mutex);
799     return rec;
800 }
801
802 Record rec_get_root(Records p)
803 {
804     return rec_get(p, rec_sysno_to_ext(1));
805 }
806
807 static Record rec_new_int(Records p)
808 {
809     int i;
810     SYSNO sysno;
811     Record rec;
812
813     assert(p);
814     rec = (Record) xmalloc(sizeof(*rec));
815     if (1 || p->head.index_free == 0)
816         sysno = (p->head.index_last)++;
817     else
818     {
819         struct record_index_entry entry;
820
821         read_indx(p, p->head.index_free, &entry, sizeof(entry), 0);
822         sysno = p->head.index_free;
823         p->head.index_free = entry.next;
824     }
825     (p->head.no_records)++;
826     rec->sysno = rec_sysno_to_ext(sysno);
827     for (i = 0; i < REC_NO_INFO; i++)
828     {
829         rec->info[i] = NULL;
830         rec->size[i] = 0;
831     }
832     rec_cache_insert(p, rec, recordFlagNew);
833     return rec;
834 }
835
836 Record rec_new(Records p)
837 {
838     Record rec;
839     zebra_mutex_lock(&p->mutex);
840
841     rec = rec_new_int(p);
842     zebra_mutex_unlock(&p->mutex);
843     return rec;
844 }
845
846 void rec_del(Records p, Record *recpp)
847 {
848     Record *recp;
849
850     zebra_mutex_lock(&p->mutex);
851     (p->head.no_records)--;
852     if ((recp = rec_cache_lookup(p, (*recpp)->sysno, recordFlagDelete)))
853     {
854         rec_rm(recp);
855         *recp = *recpp;
856     }
857     else
858     {
859         rec_cache_insert(p, *recpp, recordFlagDelete);
860         rec_rm(recpp);
861     }
862     zebra_mutex_unlock(&p->mutex);
863     *recpp = NULL;
864 }
865
866 void rec_put(Records p, Record *recpp)
867 {
868     Record *recp;
869
870     zebra_mutex_lock(&p->mutex);
871     if ((recp = rec_cache_lookup(p, (*recpp)->sysno, recordFlagWrite)))
872     {
873         rec_rm(recp);
874         *recp = *recpp;
875     }
876     else
877     {
878         rec_cache_insert(p, *recpp, recordFlagWrite);
879         rec_rm(recpp);
880     }
881     zebra_mutex_unlock(&p->mutex);
882     *recpp = NULL;
883 }
884
885 void rec_rm(Record *recpp)
886 {
887     int i;
888
889     if (!*recpp)
890         return ;
891     for (i = 0; i < REC_NO_INFO; i++)
892         xfree((*recpp)->info[i]);
893     xfree(*recpp);
894     *recpp = NULL;
895 }
896
897 Record rec_cp(Record rec)
898 {
899     Record n;
900     int i;
901
902     n = (Record) xmalloc(sizeof(*n));
903     n->sysno = rec->sysno;
904     for (i = 0; i < REC_NO_INFO; i++)
905         if (!rec->info[i])
906         {
907             n->info[i] = NULL;
908             n->size[i] = 0;
909         }
910         else
911         {
912             n->size[i] = rec->size[i];
913             n->info[i] = (char *) xmalloc(rec->size[i]);
914             memcpy(n->info[i], rec->info[i], rec->size[i]);
915         }
916     return n;
917 }
918
919
920 char *rec_strdup(const char *s, size_t *len)
921 {
922     char *p;
923
924     if (!s)
925     {
926         *len = 0;
927         return NULL;
928     }
929     *len = strlen(s)+1;
930     p = (char *) xmalloc(*len);
931     strcpy(p, s);
932     return p;
933 }
934