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