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