Fixed a memory leak
[idzebra-moved-to-github.git] / isamc / isamd.c
1 /*
2  * Copyright (c) 1995-1998, Index Data.
3  * See the file LICENSE for details.
4  * Heikki Levanto
5  * 
6  * Isamd - isam with diffs 
7  *
8  */
9
10
11 #include <stdlib.h>
12 #include <assert.h>
13 #include <string.h>
14 #include <stdio.h>
15
16 #include <log.h>
17 #include "../index/index.h"  /* isamd uses the internal structure of it_key */
18 #include "isamd-p.h"
19
20 static void flush_block (ISAMD is, int cat);
21 static void release_fc (ISAMD is, int cat);
22 static void init_fc (ISAMD is, int cat);
23
24 #define ISAMD_FREELIST_CHUNK 1
25
26 #define SMALL_TEST 1
27
28 ISAMD_M isamd_getmethod (ISAMD_M me)
29 {
30     static struct ISAMD_filecat_s def_cat[] = {
31 #if SMALL_TEST
32 /*        blocksz,   max keys before switching size. Unused time being */
33         {    20,   40 },
34         {    32,    0 },
35 #else
36         {    24,    1 },
37         {    32,    1 },
38         {    64,    1 },
39         {   128,    1 },
40         {   256,    1 },
41         {  1024,    1 },
42         {  2048,    0 },
43 #endif 
44
45 /* old values from isamc, long time ago...
46         {    24,   40 },
47         {   128,  256 },
48         {   512, 1024 },
49         {  2048, 4096 },
50         {  8192,16384 },
51         { 32768,   0  },
52 */
53
54 /* assume about 2 bytes per pointer, when compressed. The head uses */
55 /* 16 bytes, and other blocks use 8 for header info... If you want 3 */
56 /* blocks of 32 bytes, say max 16+24+24 = 64 keys */
57
58     };
59     ISAMD_M m = (ISAMD_M) xmalloc (sizeof(*m));  /*??? never released??? */
60     m->filecat = def_cat;
61
62     m->code_start = NULL;
63     m->code_item = NULL;
64     m->code_stop = NULL;
65     m->code_reset = NULL;
66
67     m->compare_item = NULL;
68
69     m->debug = 1;
70
71     m->max_blocks_mem = 10;
72
73     return m;
74 }
75
76
77
78 ISAMD isamd_open (BFiles bfs, const char *name, int writeflag, ISAMD_M method)
79 {
80     ISAMD is;
81     ISAMD_filecat filecat;
82     int i = 0;
83
84     is = (ISAMD) xmalloc (sizeof(*is));
85
86     is->method = (ISAMD_M) xmalloc (sizeof(*is->method));
87     memcpy (is->method, method, sizeof(*method));
88     filecat = is->method->filecat;
89     assert (filecat);
90
91     /* determine number of block categories */
92     if (is->method->debug)
93         logf (LOG_LOG, "isamd: bsize  maxkeys");
94     do
95     {
96         if (is->method->debug)
97             logf (LOG_LOG, "isamd:%6d %6d",
98                   filecat[i].bsize, filecat[i].mblocks);
99     } while (filecat[i++].mblocks);
100     is->no_files = i;
101     is->max_cat = --i;
102  
103     assert (is->no_files > 0);
104     assert (is->max_cat <=8 ); /* we have only 3 bits for it */
105     
106     is->files = (ISAMD_file) xmalloc (sizeof(*is->files)*is->no_files);
107     if (writeflag)
108     {
109       /* TODO - what ever needs to be done here... */
110     }
111     else
112     {
113     }
114
115     for (i = 0; i<is->no_files; i++)
116     {
117         char fname[512];
118
119         sprintf (fname, "%s%c", name, i+'A');
120         is->files[i].bf = bf_open (bfs, fname, is->method->filecat[i].bsize,
121                                    writeflag);
122         is->files[i].head_is_dirty = 0;
123         if (!bf_read (is->files[i].bf, 0, 0, sizeof(ISAMD_head),
124                      &is->files[i].head))
125         {
126             is->files[i].head.lastblock = 1;
127             is->files[i].head.freelist = 0;
128         }
129         is->files[i].alloc_entries_num = 0;
130         is->files[i].alloc_entries_max =
131             is->method->filecat[i].bsize / sizeof(int) - 1;
132         is->files[i].alloc_buf = (char *)
133             xmalloc (is->method->filecat[i].bsize);
134         is->files[i].no_writes = 0; /* clear statistics */
135         is->files[i].no_reads = 0;
136         is->files[i].no_skip_writes = 0;
137         is->files[i].no_allocated = 0;
138         is->files[i].no_released = 0;
139         is->files[i].no_remap = 0;
140         is->files[i].no_forward = 0;
141         is->files[i].no_backward = 0;
142         is->files[i].sum_forward = 0;
143         is->files[i].sum_backward = 0;
144         is->files[i].no_next = 0;
145         is->files[i].no_prev = 0;
146
147         init_fc (is, i);
148     }
149     return is;
150 }
151
152 int isamd_block_used (ISAMD is, int type)
153 {
154     if (type < 0 || type >= is->no_files)
155         return -1;
156     return is->files[type].head.lastblock-1;
157 }
158
159 int isamd_block_size (ISAMD is, int type)
160 {
161     ISAMD_filecat filecat = is->method->filecat;
162     if (type < 0 || type >= is->no_files)
163         return -1;
164     return filecat[type].bsize;
165 }
166
167 int isamd_close (ISAMD is)
168 {
169     int i;
170
171     if (is->method->debug)
172     {
173         logf (LOG_LOG, "isamd:    next    forw   mid-f    prev   backw   mid-b");
174         for (i = 0; i<is->no_files; i++)
175             logf (LOG_LOG, "isamd:%8d%8d%8.1f%8d%8d%8.1f",
176                   is->files[i].no_next,
177                   is->files[i].no_forward,
178                   is->files[i].no_forward ?
179                   (double) is->files[i].sum_forward/is->files[i].no_forward
180                   : 0.0,
181                   is->files[i].no_prev,
182                   is->files[i].no_backward,
183                   is->files[i].no_backward ?
184                   (double) is->files[i].sum_backward/is->files[i].no_backward
185                   : 0.0);
186     }
187     if (is->method->debug)
188         logf (LOG_LOG, "isamd:  writes   reads skipped   alloc released  remap");
189     for (i = 0; i<is->no_files; i++)
190     {
191         release_fc (is, i);
192         assert (is->files[i].bf);
193         if (is->files[i].head_is_dirty)
194             bf_write (is->files[i].bf, 0, 0, sizeof(ISAMD_head),
195                  &is->files[i].head);
196         if (is->method->debug)
197             logf (LOG_LOG, "isamd:%8d%8d%8d%8d%8d%8d",
198                   is->files[i].no_writes,
199                   is->files[i].no_reads,
200                   is->files[i].no_skip_writes,
201                   is->files[i].no_allocated,
202                   is->files[i].no_released,
203                   is->files[i].no_remap);
204         xfree (is->files[i].fc_list);
205         flush_block (is, i);
206         bf_close (is->files[i].bf);
207     }
208     xfree (is->files);
209     xfree (is->method);
210     xfree (is);
211     return 0;
212 }
213
214 int isamd_read_block (ISAMD is, int cat, int pos, char *dst)
215 {
216     ++(is->files[cat].no_reads);
217     return bf_read (is->files[cat].bf, pos, 0, 0, dst);
218 }
219
220 int isamd_write_block (ISAMD is, int cat, int pos, char *src)
221 {
222     ++(is->files[cat].no_writes);
223     if (is->method->debug > 2)
224         logf (LOG_LOG, "isamd: write_block %d %d", cat, pos);
225     return bf_write (is->files[cat].bf, pos, 0, 0, src);
226 }
227
228 int isamd_write_dblock (ISAMD is, int cat, int pos, char *src,
229                       int nextpos, int offset)
230 {
231     ISAMD_BLOCK_SIZE size = offset + ISAMD_BLOCK_OFFSET_N;
232     if (is->method->debug > 2)
233         logf (LOG_LOG, "isamd: write_dblock. size=%d nextpos=%d",
234               (int) size, nextpos);
235     src -= ISAMD_BLOCK_OFFSET_N;
236     assert( ISAMD_BLOCK_OFFSET_N == sizeof(int)+sizeof(int) );
237     memcpy (src, &nextpos, sizeof(int));
238     memcpy (src + sizeof(int), &size, sizeof(size));
239     return isamd_write_block (is, cat, pos, src);
240 }
241
242 #if ISAMD_FREELIST_CHUNK
243 static void flush_block (ISAMD is, int cat)
244 {
245     char *abuf = is->files[cat].alloc_buf;
246     int block = is->files[cat].head.freelist;
247     if (block && is->files[cat].alloc_entries_num)
248     {
249         memcpy (abuf, &is->files[cat].alloc_entries_num, sizeof(int));
250         bf_write (is->files[cat].bf, block, 0, 0, abuf);
251         is->files[cat].alloc_entries_num = 0;
252     }
253     xfree (abuf);
254 }
255
256 static int alloc_block (ISAMD is, int cat)
257 {
258     int block = is->files[cat].head.freelist;
259     char *abuf = is->files[cat].alloc_buf;
260
261     (is->files[cat].no_allocated)++;
262
263     if (!block)
264     {
265         block = (is->files[cat].head.lastblock)++;   /* no free list */
266         is->files[cat].head_is_dirty = 1;
267     }
268     else
269     {
270         if (!is->files[cat].alloc_entries_num) /* read first time */
271         {
272             bf_read (is->files[cat].bf, block, 0, 0, abuf);
273             memcpy (&is->files[cat].alloc_entries_num, abuf,
274                     sizeof(is->files[cat].alloc_entries_num));
275             assert (is->files[cat].alloc_entries_num > 0);
276         }
277         /* have some free blocks now */
278         assert (is->files[cat].alloc_entries_num > 0);
279         is->files[cat].alloc_entries_num--;
280         if (!is->files[cat].alloc_entries_num)  /* last one in block? */
281         {
282             memcpy (&is->files[cat].head.freelist, abuf + sizeof(int),
283                     sizeof(int));
284             is->files[cat].head_is_dirty = 1;
285
286             if (is->files[cat].head.freelist)
287             {
288                 bf_read (is->files[cat].bf, is->files[cat].head.freelist,
289                          0, 0, abuf);
290                 memcpy (&is->files[cat].alloc_entries_num, abuf,
291                         sizeof(is->files[cat].alloc_entries_num));
292                 assert (is->files[cat].alloc_entries_num);
293             }
294         }
295         else
296             memcpy (&block, abuf + sizeof(int) + sizeof(int) *
297                     is->files[cat].alloc_entries_num, sizeof(int));
298     }
299     return block;
300 }
301
302 static void release_block (ISAMD is, int cat, int pos)
303 {
304     char *abuf = is->files[cat].alloc_buf;
305     int block = is->files[cat].head.freelist;
306
307     (is->files[cat].no_released)++;
308
309     if (block && !is->files[cat].alloc_entries_num) /* must read block */
310     {
311         bf_read (is->files[cat].bf, block, 0, 0, abuf);
312         memcpy (&is->files[cat].alloc_entries_num, abuf,
313                 sizeof(is->files[cat].alloc_entries_num));
314         assert (is->files[cat].alloc_entries_num > 0);
315     }
316     assert (is->files[cat].alloc_entries_num <= is->files[cat].alloc_entries_max);
317     if (is->files[cat].alloc_entries_num == is->files[cat].alloc_entries_max)
318     {
319         assert (block);
320         memcpy (abuf, &is->files[cat].alloc_entries_num, sizeof(int));
321         bf_write (is->files[cat].bf, block, 0, 0, abuf);
322         is->files[cat].alloc_entries_num = 0;
323     }
324     if (!is->files[cat].alloc_entries_num) /* make new buffer? */
325     {
326         memcpy (abuf + sizeof(int), &block, sizeof(int));
327         is->files[cat].head.freelist = pos;
328         is->files[cat].head_is_dirty = 1; 
329     }
330     else
331     {
332         memcpy (abuf + sizeof(int) +
333                 is->files[cat].alloc_entries_num*sizeof(int),
334                 &pos, sizeof(int));
335     }
336     is->files[cat].alloc_entries_num++;
337 }
338 #else
339 static void flush_block (ISAMD is, int cat)
340 {
341     char *abuf = is->files[cat].alloc_buf;
342     xfree (abuf);
343 }
344
345 static int alloc_block (ISAMD is, int cat)
346 {
347     int block;
348     char buf[sizeof(int)];
349
350     is->files[cat].head_is_dirty = 1;
351     (is->files[cat].no_allocated)++;
352     if ((block = is->files[cat].head.freelist))
353     {
354         bf_read (is->files[cat].bf, block, 0, sizeof(int), buf);
355         memcpy (&is->files[cat].head.freelist, buf, sizeof(int));
356     }
357     else
358         block = (is->files[cat].head.lastblock)++;
359     return block;
360 }
361
362 static void release_block (ISAMD is, int cat, int pos)
363 {
364     char buf[sizeof(int)];
365    
366     (is->files[cat].no_released)++;
367     is->files[cat].head_is_dirty = 1; 
368     memcpy (buf, &is->files[cat].head.freelist, sizeof(int));
369     is->files[cat].head.freelist = pos;
370     bf_write (is->files[cat].bf, pos, 0, sizeof(int), buf);
371 }
372 #endif
373
374 int isamd_alloc_block (ISAMD is, int cat)
375 {
376     int block = 0;
377
378     if (is->files[cat].fc_list)
379     {
380         int j, nb;
381         for (j = 0; j < is->files[cat].fc_max; j++)
382             if ((nb = is->files[cat].fc_list[j]) && (!block || nb < block))
383             {
384                 is->files[cat].fc_list[j] = 0;
385                 block = nb;
386                 break;
387             }
388     }
389     if (!block)
390         block = alloc_block (is, cat);
391     if (is->method->debug > 3)
392         logf (LOG_LOG, "isamd: alloc_block in cat %d: %d", cat, block);
393     return block;
394 }
395
396 void isamd_release_block (ISAMD is, int cat, int pos)
397 {
398     if (is->method->debug > 3)
399         logf (LOG_LOG, "isamd: release_block in cat %d: %d", cat, pos);
400     assert(pos!=0);
401     
402     if (is->files[cat].fc_list)
403     {
404         int j;
405         for (j = 0; j<is->files[cat].fc_max; j++)
406             if (!is->files[cat].fc_list[j])
407             {
408                 is->files[cat].fc_list[j] = pos;
409                 return;
410             }
411     }
412     release_block (is, cat, pos);
413 }
414
415 static void init_fc (ISAMD is, int cat)
416 {
417     int j = 100;
418         
419     is->files[cat].fc_max = j;
420     is->files[cat].fc_list = (int *)
421         xmalloc (sizeof(*is->files[0].fc_list) * j);
422     while (--j >= 0)
423         is->files[cat].fc_list[j] = 0;
424 }
425
426 static void release_fc (ISAMD is, int cat)
427 {
428     int b, j = is->files[cat].fc_max;
429
430     while (--j >= 0)
431         if ((b = is->files[cat].fc_list[j]))
432         {
433             release_block (is, cat, b);
434             is->files[cat].fc_list[j] = 0;
435         }
436 }
437
438 void isamd_pp_close (ISAMD_PP pp)
439 {
440     ISAMD is = pp->is;
441
442     (*is->method->code_stop)(ISAMD_DECODE, pp->decodeClientData);
443     isamd_free_diffs(pp);  /* see merge-d.h */
444     xfree (pp->buf);
445     xfree (pp);
446     if (is->method->debug > 2)
447        logf (LOG_LOG, "isamd_pp_close %p %d=%d:%d  sz=%d n=%d=%d:%d",
448              pp, isamd_addr(pp->pos, pp->cat), pp->cat, pp->pos, pp->size, 
449              pp->next, isamd_type(pp->next), isamd_block(pp->next) );
450 }
451
452
453
454 ISAMD_PP isamd_pp_open (ISAMD is, ISAMD_P ipos)
455 {
456     ISAMD_PP pp = (ISAMD_PP) xmalloc (sizeof(*pp));
457     char *src;
458    
459     pp->cat = isamd_type(ipos);
460     pp->pos = isamd_block(ipos); 
461
462     src = pp->buf = (char *) xmalloc (is->method->filecat[is->max_cat].bsize);
463                  /* always allocate for the largest blocks, saves trouble */
464     pp->next = 0;
465     pp->size = 0;
466     pp->offset = 0;
467     pp->is = is;
468     pp->decodeClientData = (*is->method->code_start)(ISAMD_DECODE);
469     pp->numKeys = 0;
470     pp->diffs=0;
471   
472     pp->diffbuf=0;
473     pp->diffinfo=0;
474     
475     if (pp->pos)
476     {
477         src = pp->buf;
478         isamd_read_block (is, pp->cat, pp->pos, src);
479         memcpy (&pp->next, src, sizeof(pp->next));
480         src += sizeof(pp->next);
481         memcpy (&pp->size, src, sizeof(pp->size));
482         src += sizeof(pp->size);
483         memcpy (&pp->numKeys, src, sizeof(pp->numKeys));
484         src += sizeof(pp->numKeys);
485         memcpy (&pp->diffs, src, sizeof(pp->diffs));
486         src += sizeof(pp->diffs);
487         assert (pp->next != pp->pos);
488         pp->offset = src - pp->buf; 
489         assert (pp->offset == ISAMD_BLOCK_OFFSET_1);
490 //        if (is->method->debug > 2)
491 //           logf (LOG_LOG, "isamd_pp_open  %p %d=%d:%d  sz=%d n=%d=%d:%d",
492 //                 pp, isamd_addr(pp->pos, pp->cat), pp->cat, pp->pos, pp->size, 
493 //                 pp->next, isamd_type(pp->next), isamd_block(pp->next) );
494     }
495     if (is->method->debug > 2)
496        logf (LOG_LOG, "isamd_pp_open  %p %d=%d:%d  sz=%d n=%d=%d:%d",
497              pp, isamd_addr(pp->pos, pp->cat), pp->cat, pp->pos, pp->size, 
498              pp->next, isamd_type(pp->next), isamd_block(pp->next) );
499     
500       
501     return pp;
502 }
503
504
505
506 void isamd_buildfirstblock(ISAMD_PP pp){
507   char *dst=pp->buf;
508   assert(pp->buf);
509   assert(pp->next != pp->pos); 
510   memcpy(dst, &pp->next, sizeof(pp->next) );
511   dst += sizeof(pp->next);
512   memcpy(dst, &pp->size,sizeof(pp->size));
513   dst += sizeof(pp->size);
514   memcpy(dst, &pp->numKeys, sizeof(pp->numKeys));
515   dst += sizeof(pp->numKeys);
516   memcpy(dst, &pp->diffs, sizeof(pp->diffs));
517   dst += sizeof(pp->diffs);  
518   assert (dst - pp->buf  == ISAMD_BLOCK_OFFSET_1);
519   if (pp->is->method->debug > 2)
520      logf (LOG_LOG, "isamd: first: sz=%d  p=%d/%d>%d/%d nk=%d d=%d",
521            pp->size, 
522            pp->cat, pp->pos, 
523            isamd_type(pp->next), isamd_block(pp->next),
524            pp->numKeys, pp->diffs);
525 }
526
527 void isamd_buildlaterblock(ISAMD_PP pp){
528   char *dst=pp->buf;
529   assert(pp->buf);
530   assert(pp->next != isamd_addr(pp->pos,pp->cat)); 
531   memcpy(dst, &pp->next, sizeof(pp->next) );
532   dst += sizeof(pp->next);
533   memcpy(dst, &pp->size,sizeof(pp->size));
534   dst += sizeof(pp->size);
535   assert (dst - pp->buf  == ISAMD_BLOCK_OFFSET_N);
536   if (pp->is->method->debug > 2)
537      logf (LOG_LOG, "isamd: l8r: sz=%d  p=%d/%d>%d/%d",
538            pp->size, 
539            pp->pos, pp->cat, 
540            isamd_block(pp->next), isamd_type(pp->next) );
541 }
542
543
544
545 /* returns non-zero if item could be read; 0 otherwise */
546 int isamd_pp_read (ISAMD_PP pp, void *buf)
547 {
548     return isamd_read_item (pp, (char **) &buf);
549     /* note: isamd_read_item is in merge-d.c, because it is so */
550     /* convoluted with the merge process */
551 }
552
553 /* read one main item from file - decode and store it in *dst.
554    Does not worry about diffs
555    Returns
556      0 if end-of-file
557      1 if item could be read ok
558 */
559 int isamd_read_main_item (ISAMD_PP pp, char **dst)
560 {
561     ISAMD is = pp->is;
562     char *src = pp->buf + pp->offset;
563     int newcat;
564
565     if (pp->offset >= pp->size)
566     {
567         if (!pp->next)
568         {
569             pp->pos = 0;
570             return 0; /* end of file */
571         }
572         if (pp->next > pp->pos)
573         {
574             if (pp->next == pp->pos + 1)
575                 is->files[pp->cat].no_next++;
576             else
577             {
578                 is->files[pp->cat].no_forward++;
579                 is->files[pp->cat].sum_forward += pp->next - pp->pos;
580             }
581         }
582         else
583         {
584             if (pp->next + 1 == pp->pos)
585                 is->files[pp->cat].no_prev++;
586             else
587             {
588                 is->files[pp->cat].no_backward++;
589                 is->files[pp->cat].sum_backward += pp->pos - pp->next;
590             }
591         }
592         /* out new block position */
593         newcat = isamd_type(pp->next);
594         pp->pos = isamd_block(pp->next);
595         pp->cat = isamd_type(pp->next);
596         
597         src = pp->buf;
598         /* read block and save 'next' and 'size' entry */
599         isamd_read_block (is, pp->cat, pp->pos, src);
600         memcpy (&pp->next, src, sizeof(pp->next));
601         src += sizeof(pp->next);
602         memcpy (&pp->size, src, sizeof(pp->size));
603         src += sizeof(pp->size);
604         /* assume block is non-empty */
605         assert (src - pp->buf == ISAMD_BLOCK_OFFSET_N);
606         assert (pp->next != isamd_addr(pp->pos,pp->cat));
607         //if (pp->deleteFlag)
608         //    isamd_release_block (is, pp->cat, pp->pos);
609         (*is->method->code_reset)(pp->decodeClientData);
610         (*is->method->code_item)(ISAMD_DECODE, pp->decodeClientData, dst, &src);
611         pp->offset = src - pp->buf; 
612         if (is->method->debug > 2)
613             logf (LOG_LOG, "isamd: read_block size=%d %d %d next=%d",
614                  pp->size, pp->cat, pp->pos, pp->next);
615         return 2;
616     }
617     (*is->method->code_item)(ISAMD_DECODE, pp->decodeClientData, dst, &src);
618     pp->offset = src - pp->buf; 
619     return 1;
620 }
621
622 int isamd_pp_num (ISAMD_PP pp)
623 {
624     return pp->numKeys;
625 }
626
627 static char *hexdump(unsigned char *p, int len, char *buff) {
628   static char localbuff[128];
629   char bytebuff[8];
630   if (!buff) buff=localbuff;
631   *buff='\0';
632   while (len--) {
633     sprintf(bytebuff,"%02x",*p);
634     p++;
635     strcat(buff,bytebuff);
636     if (len) strcat(buff," ");
637   }
638   return buff;
639 }
640
641
642 void isamd_pp_dump (ISAMD is, ISAMD_P ipos)
643 {
644   ISAMD_PP pp;
645   ISAMD_P oldaddr=0;
646   struct it_key key;
647   int i,n;
648   int occur =0;
649   int oldoffs;
650   char hexbuff[64];
651   
652   logf(LOG_LOG,"dumping isamd block %d (%d:%d)",
653                   (int)ipos, isamd_type(ipos), isamd_block(ipos) );
654   pp=isamd_pp_open(is,ipos);
655   logf(LOG_LOG,"numKeys=%d,  ofs=%d d=%d",
656        pp->numKeys, 
657        pp->offset, pp->diffs);
658   oldoffs= pp->offset;
659   while(isamd_pp_read(pp, &key))
660   {
661      if (oldaddr != isamd_addr(pp->pos,pp->cat) )
662      {
663         oldaddr = isamd_addr(pp->pos,pp->cat); 
664         logf(LOG_LOG,"block %d (%d:%d) sz=%d nx=%d (%d:%d) ofs=%d",
665                   isamd_addr(pp->pos,pp->cat), 
666                   pp->cat, pp->pos, pp->size,
667                   pp->next, isamd_type(pp->next), isamd_block(pp->next),
668                   pp->offset);
669         i=0;      
670         while (i<pp->size) {
671           n=pp->size-i;
672           if (n>8) n=8;
673           logf(LOG_LOG,"  %05x: %s",i,hexdump(pp->buf+i,n,hexbuff));
674           i+=n;
675         }
676         if (oldoffs >  ISAMD_BLOCK_OFFSET_N)
677            oldoffs=ISAMD_BLOCK_OFFSET_N;
678      } /* new block */
679      occur++;
680      logf (LOG_LOG,"    got %d:%d=%x:%x from %s at %d=%x",
681                   key.sysno, key.seqno,
682                   key.sysno, key.seqno,
683                   hexdump(pp->buf+oldoffs, pp->offset-oldoffs, hexbuff),
684                   oldoffs, oldoffs);
685      oldoffs = pp->offset;
686   }
687   /*!*/ /*TODO: dump diffs too!!! */
688   isamd_pp_close(pp);
689 } /* dump */
690
691 /*
692  * $Log: isamd.c,v $
693  * Revision 1.6  1999-08-17 19:44:25  heikki
694  * Fixed memory leaks
695  *
696  * Revision 1.4  1999/08/04 14:21:18  heikki
697  * isam-d seems to be working.
698  *
699  * Revision 1.3  1999/07/21 14:24:50  heikki
700  * isamd write and read functions ok, except when diff block full.
701  * (merge not yet done)
702  *
703  * Revision 1.1  1999/07/14 12:34:43  heikki
704  * Copied from isamh, starting to change things...
705  *
706  *
707  */