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