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