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