WS changes for function calls.
[idzebra-moved-to-github.git] / index / recindex.c
1 /* $Id: recindex.c,v 1.56 2007-01-15 15:10:17 adam Exp $
2    Copyright (C) 1995-2007
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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
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 zint rec_sysno_to_ext(zint sysno)
65 {
66     assert(sysno >= 0 && sysno <= USUAL_RANGE);
67     return sysno + FAKE_OFFSET;
68 }
69
70 zint rec_sysno_to_int(zint sysno)
71 {
72     assert(sysno >= FAKE_OFFSET && sysno <= FAKE_OFFSET + USUAL_RANGE);
73     return sysno - FAKE_OFFSET;
74 }
75
76 static ZEBRA_RES 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         return ZEBRA_FAIL;
88     }
89     return ZEBRA_OK;
90 }
91
92 static void rec_tmp_expand(Records p, int size)
93 {
94     if (p->tmp_size < size + 2048 ||
95         p->tmp_size < p->head.block_size[REC_BLOCK_TYPES-1]*2)
96     {
97         xfree(p->tmp_buf);
98         p->tmp_size = size + (int)
99                         (p->head.block_size[REC_BLOCK_TYPES-1])*2 + 2048;
100         p->tmp_buf = (char *) xmalloc(p->tmp_size);
101     }
102 }
103
104 static int read_indx(Records p, zint sysno, void *buf, int itemsize, 
105                       int ignoreError)
106 {
107     int r;
108     zint pos = (sysno-1)*itemsize;
109     int off = CAST_ZINT_TO_INT(pos%RIDX_CHUNK);
110     int sz1 = RIDX_CHUNK - off;    /* sz1 is size of buffer to read.. */
111
112     if (sz1 > itemsize)
113         sz1 = itemsize;  /* no more than itemsize bytes */
114
115     r = bf_read(p->index_BFile, 1+pos/RIDX_CHUNK, off, sz1, buf);
116     if (r == 1 && sz1 < itemsize) /* boundary? - must read second part */
117         r = bf_read(p->index_BFile, 2+pos/RIDX_CHUNK, 0, itemsize - sz1,
118                         (char*) buf + sz1);
119     if (r != 1 && !ignoreError)
120     {
121         yaz_log(YLOG_FATAL|YLOG_ERRNO, "read in %s at pos %ld",
122                 p->index_fname, (long) pos);
123     }
124     return r;
125 }
126
127 static void write_indx(Records p, zint sysno, void *buf, int itemsize)
128 {
129     zint pos = (sysno-1)*itemsize;
130     int off = CAST_ZINT_TO_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 ZEBRA_RES rec_release_blocks(Records p, zint 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 ZEBRA_FAIL;
152
153     freeblock = entry.next;
154     assert(freeblock > 0);
155     dst_type = CAST_ZINT_TO_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             return ZEBRA_FAIL;
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                     return ZEBRA_FAIL;
180                 }
181                 return ZEBRA_OK;
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             return ZEBRA_FAIL;
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     return ZEBRA_OK;
199 }
200
201 static ZEBRA_RES rec_delete_single(Records p, Record rec)
202 {
203     struct record_index_entry entry;
204
205     /* all data in entry must be reset, since it's written verbatim */
206     memset(&entry, '\0', sizeof(entry));
207     if (rec_release_blocks(p, rec_sysno_to_int(rec->sysno)) != ZEBRA_OK)
208         return ZEBRA_FAIL;
209
210     entry.next = p->head.index_free;
211     entry.size = 0;
212     p->head.index_free = rec_sysno_to_int(rec->sysno);
213     write_indx(p, rec_sysno_to_int(rec->sysno), &entry, sizeof(entry));
214     return ZEBRA_OK;
215 }
216
217 static ZEBRA_RES rec_write_tmp_buf(Records p, int size, zint *sysnos)
218 {
219     struct record_index_entry entry;
220     int no_written = 0;
221     char *cptr = p->tmp_buf;
222     zint block_prev = -1, block_free;
223     int dst_type = 0;
224     int i;
225
226     /* all data in entry must be reset, since it's written verbatim */
227     memset(&entry, '\0', sizeof(entry));
228
229     for (i = 1; i<REC_BLOCK_TYPES; i++)
230         if (size >= p->head.block_move[i])
231             dst_type = i;
232     while (no_written < size)
233     {
234         block_free = p->head.block_free[dst_type];
235         if (block_free)
236         {
237             if (bf_read(p->data_BFile[dst_type],
238                          block_free, 0, sizeof(*p->head.block_free),
239                          &p->head.block_free[dst_type]) != 1)
240             {
241                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read in %s at free block "
242                          ZINT_FORMAT,
243                          p->data_fname[dst_type], block_free);
244                 return ZEBRA_FAIL;
245             }
246         }
247         else
248             block_free = p->head.block_last[dst_type]++;
249         if (block_prev == -1)
250         {
251             entry.next = block_free*8 + dst_type;
252             entry.size = size;
253             p->head.total_bytes += size;
254             while (*sysnos > 0)
255             {
256                 write_indx(p, *sysnos, &entry, sizeof(entry));
257                 sysnos++;
258             }
259         }
260         else
261         {
262             memcpy(cptr, &block_free, sizeof(block_free));
263             bf_write(p->data_BFile[dst_type], block_prev, 0, 0, cptr);
264             cptr = p->tmp_buf + no_written;
265         }
266         block_prev = block_free;
267         no_written += CAST_ZINT_TO_INT(p->head.block_size[dst_type]) 
268             - sizeof(zint);
269         p->head.block_used[dst_type]++;
270     }
271     assert(block_prev != -1);
272     block_free = 0;
273     memcpy(cptr, &block_free, sizeof(block_free));
274     bf_write(p->data_BFile[dst_type], block_prev, 0,
275               sizeof(block_free) + (p->tmp_buf+size) - cptr, cptr);
276     return ZEBRA_OK;
277 }
278
279 Records rec_open(BFiles bfs, int rw, int compression_method)
280 {
281     Records p;
282     int i, r;
283     int version;
284     ZEBRA_RES ret = ZEBRA_OK;
285
286     p = (Records) xmalloc(sizeof(*p));
287     memset(&p->head, '\0', sizeof(p->head));
288     p->compression_method = compression_method;
289     p->rw = rw;
290     p->tmp_size = 1024;
291     p->index_fname = "reci";
292     p->index_BFile = bf_open(bfs, p->index_fname, RIDX_CHUNK, rw);
293     if (p->index_BFile == NULL)
294     {
295         yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", p->index_fname);
296         xfree(p);
297         return 0;
298     }
299     p->tmp_buf = (char *) xmalloc(p->tmp_size);
300     r = bf_read(p->index_BFile, 0, 0, 0, p->tmp_buf);
301     switch (r)
302     {
303     case 0:
304         memcpy(p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
305         sprintf(p->head.version, "%3d", REC_VERSION);
306         p->head.index_free = 0;
307         p->head.index_last = 1;
308         p->head.no_records = 0;
309         p->head.total_bytes = 0;
310         for (i = 0; i<REC_BLOCK_TYPES; i++)
311         {
312             p->head.block_free[i] = 0;
313             p->head.block_last[i] = 1;
314             p->head.block_used[i] = 0;
315         }
316         p->head.block_size[0] = 128;
317         p->head.block_move[0] = 0;
318         for (i = 1; i<REC_BLOCK_TYPES; i++)
319         {
320             p->head.block_size[i] = p->head.block_size[i-1] * 4;
321             p->head.block_move[i] = p->head.block_size[i] * 24;
322         }
323         if (rw)
324         {
325             if (rec_write_head(p) != ZEBRA_OK)
326                 ret = ZEBRA_FAIL;
327         }
328         break;
329     case 1:
330         memcpy(&p->head, p->tmp_buf, sizeof(p->head));
331         if (memcmp(p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
332         {
333             yaz_log(YLOG_FATAL, "file %s has bad format", p->index_fname);
334             ret = ZEBRA_FAIL;
335         }
336         version = atoi(p->head.version);
337         if (version != REC_VERSION)
338         {
339             yaz_log(YLOG_FATAL, "file %s is version %d, but version"
340                   " %d is required", p->index_fname, version, REC_VERSION);
341             ret = ZEBRA_FAIL;
342         }
343         break;
344     }
345     for (i = 0; i<REC_BLOCK_TYPES; i++)
346     {
347         char str[80];
348         sprintf(str, "recd%c", i + 'A');
349         p->data_fname[i] = (char *) xmalloc(strlen(str)+1);
350         strcpy(p->data_fname[i], str);
351         p->data_BFile[i] = NULL;
352     }
353     for (i = 0; i<REC_BLOCK_TYPES; i++)
354     {
355         if (!(p->data_BFile[i] =
356               bf_open(bfs, p->data_fname[i],
357                       CAST_ZINT_TO_INT(p->head.block_size[i]), rw)))
358         {
359             yaz_log(YLOG_FATAL|YLOG_ERRNO, "bf_open %s", p->data_fname[i]);
360             ret = ZEBRA_FAIL;
361             break;
362         }
363     }
364     p->cache_max = 400;
365     p->cache_cur = 0;
366     p->record_cache = (struct record_cache_entry *)
367         xmalloc(sizeof(*p->record_cache)*p->cache_max);
368     zebra_mutex_init(&p->mutex);
369     if (ret == ZEBRA_FAIL)
370         rec_close(&p);
371     return p;
372 }
373
374 static void rec_encode_unsigned(unsigned n, unsigned char *buf, int *len)
375 {
376     (*len) = 0;
377     while (n > 127)
378     {
379         buf[*len] = 128 + (n & 127);
380         n = n >> 7;
381         (*len)++;
382     }
383     buf[*len] = n;
384     (*len)++;
385 }
386
387 static void rec_decode_unsigned(unsigned *np, unsigned char *buf, int *len)
388 {
389     unsigned n = 0;
390     unsigned w = 1;
391     (*len) = 0;
392
393     while (buf[*len] > 127)
394     {
395         n += w*(buf[*len] & 127);
396         w = w << 7;
397         (*len)++;
398     }
399     n += w * buf[*len];
400     (*len)++;
401     *np = n;
402 }
403
404 static void rec_encode_zint(zint n, unsigned char *buf, int *len)
405 {
406     (*len) = 0;
407     while (n > 127)
408     {
409         buf[*len] = (unsigned) (128 + (n & 127));
410         n = n >> 7;
411         (*len)++;
412     }
413     buf[*len] = (unsigned) n;
414     (*len)++;
415 }
416
417 static void rec_decode_zint(zint *np, unsigned char *buf, int *len)
418 {
419     zint  n = 0;
420     zint w = 1;
421     (*len) = 0;
422
423     while (buf[*len] > 127)
424     {
425         n += w*(buf[*len] & 127);
426         w = w << 7;
427         (*len)++;
428     }
429     n += w * buf[*len];
430     (*len)++;
431     *np = n;
432 }
433
434 static void rec_cache_flush_block1(Records p, Record rec, Record last_rec,
435                                    char **out_buf, int *out_size,
436                                    int *out_offset)
437 {
438     int i;
439     int len;
440
441     for (i = 0; i<REC_NO_INFO; i++)
442     {
443         if (*out_offset + CAST_ZINT_TO_INT(rec->size[i]) + 20 > *out_size)
444         {
445             int new_size = *out_offset + rec->size[i] + 65536;
446             char *np = (char *) xmalloc(new_size);
447             if (*out_offset)
448                 memcpy(np, *out_buf, *out_offset);
449             xfree(*out_buf);
450             *out_size = new_size;
451             *out_buf = np;
452         }
453         if (i == 0)
454         {
455             rec_encode_zint(rec_sysno_to_int(rec->sysno), 
456                             (unsigned char *) *out_buf + *out_offset, &len);
457             (*out_offset) += len;
458         }
459         if (rec->size[i] == 0)
460         {
461             rec_encode_unsigned(1, (unsigned char *) *out_buf + *out_offset,
462                                 &len);
463             (*out_offset) += len;
464         }
465         else if (last_rec && rec->size[i] == last_rec->size[i] &&
466                  !memcmp(rec->info[i], last_rec->info[i], rec->size[i]))
467         {
468             rec_encode_unsigned(0, (unsigned char *) *out_buf + *out_offset,
469                                 &len);
470             (*out_offset) += len;
471         }
472         else
473         {
474             rec_encode_unsigned(rec->size[i]+1,
475                                 (unsigned char *) *out_buf + *out_offset,
476                                 &len);
477             (*out_offset) += len;
478             memcpy(*out_buf + *out_offset, rec->info[i], rec->size[i]);
479             (*out_offset) += rec->size[i];
480         }
481     }
482 }
483
484 static ZEBRA_RES rec_write_multiple(Records p, int saveCount)
485 {
486     int i;
487     short ref_count = 0;
488     char compression_method;
489     Record last_rec = 0;
490     int out_size = 1000;
491     int out_offset = 0;
492     char *out_buf = (char *) xmalloc(out_size);
493     zint *sysnos = (zint *) xmalloc(sizeof(*sysnos) * (p->cache_cur + 1));
494     zint *sysnop = sysnos;
495     ZEBRA_RES ret = ZEBRA_OK;
496
497     for (i = 0; i<p->cache_cur - saveCount; i++)
498     {
499         struct record_cache_entry *e = p->record_cache + i;
500         switch (e->flag)
501         {
502         case recordFlagNew:
503             rec_cache_flush_block1(p, e->rec, last_rec, &out_buf,
504                                     &out_size, &out_offset);
505             *sysnop++ = rec_sysno_to_int(e->rec->sysno);
506             ref_count++;
507             e->flag = recordFlagNop;
508             last_rec = e->rec;
509             break;
510         case recordFlagWrite:
511             if (rec_release_blocks(p, rec_sysno_to_int(e->rec->sysno))
512                 != ZEBRA_OK)
513                 ret = ZEBRA_FAIL;
514
515             rec_cache_flush_block1(p, e->rec, last_rec, &out_buf,
516                                     &out_size, &out_offset);
517             *sysnop++ = rec_sysno_to_int(e->rec->sysno);
518             ref_count++;
519             e->flag = recordFlagNop;
520             last_rec = e->rec;
521             break;
522         case recordFlagDelete:
523             if (rec_delete_single(p, e->rec) != ZEBRA_OK)
524                 ret = ZEBRA_FAIL;
525
526             e->flag = recordFlagNop;
527             break;
528         default:
529             break;
530         }
531     }
532
533     *sysnop = -1;
534     if (ref_count)
535     {
536         unsigned int csize = 0;  /* indicate compression "not performed yet" */
537         compression_method = p->compression_method;
538         switch (compression_method)
539         {
540         case REC_COMPRESS_BZIP2:
541 #if HAVE_BZLIB_H        
542             csize = out_offset + (out_offset >> 6) + 620;
543             rec_tmp_expand(p, csize);
544 #ifdef BZ_CONFIG_ERROR
545             i = BZ2_bzBuffToBuffCompress 
546 #else
547             i = bzBuffToBuffCompress 
548 #endif
549                                     (p->tmp_buf+sizeof(zint)+sizeof(short)+
550                                       sizeof(char),
551                                       &csize, out_buf, out_offset, 1, 0, 30);
552             if (i != BZ_OK)
553             {
554                 yaz_log(YLOG_WARN, "bzBuffToBuffCompress error code=%d", i);
555                 csize = 0;
556             }
557             yaz_log(YLOG_LOG, "compress %4d %5d %5d", ref_count, out_offset,
558                   csize);
559 #endif
560             break;
561         case REC_COMPRESS_NONE:
562             break;
563         }
564         if (!csize)  
565         {
566             /* either no compression or compression not supported ... */
567             csize = out_offset;
568             rec_tmp_expand(p, csize);
569             memcpy(p->tmp_buf + sizeof(zint) + sizeof(short) + sizeof(char),
570                     out_buf, out_offset);
571             csize = out_offset;
572             compression_method = REC_COMPRESS_NONE;
573         }
574         memcpy(p->tmp_buf + sizeof(zint), &ref_count, sizeof(ref_count));
575         memcpy(p->tmp_buf + sizeof(zint)+sizeof(short),
576                 &compression_method, sizeof(compression_method));
577                 
578         /* -------- compression */
579         if (rec_write_tmp_buf(p, csize + sizeof(short) + sizeof(char), sysnos)
580             != ZEBRA_OK)
581             ret = ZEBRA_FAIL;
582     }
583     xfree(out_buf);
584     xfree(sysnos);
585     return ret;
586 }
587
588 static ZEBRA_RES rec_cache_flush(Records p, int saveCount)
589 {
590     int i, j;
591     ZEBRA_RES ret;
592
593     if (saveCount >= p->cache_cur)
594         saveCount = 0;
595
596     ret = rec_write_multiple(p, saveCount);
597
598     for (i = 0; i<p->cache_cur - saveCount; i++)
599     {
600         struct record_cache_entry *e = p->record_cache + i;
601         rec_free(&e->rec);
602     } 
603     /* i still being used ... */
604     for (j = 0; j<saveCount; j++, i++)
605         memcpy(p->record_cache+j, p->record_cache+i,
606                 sizeof(*p->record_cache));
607     p->cache_cur = saveCount;
608     return ret;
609 }
610
611 static Record *rec_cache_lookup(Records p, zint sysno,
612                                 enum recordCacheFlag flag)
613 {
614     int i;
615     for (i = 0; i<p->cache_cur; i++)
616     {
617         struct record_cache_entry *e = p->record_cache + i;
618         if (e->rec->sysno == sysno)
619         {
620             if (flag != recordFlagNop && e->flag == recordFlagNop)
621                 e->flag = flag;
622             return &e->rec;
623         }
624     }
625     return NULL;
626 }
627
628 static ZEBRA_RES rec_cache_insert(Records p, Record rec, enum recordCacheFlag flag)
629 {
630     struct record_cache_entry *e;
631     ZEBRA_RES ret = ZEBRA_OK;
632
633     if (p->cache_cur == p->cache_max)
634         ret = rec_cache_flush(p, 1);
635     else if (p->cache_cur > 0)
636     {
637         int i, j;
638         int used = 0;
639         for (i = 0; i<p->cache_cur; i++)
640         {
641             Record r = (p->record_cache + i)->rec;
642             for (j = 0; j<REC_NO_INFO; j++)
643                 used += r->size[j];
644         }
645         if (used > 90000)
646             ret = rec_cache_flush(p, 1);
647     }
648     assert(p->cache_cur < p->cache_max);
649
650     e = p->record_cache + (p->cache_cur)++;
651     e->flag = flag;
652     e->rec = rec_cp(rec);
653     return ret;
654 }
655
656 ZEBRA_RES rec_close(Records *pp)
657 {
658     Records p = *pp;
659     int i;
660     ZEBRA_RES ret = ZEBRA_OK;
661
662     if (!p)
663         return ret;
664
665     zebra_mutex_destroy(&p->mutex);
666     if (rec_cache_flush(p, 0) != ZEBRA_OK)
667         ret = ZEBRA_FAIL;
668
669     xfree(p->record_cache);
670
671     if (p->rw)
672     {
673         if (rec_write_head(p) != ZEBRA_OK)
674             ret = ZEBRA_FAIL;
675     }
676
677     if (p->index_BFile)
678         bf_close(p->index_BFile);
679
680     for (i = 0; i<REC_BLOCK_TYPES; i++)
681     {
682         if (p->data_BFile[i])
683             bf_close(p->data_BFile[i]);
684         xfree(p->data_fname[i]);
685     }
686     xfree(p->tmp_buf);
687     xfree(p);
688     *pp = NULL;
689     return ret;
690 }
691
692 static Record rec_get_int(Records p, zint sysno)
693 {
694     int i, in_size, r;
695     Record rec, *recp;
696     struct record_index_entry entry;
697     zint freeblock;
698     int dst_type;
699     char *nptr, *cptr;
700     char *in_buf = 0;
701     char *bz_buf = 0;
702 #if HAVE_BZLIB_H
703     unsigned int bz_size;
704 #endif
705     char compression_method;
706
707     assert(sysno > 0);
708     assert(p);
709
710     if ((recp = rec_cache_lookup(p, sysno, recordFlagNop)))
711         return rec_cp(*recp);
712
713     if (read_indx(p, rec_sysno_to_int(sysno), &entry, sizeof(entry), 1) < 1)
714         return NULL;       /* record is not there! */
715
716     if (!entry.size)
717         return NULL;       /* record is deleted */
718
719     dst_type = (int) (entry.next & 7);
720     assert(dst_type < REC_BLOCK_TYPES);
721     freeblock = entry.next / 8;
722
723     assert(freeblock > 0);
724     
725     rec_tmp_expand(p, entry.size);
726
727     cptr = p->tmp_buf;
728     r = bf_read(p->data_BFile[dst_type], freeblock, 0, 0, cptr);
729     if (r < 0)
730         return 0;
731     memcpy(&freeblock, cptr, sizeof(freeblock));
732
733     while (freeblock)
734     {
735         zint tmp;
736
737         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
738         
739         memcpy(&tmp, cptr, sizeof(tmp));
740         r = bf_read(p->data_BFile[dst_type], freeblock, 0, 0, cptr);
741         if (r < 0)
742             return 0;
743         memcpy(&freeblock, cptr, sizeof(freeblock));
744         memcpy(cptr, &tmp, sizeof(tmp));
745     }
746
747     rec = (Record) xmalloc(sizeof(*rec));
748     rec->sysno = sysno;
749     memcpy(&compression_method, p->tmp_buf + sizeof(zint) + sizeof(short),
750             sizeof(compression_method));
751     in_buf = p->tmp_buf + sizeof(zint) + sizeof(short) + sizeof(char);
752     in_size = entry.size - sizeof(short) - sizeof(char);
753     switch (compression_method)
754     {
755     case REC_COMPRESS_BZIP2:
756 #if HAVE_BZLIB_H
757         bz_size = entry.size * 20 + 100;
758         while (1)
759         {
760             bz_buf = (char *) xmalloc(bz_size);
761 #ifdef BZ_CONFIG_ERROR
762             i = BZ2_bzBuffToBuffDecompress
763 #else
764             i = bzBuffToBuffDecompress
765 #endif
766                 (bz_buf, &bz_size, in_buf, in_size, 0, 0);
767             yaz_log(YLOG_LOG, "decompress %5d %5d", in_size, bz_size);
768             if (i == BZ_OK)
769                 break;
770             yaz_log(YLOG_LOG, "failed");
771             xfree(bz_buf);
772             bz_size *= 2;
773         }
774         in_buf = bz_buf;
775         in_size = bz_size;
776 #else
777         yaz_log(YLOG_FATAL, "cannot decompress record(s) in BZIP2 format");
778         return 0;
779 #endif
780         break;
781     case REC_COMPRESS_NONE:
782         break;
783     }
784     for (i = 0; i<REC_NO_INFO; i++)
785         rec->info[i] = 0;
786
787     nptr = in_buf;                /* skip ref count */
788     while (nptr < in_buf + in_size)
789     {
790         zint this_sysno;
791         int len;
792         rec_decode_zint(&this_sysno, (unsigned char *) nptr, &len);
793         nptr += len;
794
795         for (i = 0; i < REC_NO_INFO; i++)
796         {
797             unsigned int this_size;
798             rec_decode_unsigned(&this_size, (unsigned char *) nptr, &len);
799             nptr += len;
800
801             if (this_size == 0)
802                 continue;
803             rec->size[i] = this_size-1;
804
805             if (rec->size[i])
806             {
807                 rec->info[i] = nptr;
808                 nptr += rec->size[i];
809             }
810             else
811                 rec->info[i] = NULL;
812         }
813         if (this_sysno == rec_sysno_to_int(sysno))
814             break;
815     }
816     for (i = 0; i<REC_NO_INFO; i++)
817     {
818         if (rec->info[i] && rec->size[i])
819         {
820             char *np = xmalloc(rec->size[i]+1);
821             memcpy(np, rec->info[i], rec->size[i]);
822             np[rec->size[i]] = '\0';
823             rec->info[i] = np;
824         }
825         else
826         {
827             assert(rec->info[i] == 0);
828             assert(rec->size[i] == 0);
829         }
830     }
831     xfree(bz_buf);
832     if (rec_cache_insert(p, rec, recordFlagNop) != ZEBRA_OK)
833         return 0;
834     return rec;
835 }
836
837 Record rec_get(Records p, zint sysno)
838 {
839     Record rec;
840     zebra_mutex_lock(&p->mutex);
841
842     rec = rec_get_int(p, sysno);
843     zebra_mutex_unlock(&p->mutex);
844     return rec;
845 }
846
847 Record rec_get_root(Records p)
848 {
849     return rec_get(p, rec_sysno_to_ext(1));
850 }
851
852 static Record rec_new_int(Records p)
853 {
854     int i;
855     zint sysno;
856     Record rec;
857
858     assert(p);
859     rec = (Record) xmalloc(sizeof(*rec));
860     if (1 || p->head.index_free == 0)
861         sysno = (p->head.index_last)++;
862     else
863     {
864         struct record_index_entry entry;
865
866         if (read_indx(p, p->head.index_free, &entry, sizeof(entry), 0) < 1)
867         {
868             xfree(rec);
869             return 0;
870         }
871         sysno = p->head.index_free;
872         p->head.index_free = entry.next;
873     }
874     (p->head.no_records)++;
875     rec->sysno = rec_sysno_to_ext(sysno);
876     for (i = 0; i < REC_NO_INFO; i++)
877     {
878         rec->info[i] = NULL;
879         rec->size[i] = 0;
880     }
881     rec_cache_insert(p, rec, recordFlagNew);
882     return rec;
883 }
884
885 Record rec_new(Records p)
886 {
887     Record rec;
888     zebra_mutex_lock(&p->mutex);
889
890     rec = rec_new_int(p);
891     zebra_mutex_unlock(&p->mutex);
892     return rec;
893 }
894
895 ZEBRA_RES rec_del(Records p, Record *recpp)
896 {
897     Record *recp;
898     ZEBRA_RES ret = ZEBRA_OK;
899
900     zebra_mutex_lock(&p->mutex);
901     (p->head.no_records)--;
902     if ((recp = rec_cache_lookup(p, (*recpp)->sysno, recordFlagDelete)))
903     {
904         rec_free(recp);
905         *recp = *recpp;
906     }
907     else
908     {
909         ret = rec_cache_insert(p, *recpp, recordFlagDelete);
910         rec_free(recpp);
911     }
912     zebra_mutex_unlock(&p->mutex);
913     *recpp = NULL;
914     return ret;
915 }
916
917 ZEBRA_RES rec_put(Records p, Record *recpp)
918 {
919     Record *recp;
920     ZEBRA_RES ret = ZEBRA_OK;
921
922     zebra_mutex_lock(&p->mutex);
923     if ((recp = rec_cache_lookup(p, (*recpp)->sysno, recordFlagWrite)))
924     {
925         rec_free(recp);
926         *recp = *recpp;
927     }
928     else
929     {
930         ret = rec_cache_insert(p, *recpp, recordFlagWrite);
931         rec_free(recpp);
932     }
933     zebra_mutex_unlock(&p->mutex);
934     *recpp = NULL;
935     return ret;
936 }
937
938 void rec_free(Record *recpp)
939 {
940     int i;
941
942     if (!*recpp)
943         return ;
944     for (i = 0; i < REC_NO_INFO; i++)
945         xfree((*recpp)->info[i]);
946     xfree(*recpp);
947     *recpp = NULL;
948 }
949
950 Record rec_cp(Record rec)
951 {
952     Record n;
953     int i;
954
955     n = (Record) xmalloc(sizeof(*n));
956     n->sysno = rec->sysno;
957     for (i = 0; i < REC_NO_INFO; i++)
958         if (!rec->info[i])
959         {
960             n->info[i] = NULL;
961             n->size[i] = 0;
962         }
963         else
964         {
965             n->size[i] = rec->size[i];
966             n->info[i] = (char *) xmalloc(rec->size[i]+1);
967             memcpy(n->info[i], rec->info[i], rec->size[i]);
968             n->info[i][rec->size[i]] = '\0';
969         }
970     return n;
971 }
972
973
974 char *rec_strdup(const char *s, size_t *len)
975 {
976     char *p;
977
978     if (!s)
979     {
980         *len = 0;
981         return NULL;
982     }
983     *len = strlen(s)+1;
984     p = (char *) xmalloc(*len);
985     strcpy(p, s);
986     return p;
987 }
988
989 /*
990  * Local variables:
991  * c-basic-offset: 4
992  * indent-tabs-mode: nil
993  * End:
994  * vim: shiftwidth=4 tabstop=8 expandtab
995  */
996