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