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