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