mproved statistics on isam-d
[idzebra-moved-to-github.git] / isamc / isamd.c
1 /*
2  * Copyright (c) 1995-1998, Index Data.
3  * See the file LICENSE for details.
4  * $Id: isamd.c,v 1.17 1999-10-06 11:46:36 heikki Exp $ 
5  *
6  * Isamd - isam with diffs 
7  * Programmed by: Heikki Levanto
8  *
9  * Todo
10  *  - Statistics are missing and/or completely wrong
11  *  - Lots of code stolen from isamc, not all needed any more
12  */
13
14
15 #include <stdlib.h>
16 #include <assert.h>
17 #include <string.h>
18 #include <stdio.h>
19
20 #include <log.h>
21 #include "../index/index.h"  /* isamd uses the internal structure of it_key */
22 #include "isamd-p.h"
23
24 static void flush_block (ISAMD is, int cat);
25 static void release_fc (ISAMD is, int cat);
26 static void init_fc (ISAMD is, int cat);
27
28 #define ISAMD_FREELIST_CHUNK 1
29
30 #define SMALL_TEST 0
31
32 ISAMD_M isamd_getmethod (ISAMD_M me)
33 {
34     static struct ISAMD_filecat_s def_cat[] = {
35 #if SMALL_TEST
36 /*        blocksz,   max. Unused time being */
37         {    32,   40 },  /* 24 is the smallest unreasonable size! */
38         {    64,    0 },
39 #else
40         {    32,    1 },
41         {    64,    1 },
42         {   128,    1 },
43         {   512,    1 },
44         {  2048,    1 },
45         {  8192,    1 },
46         { 32768,    0 },
47
48 #endif
49 #ifdef SKIPTHIS
50
51
52
53         {    32,    1 },
54         {   128,    1 },
55         {   512,    1 },
56         {  2048,    1 },
57         {  8192,    1 },
58         { 32768,    1 },
59         {131072,    0 },
60
61         {    24,    1 }, /* Experimental sizes */
62         {    32,    1 },
63         {    64,    1 },
64         {   128,    1 },
65         {   256,    1 },
66         {   512,    1 },
67         {  1024,    1 },
68         {  2048,    0 },
69 #endif 
70
71     };
72     ISAMD_M m = (ISAMD_M) xmalloc (sizeof(*m));  /* never released! */
73     m->filecat = def_cat;                        /* ok, only alloc'd once */
74
75     m->code_start = NULL;
76     m->code_item = NULL;
77     m->code_stop = NULL;
78     m->code_reset = NULL;
79
80     m->compare_item = NULL;
81
82     m->debug = 0; /* default to no debug */
83
84     m->max_blocks_mem = 10;
85
86     return m;
87 }
88
89
90
91 ISAMD isamd_open (BFiles bfs, const char *name, int writeflag, ISAMD_M method)
92 {
93     ISAMD is;
94     ISAMD_filecat filecat;
95     int i = 0;
96
97     is = (ISAMD) xmalloc (sizeof(*is));
98
99     is->method = (ISAMD_M) xmalloc (sizeof(*is->method));
100     memcpy (is->method, method, sizeof(*method));
101     filecat = is->method->filecat;
102     assert (filecat);
103
104     /* determine number of block categories */
105     if (is->method->debug>0)
106         logf (LOG_LOG, "isamd: bsize  maxkeys");
107     do
108     {
109         if (is->method->debug>0)
110             logf (LOG_LOG, "isamd:%6d %6d",
111                   filecat[i].bsize, filecat[i].mblocks);
112     } while (filecat[i++].mblocks);
113     is->no_files = i;
114     is->max_cat = --i;
115  
116     assert (is->no_files > 0);
117     assert (is->max_cat <=8 ); /* we have only 3 bits for it */
118     
119     is->files = (ISAMD_file) xmalloc (sizeof(*is->files)*is->no_files);
120
121     for (i = 0; i<is->no_files; i++)
122     {
123         char fname[512];
124
125         sprintf (fname, "%s%c", name, i+'A');
126         is->files[i].bf = bf_open (bfs, fname, is->method->filecat[i].bsize,
127                                    writeflag);
128         is->files[i].head_is_dirty = 0;
129         if (!bf_read (is->files[i].bf, 0, 0, sizeof(ISAMD_head),
130                      &is->files[i].head))
131         {
132             is->files[i].head.lastblock = 1;
133             is->files[i].head.freelist = 0;
134         }
135         is->files[i].alloc_entries_num = 0;
136         is->files[i].alloc_entries_max =
137             is->method->filecat[i].bsize / sizeof(int) - 1;
138         is->files[i].alloc_buf = (char *)
139             xmalloc (is->method->filecat[i].bsize);
140         is->files[i].no_writes = 0; /* clear statistics */
141         is->files[i].no_reads = 0;
142         is->files[i].no_skip_writes = 0;
143         is->files[i].no_allocated = 0;
144         is->files[i].no_released = 0;
145         is->files[i].no_remap = 0;
146         is->files[i].no_forward = 0;
147         is->files[i].no_backward = 0;
148         is->files[i].sum_forward = 0;
149         is->files[i].sum_backward = 0;
150         is->files[i].no_next = 0;
151         is->files[i].no_prev = 0;
152         is->files[i].no_op_diffonly=0;
153         is->files[i].no_op_main=0;
154         init_fc (is, i);
155     }
156     is->last_pos=0;
157     is->last_cat=0;   
158     is->no_read=0;    
159     is->no_read_main=0;
160     is->no_write=0;   
161     is->no_op_single=0;
162     is->no_op_new=0;
163     is->no_read_keys=0;
164     is->no_read_eof=0;
165     is->no_seek_nxt=0;
166     is->no_seek_sam=0;
167     is->no_seek_fwd=0;
168     is->no_seek_prv=0;
169     is->no_seek_bak=0;
170     is->no_seek_cat=0;
171     is->no_fbuilds=0;
172     is->no_appds=0;
173     is->no_merges=0;
174     is->no_non=0;
175     is->no_singles=0;
176
177     return is;
178 }
179
180 int isamd_block_used (ISAMD is, int type)
181 {
182     if ( type==-1) /* singleton */
183       return 0; 
184     if (type < 0 || type >= is->no_files)
185         return -1;
186     return is->files[type].head.lastblock-1;
187 }
188
189 int isamd_block_size (ISAMD is, int type)
190 {
191     ISAMD_filecat filecat = is->method->filecat;
192     if ( type==-1) /* singleton */
193       return 0; /* no bytes used */ 
194     if (type < 0 || type >= is->no_files)
195         return -1;
196     return filecat[type].bsize;
197 }
198
199 int isamd_close (ISAMD is)
200 {
201     int i;
202     int s;
203
204     if (is->method->debug>0)
205     {
206         logf (LOG_LOG, "isamd statistics");
207         logf (LOG_LOG, "f    nxt   forw  mid-f   prev  backw  mid-b");
208         for (i = 0; i<is->no_files; i++)
209             logf (LOG_LOG, "%d%7d%7d%7.1f%7d%7d%7.1f",i,
210                   is->files[i].no_next,
211                   is->files[i].no_forward,
212                   is->files[i].no_forward ?
213                     (double) is->files[i].sum_forward/is->files[i].no_forward
214                     : 0.0,
215                   is->files[i].no_prev,
216                   is->files[i].no_backward,
217                   is->files[i].no_backward ?
218                     (double) is->files[i].sum_backward/is->files[i].no_backward
219                     : 0.0);
220     }
221     if (is->method->debug>0)
222         logf (LOG_LOG, "f  writes   reads skipped   alloc released ");
223     for (i = 0; i<is->no_files; i++)
224     {
225         release_fc (is, i);
226         assert (is->files[i].bf);
227         if (is->files[i].head_is_dirty)
228             bf_write (is->files[i].bf, 0, 0, sizeof(ISAMD_head),
229                  &is->files[i].head);
230         if (is->method->debug>0)
231             logf (LOG_LOG, "%d%8d%8d%8d%8d%8d",i,
232                   is->files[i].no_writes,
233                   is->files[i].no_reads,
234                   is->files[i].no_skip_writes,
235                   is->files[i].no_allocated,
236                   is->files[i].no_released);
237         xfree (is->files[i].fc_list);
238         flush_block (is, i);
239         bf_close (is->files[i].bf);
240     }
241     
242     if (is->method->debug>0) 
243     {
244         logf (LOG_LOG, "f   opens    main  diffonly");
245         for (i = 0; i<is->no_files; i++)
246         {
247             logf (LOG_LOG, "%d%8d%8d%8d",i,
248                   is->files[i].no_op_main+
249                   is->files[i].no_op_diffonly,
250                   is->files[i].no_op_main,
251                   is->files[i].no_op_diffonly);
252         }
253         logf(LOG_LOG,"open single  %8d", is->no_op_single);
254         logf(LOG_LOG,"open new     %8d", is->no_op_new);
255
256         logf(LOG_LOG, "new build   %8d", is->no_fbuilds);
257         logf(LOG_LOG, "append      %8d", is->no_appds);
258         logf(LOG_LOG, "  merges    %8d", is->no_merges);
259         logf(LOG_LOG, "  singles   %8d", is->no_singles);
260         logf(LOG_LOG, "  no-ops    %8d", is->no_non);
261
262         logf(LOG_LOG, "read blocks %8d", is->no_read);
263         logf(LOG_LOG, "read keys:  %8d %8.1f k/bl", 
264                   is->no_read_keys, 
265                   1.0*(is->no_read_keys+1)/(is->no_read+1) );
266         logf(LOG_LOG, "read main-k %8d %8.1f %% of keys",
267                   is->no_read_main,
268                   100.0*(is->no_read_main+1)/(is->no_read_keys+1) );
269         logf(LOG_LOG, "read ends:  %8d %8.1f k/e",
270                   is->no_read_eof,
271                   1.0*(is->no_read_keys+1)/(is->no_read_eof+1) );
272         s= is->no_seek_nxt+ is->no_seek_sam+ is->no_seek_fwd +
273            is->no_seek_prv+ is->no_seek_bak+ is->no_seek_cat;
274         if (s==0) 
275           s++;
276         logf(LOG_LOG, "seek same   %8d %8.1f%%",
277             is->no_seek_sam, 100.0*is->no_seek_sam/s );
278         logf(LOG_LOG, "seek next   %8d %8.1f%%",
279             is->no_seek_nxt, 100.0*is->no_seek_nxt/s );
280         logf(LOG_LOG, "seek prev   %8d %8.1f%%",
281             is->no_seek_prv, 100.0*is->no_seek_prv/s );
282         logf(LOG_LOG, "seek forw   %8d %8.1f%%",
283             is->no_seek_fwd, 100.0*is->no_seek_fwd/s );
284         logf(LOG_LOG, "seek back   %8d %8.1f%%",
285             is->no_seek_bak, 100.0*is->no_seek_bak/s );
286         logf(LOG_LOG, "seek cat    %8d %8.1f%%",
287             is->no_seek_cat, 100.0*is->no_seek_cat/s );
288     }
289     xfree (is->files);
290     xfree (is->method);
291     xfree (is);
292     return 0;
293 }
294
295 static void isamd_seek_stat(ISAMD is, int cat, int pos)
296 {
297   if (cat != is->last_cat)
298      is->no_seek_cat++;
299   else if ( pos == is->last_pos)
300      is->no_seek_sam++;
301   else if ( pos == is->last_pos+1)
302      is->no_seek_nxt++;
303   else if ( pos == is->last_pos-1)
304      is->no_seek_prv++;
305   else if ( pos > is->last_pos)
306      is->no_seek_fwd++;
307   else if ( pos < is->last_pos)
308      is->no_seek_bak++;
309   is->last_cat = cat;
310   is->last_pos = pos;
311 } /* seek_stat */
312
313 int isamd_read_block (ISAMD is, int cat, int pos, char *dst)
314 {
315     isamd_seek_stat(is,cat,pos);
316     ++(is->files[cat].no_reads);
317     ++(is->no_read);
318     if (is->method->debug > 6)
319         logf (LOG_LOG, "isamd: read_block %d:%d",cat, pos);
320     return bf_read (is->files[cat].bf, pos, 0, 0, dst);
321 }
322
323 int isamd_write_block (ISAMD is, int cat, int pos, char *src)
324 {
325     isamd_seek_stat(is,cat,pos);
326     ++(is->files[cat].no_writes);
327     ++(is->no_write);
328     if (is->method->debug > 6)
329         logf (LOG_LOG, "isamd: write_block %d:%d", cat, pos);
330     return bf_write (is->files[cat].bf, pos, 0, 0, src);
331 }
332
333 int isamd_write_dblock (ISAMD is, int cat, int pos, char *src,
334                       int nextpos, int offset)
335 {
336     ISAMD_BLOCK_SIZE size = offset + ISAMD_BLOCK_OFFSET_N;
337     if (is->method->debug > 4)
338         logf (LOG_LOG, "isamd: write_dblock. size=%d nextpos=%d",
339               (int) size, nextpos);
340     src -= ISAMD_BLOCK_OFFSET_N;
341     assert( ISAMD_BLOCK_OFFSET_N == sizeof(int)+sizeof(int) );
342     memcpy (src, &nextpos, sizeof(int));
343     memcpy (src + sizeof(int), &size, sizeof(size));
344     return isamd_write_block (is, cat, pos, src);
345 }
346
347 #if ISAMD_FREELIST_CHUNK
348 static void flush_block (ISAMD is, int cat)
349 {
350     char *abuf = is->files[cat].alloc_buf;
351     int block = is->files[cat].head.freelist;
352     if (block && is->files[cat].alloc_entries_num)
353     {
354         memcpy (abuf, &is->files[cat].alloc_entries_num, sizeof(int));
355         bf_write (is->files[cat].bf, block, 0, 0, abuf);
356         is->files[cat].alloc_entries_num = 0;
357     }
358     xfree (abuf);
359 }
360
361 static int alloc_block (ISAMD is, int cat)
362 {
363     int block = is->files[cat].head.freelist;
364     char *abuf = is->files[cat].alloc_buf;
365
366     (is->files[cat].no_allocated)++;
367
368     if (!block)
369     {
370         block = (is->files[cat].head.lastblock)++;   /* no free list */
371         is->files[cat].head_is_dirty = 1;
372     }
373     else
374     {
375         if (!is->files[cat].alloc_entries_num) /* read first time */
376         {
377             bf_read (is->files[cat].bf, block, 0, 0, abuf);
378             memcpy (&is->files[cat].alloc_entries_num, abuf,
379                     sizeof(is->files[cat].alloc_entries_num));
380             assert (is->files[cat].alloc_entries_num > 0);
381         }
382         /* have some free blocks now */
383         assert (is->files[cat].alloc_entries_num > 0);
384         is->files[cat].alloc_entries_num--;
385         if (!is->files[cat].alloc_entries_num)  /* last one in block? */
386         {
387             memcpy (&is->files[cat].head.freelist, abuf + sizeof(int),
388                     sizeof(int));
389             is->files[cat].head_is_dirty = 1;
390
391             if (is->files[cat].head.freelist)
392             {
393                 bf_read (is->files[cat].bf, is->files[cat].head.freelist,
394                          0, 0, abuf);
395                 memcpy (&is->files[cat].alloc_entries_num, abuf,
396                         sizeof(is->files[cat].alloc_entries_num));
397                 assert (is->files[cat].alloc_entries_num);
398             }
399         }
400         else
401             memcpy (&block, abuf + sizeof(int) + sizeof(int) *
402                     is->files[cat].alloc_entries_num, sizeof(int));
403     }
404     return block;
405 }
406
407 static void release_block (ISAMD is, int cat, int pos)
408 {
409     char *abuf = is->files[cat].alloc_buf;
410     int block = is->files[cat].head.freelist;
411
412     (is->files[cat].no_released)++;
413
414     if (block && !is->files[cat].alloc_entries_num) /* must read block */
415     {
416         bf_read (is->files[cat].bf, block, 0, 0, abuf);
417         memcpy (&is->files[cat].alloc_entries_num, abuf,
418                 sizeof(is->files[cat].alloc_entries_num));
419         assert (is->files[cat].alloc_entries_num > 0);
420     }
421     assert (is->files[cat].alloc_entries_num <= is->files[cat].alloc_entries_max);
422     if (is->files[cat].alloc_entries_num == is->files[cat].alloc_entries_max)
423     {
424         assert (block);
425         memcpy (abuf, &is->files[cat].alloc_entries_num, sizeof(int));
426         bf_write (is->files[cat].bf, block, 0, 0, abuf);
427         is->files[cat].alloc_entries_num = 0;
428     }
429     if (!is->files[cat].alloc_entries_num) /* make new buffer? */
430     {
431         memcpy (abuf + sizeof(int), &block, sizeof(int));
432         is->files[cat].head.freelist = pos;
433         is->files[cat].head_is_dirty = 1; 
434     }
435     else
436     {
437         memcpy (abuf + sizeof(int) +
438                 is->files[cat].alloc_entries_num*sizeof(int),
439                 &pos, sizeof(int));
440     }
441     is->files[cat].alloc_entries_num++;
442 }
443 #else
444 static void flush_block (ISAMD is, int cat)
445 {
446     char *abuf = is->files[cat].alloc_buf;
447     xfree (abuf);
448 }
449
450 static int alloc_block (ISAMD is, int cat)
451 {
452     int block;
453     char buf[sizeof(int)];
454
455     is->files[cat].head_is_dirty = 1;
456     (is->files[cat].no_allocated)++;
457     if ((block = is->files[cat].head.freelist))
458     {
459         bf_read (is->files[cat].bf, block, 0, sizeof(int), buf);
460         memcpy (&is->files[cat].head.freelist, buf, sizeof(int));
461     }
462     else
463         block = (is->files[cat].head.lastblock)++;
464     return block;
465 }
466
467 static void release_block (ISAMD is, int cat, int pos)
468 {
469     char buf[sizeof(int)];
470    
471     (is->files[cat].no_released)++;
472     is->files[cat].head_is_dirty = 1; 
473     memcpy (buf, &is->files[cat].head.freelist, sizeof(int));
474     is->files[cat].head.freelist = pos;
475     bf_write (is->files[cat].bf, pos, 0, sizeof(int), buf);
476 }
477 #endif
478
479 int isamd_alloc_block (ISAMD is, int cat)
480 {
481     int block = 0;
482
483     if (is->files[cat].fc_list)
484     {
485         int j, nb;
486         for (j = 0; j < is->files[cat].fc_max; j++)
487             if ((nb = is->files[cat].fc_list[j]) && (!block || nb < block))
488             {
489                 is->files[cat].fc_list[j] = 0;
490                 block = nb;
491                 break;
492             }
493     }
494     if (!block)
495         block = alloc_block (is, cat);
496     if (is->method->debug > 4)
497         logf (LOG_LOG, "isamd: alloc_block in cat %d: %d", cat, block);
498     return block;
499 }
500
501 void isamd_release_block (ISAMD is, int cat, int pos)
502 {
503     if (is->method->debug > 4)
504         logf (LOG_LOG, "isamd: release_block in cat %d: %d", cat, pos);
505     assert(pos!=0);
506     
507     if (is->files[cat].fc_list)
508     {
509         int j;
510         for (j = 0; j<is->files[cat].fc_max; j++)
511             if (!is->files[cat].fc_list[j])
512             {
513                 is->files[cat].fc_list[j] = pos;
514                 return;
515             }
516     }
517     release_block (is, cat, pos);
518 }
519
520 static void init_fc (ISAMD is, int cat)
521 {
522     int j = 100;
523         
524     is->files[cat].fc_max = j;
525     is->files[cat].fc_list = (int *)
526         xmalloc (sizeof(*is->files[0].fc_list) * j);
527     while (--j >= 0)
528         is->files[cat].fc_list[j] = 0;
529 }
530
531 static void release_fc (ISAMD is, int cat)
532 {
533     int b, j = is->files[cat].fc_max;
534
535     while (--j >= 0)
536         if ((b = is->files[cat].fc_list[j]))
537         {
538             release_block (is, cat, b);
539             is->files[cat].fc_list[j] = 0;
540         }
541 }
542
543 void isamd_pp_close (ISAMD_PP pp)
544 {
545     ISAMD is = pp->is;
546
547     (*is->method->code_stop)(ISAMD_DECODE, pp->decodeClientData);
548     isamd_free_diffs(pp);  /* see merge-d.h */
549     xfree (pp->buf);
550     xfree (pp);
551     if (is->method->debug > 5)
552        logf (LOG_LOG, "isamd_pp_close %p %d=%d:%d  sz=%d n=%d=%d:%d",
553              pp, isamd_addr(pp->pos, pp->cat), pp->cat, pp->pos, pp->size, 
554              pp->next, isamd_type(pp->next), isamd_block(pp->next) );
555 }
556
557
558
559 ISAMD_PP isamd_pp_open (ISAMD is, ISAMD_P ipos)
560 {
561     ISAMD_PP pp = (ISAMD_PP) xmalloc (sizeof(*pp));
562     char *src;
563     int sz = is->method->filecat[is->max_cat].bsize;
564                  /* always allocate for the largest blocks, saves trouble */
565     struct it_key singlekey;
566     char *c_ptr; /* for fake encoding the singlekey */
567     char *i_ptr;
568     int ofs;
569     
570     pp->numKeys = 0;
571     src = pp->buf = (char *) xmalloc (sz);
572     memset(src,'\0',sz); /* clear the buffer, for new blocks */
573     
574     pp->next = 0;
575     pp->size = 0;
576     pp->offset = 0;
577     pp->is = is;
578     pp->diffs=0;
579     pp->diffbuf=0;
580     pp->diffinfo=0;
581     pp->decodeClientData = (*is->method->code_start)(ISAMD_DECODE);
582     
583     if ( is_singleton(ipos) ) 
584     {
585        pp->cat=0; 
586        pp->pos=0;
587        if (is->method->debug > 5)
588           logf (LOG_LOG, "isamd_pp_open  %p %d=%d:%d  sz=%d n=%d=%d:%d",
589                 pp, isamd_addr(pp->pos, pp->cat), pp->cat, pp->pos, pp->size, 
590                 pp->next, isamd_type(pp->next), isamd_block(pp->next) );
591        singleton_decode(ipos, &singlekey );
592        pp->offset=ISAMD_BLOCK_OFFSET_1;
593        pp->numKeys = 1;
594        ofs=pp->offset+sizeof(int); /* reserve length of diffsegment */
595        singlekey.seqno = singlekey.seqno * 2 + 1; /* make an insert diff */  
596        c_ptr=&(pp->buf[ofs]);
597        i_ptr=(char*)(&singlekey); 
598        (*is->method->code_item)(ISAMD_ENCODE, pp->decodeClientData, 
599                                 &c_ptr, &i_ptr);
600        (*is->method->code_reset)(pp->decodeClientData);
601        ofs += c_ptr-&(pp->buf[ofs]);
602        memcpy( &(pp->buf[pp->offset]), &ofs, sizeof(int) );
603        /* since we memset buf earlier, we already have a zero endmark! */
604        pp->size = ofs;
605        if (is->method->debug > 5)
606           logf (LOG_LOG, "isamd_pp_open single %d=%x: %d.%d sz=%d", 
607             ipos,ipos, 
608             singlekey.sysno, singlekey.seqno/2,
609             pp->size );
610        is->no_op_single++;
611        return pp;
612     } /* singleton */
613    
614     pp->cat = isamd_type(ipos);
615     pp->pos = isamd_block(ipos); 
616     
617     if (0==pp->pos)
618       is->no_op_new++; 
619       
620     if (pp->pos)
621     {
622         src = pp->buf;
623         isamd_read_block (is, pp->cat, pp->pos, src);
624         memcpy (&pp->next, src, sizeof(pp->next));
625         src += sizeof(pp->next);
626         memcpy (&pp->size, src, sizeof(pp->size));
627         src += sizeof(pp->size);
628         memcpy (&pp->numKeys, src, sizeof(pp->numKeys));
629         src += sizeof(pp->numKeys);
630         assert (pp->next != isamd_addr(pp->pos,pp->cat));
631         pp->offset = src - pp->buf; 
632         assert (pp->offset == ISAMD_BLOCK_OFFSET_1);
633         assert(pp->size>=ISAMD_BLOCK_OFFSET_1); /*??*/
634         if (pp->next)
635           is->files[pp->cat].no_op_main++;
636         else
637           is->files[pp->cat].no_op_diffonly++;
638     }
639     if (is->method->debug > 5)
640        logf (LOG_LOG, "isamd_pp_open  %p %d=%d:%d  sz=%d n=%d=%d:%d",
641              pp, isamd_addr(pp->pos, pp->cat), pp->cat, pp->pos, pp->size, 
642              pp->next, isamd_type(pp->next), isamd_block(pp->next) );
643
644     return pp;
645 }
646
647
648
649 void isamd_buildfirstblock(ISAMD_PP pp){
650   char *dst=pp->buf;
651   assert(pp->buf);
652   assert(pp->next != isamd_addr(pp->pos,pp->cat)); 
653   memcpy(dst, &pp->next, sizeof(pp->next) );
654   dst += sizeof(pp->next);
655   memcpy(dst, &pp->size,sizeof(pp->size));
656   dst += sizeof(pp->size);
657   memcpy(dst, &pp->numKeys, sizeof(pp->numKeys));
658   dst += sizeof(pp->numKeys);
659   assert (dst - pp->buf  == ISAMD_BLOCK_OFFSET_1);
660   if (pp->is->method->debug > 5)
661      logf (LOG_LOG, "isamd: bldfirst:  p=%d=%d:%d n=%d:%d:%d sz=%d nk=%d ",
662            isamd_addr(pp->pos,pp->cat),pp->cat, pp->pos, 
663            pp->next, isamd_type(pp->next), isamd_block(pp->next),
664            pp->size, pp->numKeys);
665 }
666
667 void isamd_buildlaterblock(ISAMD_PP pp){
668   char *dst=pp->buf;
669   assert(pp->buf);
670   assert(pp->next != isamd_addr(pp->pos,pp->cat)); 
671   memcpy(dst, &pp->next, sizeof(pp->next) );
672   dst += sizeof(pp->next);
673   memcpy(dst, &pp->size,sizeof(pp->size));
674   dst += sizeof(pp->size);
675   assert (dst - pp->buf  == ISAMD_BLOCK_OFFSET_N);
676   if (pp->is->method->debug > 5)
677      logf (LOG_LOG, "isamd: l8r: sz=%d  p=%d/%d>%d/%d",
678            pp->size, 
679            pp->pos, pp->cat, 
680            isamd_block(pp->next), isamd_type(pp->next) );
681 }
682
683
684
685 /* returns non-zero if item could be read; 0 otherwise */
686 int isamd_pp_read (ISAMD_PP pp, void *buf)
687 {
688
689     return isamd_read_item (pp, (char **) &buf);
690        /* note: isamd_read_item is in merge-d.c, because it is so */
691        /* convoluted with the merge process */
692 }
693
694 /* read one main item from file - decode and store it in *dst.
695    Does not worry about diffs
696    Returns
697      0 if end-of-file
698      1 if item could be read ok
699 */
700 int isamd_read_main_item (ISAMD_PP pp, char **dst)
701 {
702     ISAMD is = pp->is;
703     char *src = pp->buf + pp->offset;
704     int newcat;
705     int oldoffs;
706
707     if (pp->offset >= pp->size)
708     {
709         if (!pp->next)
710         {
711             pp->pos = 0;
712             return 0; /* end of file */
713         }
714         if (pp->next > pp->pos)
715         {
716             if (pp->next == pp->pos + 1)
717                 is->files[pp->cat].no_next++;
718             else
719             {
720                 is->files[pp->cat].no_forward++;
721                 is->files[pp->cat].sum_forward += pp->next - pp->pos;
722             }
723         }
724         else
725         {
726             if (pp->next + 1 == pp->pos)
727                 is->files[pp->cat].no_prev++;
728             else
729             {
730                 is->files[pp->cat].no_backward++;
731                 is->files[pp->cat].sum_backward += pp->pos - pp->next;
732             }
733         }
734         /* out new block position */
735         newcat = isamd_type(pp->next);
736         pp->pos = isamd_block(pp->next);
737         pp->cat = isamd_type(pp->next);
738         pp->is->no_read_main++;
739         src = pp->buf;
740         /* read block and save 'next' and 'size' entry */
741         isamd_read_block (is, pp->cat, pp->pos, src);
742         memcpy (&pp->next, src, sizeof(pp->next));
743         src += sizeof(pp->next);
744         memcpy (&pp->size, src, sizeof(pp->size));
745         src += sizeof(pp->size);
746         /* assume block is non-empty */
747         pp->offset = oldoffs = src - pp->buf; 
748         assert (pp->offset == ISAMD_BLOCK_OFFSET_N);
749         assert (pp->next != isamd_addr(pp->pos,pp->cat));
750         (*is->method->code_reset)(pp->decodeClientData);
751         /* finally, read the item */
752         (*is->method->code_item)(ISAMD_DECODE, pp->decodeClientData, dst, &src);
753         pp->offset = src - pp->buf; 
754         if (is->method->debug > 8)
755             logf (LOG_LOG, "isamd: read_m: block %d:%d sz=%d ofs=%d-%d next=%d",
756                  pp->cat, pp->pos, pp->size, oldoffs, pp->offset, pp->next);
757         return 2;
758     }
759     oldoffs=pp->offset;
760     (*is->method->code_item)(ISAMD_DECODE, pp->decodeClientData, dst, &src);
761     pp->offset = src - pp->buf; 
762     if (is->method->debug > 8)
763         logf (LOG_LOG, "isamd: read_m: got %d:%d sz=%d ofs=%d-%d next=%d",
764              pp->cat, pp->pos, pp->size, oldoffs, pp->offset, pp->next);
765     return 1;
766 }
767
768 int isamd_pp_num (ISAMD_PP pp)
769 {
770     return pp->numKeys;
771 }
772
773 static char *hexdump(unsigned char *p, int len, char *buff) {
774   static char localbuff[128];
775   char bytebuff[8];
776   if (!buff) buff=localbuff;
777   *buff='\0';
778   while (len--) {
779     sprintf(bytebuff,"%02x",*p);
780     p++;
781     strcat(buff,bytebuff);
782     if (len) strcat(buff," ");
783   }
784   return buff;
785 }
786
787
788 void isamd_pp_dump (ISAMD is, ISAMD_P ipos)
789 {
790   ISAMD_PP pp;
791   ISAMD_P oldaddr=0;
792   struct it_key key;
793   int i,n;
794   int occur =0;
795   int oldoffs;
796   int diffmax=1;
797   int diffidx;
798   char hexbuff[64];
799   int olddebug= is->method->debug;
800   is->method->debug=0; /* no debug logs while reading for dump */
801   
802   logf(LOG_LOG,"dumping isamd block %d (%d:%d)",
803                   (int)ipos, isamd_type(ipos), isamd_block(ipos) );
804   pp=isamd_pp_open(is,ipos);
805   logf(LOG_LOG,"numKeys=%d,  ofs=%d sz=%d",
806        pp->numKeys, pp->offset, pp->size );
807   diffidx=oldoffs= pp->offset;
808   while ((diffidx < is->method->filecat[pp->cat].bsize) && (diffmax>0))
809   {
810     memcpy(&diffmax,&(pp->buf[diffidx]),sizeof(int));
811     logf (LOG_LOG,"diff set at %d-%d: %s", diffidx, diffmax, 
812       hexdump(pp->buf+diffidx,8,0)); 
813       /*! todo: dump the actual diffs as well !!! */
814     diffidx=diffmax;
815     
816   } /* dump diffs */
817   while(isamd_pp_read(pp, &key))
818   {
819      if (oldaddr != isamd_addr(pp->pos,pp->cat) )
820      {
821         oldaddr = isamd_addr(pp->pos,pp->cat); 
822         logf(LOG_LOG,"block %d=%d:%d sz=%d nx=%d=%d:%d ofs=%d",
823                   isamd_addr(pp->pos,pp->cat), pp->cat, pp->pos, 
824                   pp->size,
825                   pp->next, isamd_type(pp->next), isamd_block(pp->next),
826                   pp->offset);
827         i=0;      
828         while (i<pp->size) {
829           n=pp->size-i;
830           if (n>8) n=8;
831           logf(LOG_LOG,"  %05x: %s",i,hexdump(pp->buf+i,n,hexbuff));
832           i+=n;
833         }
834         if (oldoffs >  ISAMD_BLOCK_OFFSET_N)
835            oldoffs=ISAMD_BLOCK_OFFSET_N;
836      } /* new block */
837      occur++;
838      logf (LOG_LOG,"    got %d:%d=%x:%x from %s at %d=%x",
839                   key.sysno, key.seqno,
840                   key.sysno, key.seqno,
841                   hexdump(pp->buf+oldoffs, pp->offset-oldoffs, hexbuff),
842                   oldoffs, oldoffs);
843      oldoffs = pp->offset;
844   }
845   /*!*/ /*TODO: dump diffs too!!! */
846   isamd_pp_close(pp);
847   is->method->debug=olddebug;
848 } /* dump */
849
850 /*
851  * $Log: isamd.c,v $
852  * Revision 1.17  1999-10-06 11:46:36  heikki
853  * mproved statistics on isam-d
854  *
855  * Revision 1.16  1999/10/05 09:57:40  heikki
856  * Tuning the isam-d (and fixed a small "detail")
857  *
858  * Revision 1.15  1999/09/27 14:36:36  heikki
859  * singletons
860  *
861  * Revision 1.14  1999/09/23 18:01:18  heikki
862  * singleton optimising
863  *
864  * Revision 1.13  1999/09/20 15:48:06  heikki
865  * Small changes
866  *
867  * Revision 1.12  1999/09/13 13:28:28  heikki
868  * isam-d optimizing: merging input data in the same go
869  *
870  * Revision 1.11  1999/08/25 18:09:24  heikki
871  * Starting to optimize
872  *
873  * Revision 1.10  1999/08/24 13:17:42  heikki
874  * Block sizes, comments
875  *
876  * Revision 1.9  1999/08/20 12:25:58  heikki
877  * Statistics in isamd
878  *
879  * Revision 1.8  1999/08/18 13:28:16  heikki
880  * Set log levels to decent values
881  *
882  * Revision 1.6  1999/08/17 19:44:25  heikki
883  * Fixed memory leaks
884  *
885  * Revision 1.4  1999/08/04 14:21:18  heikki
886  * isam-d seems to be working.
887  *
888  * Revision 1.3  1999/07/21 14:24:50  heikki
889  * isamd write and read functions ok, except when diff block full.
890  * (merge not yet done)
891  *
892  * Revision 1.1  1999/07/14 12:34:43  heikki
893  * Copied from isamh, starting to change things...
894  *
895  *
896  */