1e0a7dd514fa7c2c34afc397bbd5583065dfb352
[idzebra-moved-to-github.git] / isamb / isamb.c
1 /*
2  *  Copyright (c) 2000-2002, Index Data.
3  *  See the file LICENSE for details.
4  *
5  *  $Id: isamb.c,v 1.18 2002-07-15 11:50:45 adam Exp $
6  */
7 #include <yaz/xmalloc.h>
8 #include <yaz/log.h>
9 #include <isamb.h>
10 #include <assert.h>
11
12 struct ISAMB_head {
13     int first_block;
14     int last_block;
15     int block_size;
16     int block_max;
17 };
18
19 #define ISAMB_DATA_OFFSET 3
20
21 #define DST_ITEM_MAX 256
22
23 /* approx 2*4 K + max size of item */
24 #define DST_BUF_SIZE 8448
25
26 #define ISAMB_CACHE_ENTRY_SIZE 4096
27
28 struct ISAMB_cache_entry {
29     ISAMB_P pos;
30     char *buf;
31     int dirty;
32     int hits;
33     struct ISAMB_cache_entry *next;
34 };
35
36
37 struct ISAMB_file {
38     BFile bf;
39     int head_dirty;
40     struct ISAMB_head head;
41     struct ISAMB_cache_entry *cache_entries;
42 };
43
44 struct ISAMB_s {
45     BFiles bfs;
46     ISAMC_M method;
47
48     struct ISAMB_file *file;
49     int no_cat;
50     int cache; /* 0=no cache, 1=use cache, -1=dummy isam (for testing only) */
51 };
52
53 struct ISAMB_block {
54     ISAMB_P pos;
55     int cat;
56     int size;
57     int leaf;
58     int dirty;
59     int offset;
60     char *bytes;
61     unsigned char *buf;
62     void *decodeClientData;
63 };
64
65 struct ISAMB_PP_s {
66     ISAMB isamb;
67     ISAMB_P pos;
68     int level;
69     int total_size;
70     int no_blocks;
71     struct ISAMB_block **block;
72 };
73
74 void encode_ptr (char **dst, int pos)
75 {
76     memcpy (*dst, &pos, sizeof(pos));
77     (*dst) += sizeof(pos);
78 }
79
80 void decode_ptr (char **src, int *pos)
81 {
82     memcpy (pos, *src, sizeof(*pos));
83     (*src) += sizeof(*pos);
84 }
85
86 ISAMB isamb_open (BFiles bfs, const char *name, int writeflag, ISAMC_M method,
87                   int cache)
88 {
89     ISAMB isamb = xmalloc (sizeof(*isamb));
90     int i, b_size = 32;
91
92     isamb->bfs = bfs;
93     isamb->method = (ISAMC_M) xmalloc (sizeof(*method));
94     memcpy (isamb->method, method, sizeof(*method));
95     isamb->no_cat = 4;
96     isamb->cache = cache;
97
98     isamb->file = xmalloc (sizeof(*isamb->file) * isamb->no_cat);
99     for (i = 0; i<isamb->no_cat; i++)
100     {
101         char fname[DST_BUF_SIZE];
102         isamb->file[i].cache_entries = 0;
103         isamb->file[i].head_dirty = 0;
104         sprintf (fname, "%s%c", name, i+'A');
105         if (cache)
106             isamb->file[i].bf = bf_open (bfs, fname, ISAMB_CACHE_ENTRY_SIZE,
107                                          writeflag);
108         else
109             isamb->file[i].bf = bf_open (bfs, fname, b_size, writeflag);
110
111         
112         if (!bf_read (isamb->file[i].bf, 0, 0, sizeof(struct ISAMB_head),
113                       &isamb->file[i].head))
114         {
115             isamb->file[i].head.first_block = ISAMB_CACHE_ENTRY_SIZE/b_size+1;
116             isamb->file[i].head.last_block = isamb->file[i].head.first_block;
117             isamb->file[i].head.block_size = b_size;
118             isamb->file[i].head.block_max = b_size - ISAMB_DATA_OFFSET;
119         }
120         assert (isamb->file[i].head.block_size >= ISAMB_DATA_OFFSET);
121         isamb->file[i].head_dirty = 0;
122         assert(isamb->file[i].head.block_size == b_size);
123         b_size = b_size * 4;
124     }
125     return isamb;
126 }
127
128 static void flush_blocks (ISAMB b, int cat)
129 {
130     while (b->file[cat].cache_entries)
131     {
132         struct ISAMB_cache_entry *ce_this = b->file[cat].cache_entries;
133         b->file[cat].cache_entries = ce_this->next;
134
135         if (ce_this->dirty)
136             bf_write (b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
137         xfree (ce_this->buf);
138         xfree (ce_this);
139     }
140 }
141
142 static int get_block (ISAMB b, ISAMC_P pos, char *userbuf, int wr)
143 {
144     int cat = pos&3;
145     int off = ((pos/4) & 
146                (ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size - 1))
147         * b->file[cat].head.block_size;
148     int norm = pos / (4*ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size);
149     int no = 0;
150     struct ISAMB_cache_entry **ce, *ce_this = 0, **ce_last = 0;
151
152     if (!b->cache)
153         return 0;
154
155     assert (ISAMB_CACHE_ENTRY_SIZE >= b->file[cat].head.block_size);
156     for (ce = &b->file[cat].cache_entries; *ce; ce = &(*ce)->next, no++)
157     {
158         ce_last = ce;
159         if ((*ce)->pos == norm)
160         {
161             ce_this = *ce;
162             *ce = (*ce)->next;   /* remove from list */
163             
164             ce_this->next = b->file[cat].cache_entries;  /* move to front */
165             b->file[cat].cache_entries = ce_this;
166             
167             if (wr)
168             {
169                 memcpy (ce_this->buf + off, userbuf, 
170                         b->file[cat].head.block_size);
171                 ce_this->dirty = 1;
172             }
173             else
174                 memcpy (userbuf, ce_this->buf + off,
175                         b->file[cat].head.block_size);
176             return 1;
177         }
178     }
179     if (no >= 40)
180     {
181         assert (no == 40);
182         assert (ce_last && *ce_last);
183         ce_this = *ce_last;
184         *ce_last = 0;  /* remove the last entry from list */
185         if (ce_this->dirty)
186             bf_write (b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
187         xfree (ce_this->buf);
188         xfree (ce_this);
189     }
190     ce_this = xmalloc (sizeof(*ce_this));
191     ce_this->next = b->file[cat].cache_entries;
192     b->file[cat].cache_entries = ce_this;
193     ce_this->buf = xmalloc (ISAMB_CACHE_ENTRY_SIZE);
194     ce_this->pos = norm;
195     if (!bf_read (b->file[cat].bf, norm, 0, 0, ce_this->buf))
196         memset (ce_this->buf, 0, ISAMB_CACHE_ENTRY_SIZE);
197     if (wr)
198     {
199         memcpy (ce_this->buf + off, userbuf, b->file[cat].head.block_size);
200         ce_this->dirty = 1;
201     }
202     else
203     {
204         ce_this->dirty = 0;
205         memcpy (userbuf, ce_this->buf + off, b->file[cat].head.block_size);
206     }
207     return 1;
208 }
209
210
211 void isamb_close (ISAMB isamb)
212 {
213     int i;
214     for (i = 0; i<isamb->no_cat; i++)
215     {
216         flush_blocks (isamb, i);
217         if (isamb->file[i].head_dirty)
218             bf_write (isamb->file[i].bf, 0, 0,
219                       sizeof(struct ISAMB_head), &isamb->file[i].head);
220         
221     }
222     xfree (isamb->file);
223     xfree (isamb->method);
224     xfree (isamb);
225 }
226
227
228 struct ISAMB_block *open_block (ISAMB b, ISAMC_P pos)
229 {
230     int cat = pos&3;
231     struct ISAMB_block *p;
232     if (!pos)
233         return 0;
234     p = xmalloc (sizeof(*p));
235     p->pos = pos;
236     p->cat = pos & 3;
237     p->buf = xmalloc (b->file[cat].head.block_size);
238
239     if (!get_block (b, pos, p->buf, 0))
240     {
241         if (!bf_read (b->file[cat].bf, pos/4, 0, 0, p->buf))
242         {
243             yaz_log (LOG_FATAL, "read failure for pos=%ld block=%ld",
244                      (long) pos, (long) pos/4);
245             abort();
246         }
247     }
248     p->bytes = p->buf + ISAMB_DATA_OFFSET;
249     p->leaf = p->buf[0];
250     p->size = p->buf[1] + 256 * p->buf[2] - ISAMB_DATA_OFFSET;
251     p->offset = 0;
252     p->dirty = 0;
253     p->decodeClientData = (*b->method->code_start)(ISAMC_DECODE);
254     return p;
255 }
256
257 struct ISAMB_block *new_block (ISAMB b, int leaf, int cat)
258 {
259     struct ISAMB_block *p;
260     int block_no;
261     
262     p = xmalloc (sizeof(*p));
263     block_no = b->file[cat].head.last_block++;
264     p->cat = cat;
265     p->pos = block_no * 4 + cat;
266     p->cat = cat;
267     b->file[cat].head_dirty = 1;
268     p->buf = xmalloc (b->file[cat].head.block_size);
269     memset (p->buf, 0, b->file[cat].head.block_size);
270     p->bytes = p->buf + ISAMB_DATA_OFFSET;
271     p->leaf = leaf;
272     p->size = 0;
273     p->dirty = 1;
274     p->offset = 0;
275     p->decodeClientData = (*b->method->code_start)(ISAMC_DECODE);
276     return p;
277 }
278
279 struct ISAMB_block *new_leaf (ISAMB b, int cat)
280 {
281     return new_block (b, 1, cat);
282 }
283
284
285 struct ISAMB_block *new_int (ISAMB b, int cat)
286 {
287     return new_block (b, 0, cat);
288 }
289
290 void close_block (ISAMB b, struct ISAMB_block *p)
291 {
292     if (!p)
293         return;
294     if (p->dirty)
295     {
296         int size = p->size + ISAMB_DATA_OFFSET;
297         p->buf[0] = p->leaf;
298         p->buf[1] = size & 255;
299         p->buf[2] = size >> 8;
300         if (!get_block (b, p->pos, p->buf, 1))
301             bf_write (b->file[p->cat].bf, p->pos/4, 0, 0, p->buf);
302     }
303     (*b->method->code_stop)(ISAMC_DECODE, p->decodeClientData);
304     xfree (p->buf);
305     xfree (p);
306 }
307
308 int insert_sub (ISAMB b, struct ISAMB_block **p,
309                 void *new_item, int *mode,
310                 ISAMC_I stream,
311                 struct ISAMB_block **sp,
312                 void *sub_item, int *sub_size,
313                 void *max_item);
314
315 int insert_int (ISAMB b, struct ISAMB_block *p, void *lookahead_item,
316                 int *mode,
317                 ISAMC_I stream, struct ISAMB_block **sp,
318                 void *split_item, int *split_size, void *last_max_item)
319 {
320     char *startp = p->bytes;
321     char *src = startp;
322     char *endp = p->bytes + p->size;
323     int pos;
324     struct ISAMB_block *sub_p1 = 0, *sub_p2 = 0;
325     char sub_item[DST_ITEM_MAX];
326     int sub_size;
327     int more;
328
329     *sp = 0;
330
331     decode_ptr (&src, &pos);
332     while (src != endp)
333     {
334         int item_len;
335         int d;
336         decode_ptr (&src, &item_len);
337         d = (*b->method->compare_item)(src, lookahead_item);
338         if (d > 0)
339         {
340             sub_p1 = open_block (b, pos);
341             assert (sub_p1);
342             more = insert_sub (b, &sub_p1, lookahead_item, mode,
343                                stream, &sub_p2, 
344                                sub_item, &sub_size, src);
345             break;
346         }
347         src += item_len;
348         decode_ptr (&src, &pos);
349     }
350     if (!sub_p1)
351     {
352         sub_p1 = open_block (b, pos);
353         assert (sub_p1);
354         more = insert_sub (b, &sub_p1, lookahead_item, mode, stream, &sub_p2, 
355                            sub_item, &sub_size, last_max_item);
356     }
357     if (sub_p2)
358     {
359         /* there was a split - must insert pointer in this one */
360         char dst_buf[DST_BUF_SIZE];
361         char *dst = dst_buf;
362
363         assert (sub_size < 30 && sub_size > 1);
364
365         memcpy (dst, startp, src - startp);
366                 
367         dst += src - startp;
368
369         encode_ptr (&dst, sub_size);      /* sub length and item */
370         memcpy (dst, sub_item, sub_size);
371         dst += sub_size;
372
373         encode_ptr (&dst, sub_p2->pos);   /* pos */
374
375         if (endp - src)                   /* remaining data */
376         {
377             memcpy (dst, src, endp - src);
378             dst += endp - src;
379         }
380         p->size = dst - dst_buf;
381         if (p->size <= b->file[p->cat].head.block_max)
382         {
383             memcpy (startp, dst_buf, dst - dst_buf);
384         }
385         else
386         {
387             int p_new_size;
388             char *half;
389             src = dst_buf;
390             endp = dst;
391
392             half = src + b->file[p->cat].head.block_size/2;
393             decode_ptr (&src, &pos);
394             while (src <= half)
395             {
396                 decode_ptr (&src, split_size);
397                 src += *split_size;
398                 decode_ptr (&src, &pos);
399             }
400             p_new_size = src - dst_buf;
401             memcpy (p->bytes, dst_buf, p_new_size);
402
403             decode_ptr (&src, split_size);
404             memcpy (split_item, src, *split_size);
405             src += *split_size;
406
407             *sp = new_int (b, p->cat);
408             (*sp)->size = endp - src;
409             memcpy ((*sp)->bytes, src, (*sp)->size);
410
411             p->size = p_new_size;
412         }
413         p->dirty = 1;
414         close_block (b, sub_p2);
415     }
416     close_block (b, sub_p1);
417     return more;
418 }
419
420
421 int insert_leaf (ISAMB b, struct ISAMB_block **sp1, void *lookahead_item,
422                  int *lookahead_mode, ISAMC_I stream, struct ISAMB_block **sp2,
423                  void *sub_item, int *sub_size,
424                  void *max_item)
425 {
426     struct ISAMB_block *p = *sp1;
427     char *src = 0, *endp = 0;
428     char dst_buf[DST_BUF_SIZE], *dst = dst_buf;
429     int new_size;
430     void *c1 = (*b->method->code_start)(ISAMC_DECODE);
431     void *c2 = (*b->method->code_start)(ISAMC_ENCODE);
432     int more = 1;
433     int quater = b->file[b->no_cat-1].head.block_max / 4;
434     char *cut = dst_buf + quater * 2;
435     char *maxp = dst_buf + b->file[b->no_cat-1].head.block_max;
436     char *half1 = 0;
437     char *half2 = 0;
438     char cut_item_buf[DST_ITEM_MAX];
439     int cut_item_size = 0;
440
441     if (p && p->size)
442     {
443         char file_item_buf[DST_ITEM_MAX];
444         char *file_item = file_item_buf;
445             
446         src = p->bytes;
447         endp = p->bytes + p->size;
448         (*b->method->code_item)(ISAMC_DECODE, c1, &file_item, &src);
449         while (1)
450         {
451             char *dst_item = 0;
452             char *dst_0 = dst;
453             char *lookahead_next;
454             int d = -1;
455             
456             if (lookahead_item)
457                 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
458             
459             if (d > 0)
460             {
461                 dst_item = lookahead_item;
462                 assert (*lookahead_mode);
463             }
464             else
465                 dst_item = file_item_buf;
466             if (!*lookahead_mode && d == 0)
467             {
468                 p->dirty = 1;
469             }
470             else if (!half1 && dst > cut)
471             {
472                 char *dst_item_0 = dst_item;
473                 half1 = dst; /* candidate for splitting */
474                 
475                 (*b->method->code_item)(ISAMC_ENCODE, c2, &dst, &dst_item);
476                 
477                 cut_item_size = dst_item - dst_item_0;
478                 memcpy (cut_item_buf, dst_item_0, cut_item_size);
479                 
480                 half2 = dst;
481             }
482             else
483                 (*b->method->code_item)(ISAMC_ENCODE, c2, &dst, &dst_item);
484             if (d > 0)  
485             {
486                 if (dst > maxp)
487                 {
488                     dst = dst_0;
489                     lookahead_item = 0;
490                 }
491                 else
492                 {
493                     lookahead_next = lookahead_item;
494                     if (!(*stream->read_item)(stream->clientData,
495                                               &lookahead_next,
496                                               lookahead_mode))
497                     {
498                         lookahead_item = 0;
499                         more = 0;
500                     }
501                     if (lookahead_item && max_item &&
502                         (*b->method->compare_item)(max_item, lookahead_item) <= 0)
503                     {
504                         /* max_item 1 */
505                         lookahead_item = 0;
506                     }
507                     
508                     p->dirty = 1;
509                 }
510             }
511             else if (d == 0)
512             {
513                 lookahead_next = lookahead_item;
514                 if (!(*stream->read_item)(stream->clientData,
515                                           &lookahead_next, lookahead_mode))
516                 {
517                     lookahead_item = 0;
518                     more = 0;
519                 }
520                 if (src == endp)
521                     break;
522                 file_item = file_item_buf;
523                 (*b->method->code_item)(ISAMC_DECODE, c1, &file_item, &src);
524             }
525             else
526             {
527                 if (src == endp)
528                     break;
529                 file_item = file_item_buf;
530                 (*b->method->code_item)(ISAMC_DECODE, c1, &file_item, &src);
531             }
532         }
533     }
534     maxp = dst_buf + b->file[b->no_cat-1].head.block_max + quater;
535     while (lookahead_item)
536     {
537         char *dst_item = lookahead_item;
538         char *dst_0 = dst;
539         
540         if (max_item &&
541             (*b->method->compare_item)(max_item, lookahead_item) <= 0)
542         {
543             /* max_item 2 */
544             break;
545         }
546         if (!*lookahead_mode)
547         {
548             yaz_log (LOG_WARN, "Inconsistent register (2)");
549             abort();
550         }
551         else if (!half1 && dst > cut)   
552         {
553             char *dst_item_0 = dst_item;
554             half1 = dst; /* candidate for splitting */
555             
556             (*b->method->code_item)(ISAMC_ENCODE, c2, &dst, &dst_item);
557             
558             cut_item_size = dst_item - dst_item_0;
559             memcpy (cut_item_buf, dst_item_0, cut_item_size);
560             
561             half2 = dst;
562         }
563         else
564             (*b->method->code_item)(ISAMC_ENCODE, c2, &dst, &dst_item);
565
566         if (dst > maxp)
567         {
568             dst = dst_0;
569             break;
570         }
571         if (p)
572             p->dirty = 1;
573         dst_item = lookahead_item;
574         if (!(*stream->read_item)(stream->clientData, &dst_item,
575                                   lookahead_mode))
576         {
577             lookahead_item = 0;
578             more = 0;
579         }
580     }
581     new_size = dst - dst_buf;
582     if (p && p->cat != b->no_cat-1 && 
583         new_size > b->file[p->cat].head.block_max)
584     {
585         /* non-btree block will be removed */
586         close_block (b, p);
587         /* delete it too!! */
588         p = 0; /* make a new one anyway */
589     }
590     if (!p)
591     {   /* must create a new one */
592         int i;
593         for (i = 0; i < b->no_cat; i++)
594             if (new_size <= b->file[i].head.block_max)
595                 break;
596         if (i == b->no_cat)
597             i = b->no_cat - 1;
598         p = new_leaf (b, i);
599     }
600     if (new_size > b->file[p->cat].head.block_max)
601     {
602         char *first_dst;
603         char *cut_item = cut_item_buf;
604
605         assert (half1);
606         assert (half2);
607
608        /* first half */
609         p->size = half1 - dst_buf;
610         memcpy (p->bytes, dst_buf, half1 - dst_buf);
611
612         /* second half */
613         *sp2 = new_leaf (b, p->cat);
614
615         (*b->method->code_reset)(c2);
616
617         first_dst = (*sp2)->bytes;
618
619         (*b->method->code_item)(ISAMC_ENCODE, c2, &first_dst, &cut_item);
620
621         memcpy (first_dst, half2, dst - half2);
622
623         (*sp2)->size = (first_dst - (*sp2)->bytes) + (dst - half2);
624         (*sp2)->dirty = 1;
625         p->dirty = 1;
626         memcpy (sub_item, cut_item_buf, cut_item_size);
627         *sub_size = cut_item_size;
628     }
629     else
630     {
631         memcpy (p->bytes, dst_buf, dst - dst_buf);
632         p->size = new_size;
633     }
634     (*b->method->code_stop)(ISAMC_DECODE, c1);
635     (*b->method->code_stop)(ISAMC_ENCODE, c2);
636     *sp1 = p;
637     return more;
638 }
639
640 int insert_sub (ISAMB b, struct ISAMB_block **p, void *new_item,
641                 int *mode,
642                 ISAMC_I stream,
643                 struct ISAMB_block **sp,
644                 void *sub_item, int *sub_size,
645                 void *max_item)
646 {
647     if (!*p || (*p)->leaf)
648         return insert_leaf (b, p, new_item, mode, stream, sp, sub_item, 
649                             sub_size, max_item);
650     else
651         return insert_int (b, *p, new_item, mode, stream, sp, sub_item,
652                            sub_size, max_item);
653 }
654
655 int isamb_merge (ISAMB b, ISAMC_P pos, ISAMC_I stream)
656 {
657     char item_buf[DST_ITEM_MAX];
658     char *item_ptr;
659     int i_mode;
660     int more;
661
662     if (b->cache < 0)
663     {
664         int more = 1;
665         while (more)
666         {
667             item_ptr = item_buf;
668             more =
669                 (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
670         }
671         return 1;
672     }
673     item_ptr = item_buf;
674     more = (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
675     while (more)
676     {
677         struct ISAMB_block *p = 0, *sp = 0;
678         char sub_item[DST_ITEM_MAX];
679         int sub_size;
680         
681         if (pos)
682             p = open_block (b, pos);
683         more = insert_sub (b, &p, item_buf, &i_mode, stream, &sp,
684                             sub_item, &sub_size, 0);
685         if (sp)
686         {    /* increase level of tree by one */
687             struct ISAMB_block *p2 = new_int (b, p->cat);
688             char *dst = p2->bytes + p2->size;
689             
690             encode_ptr (&dst, p->pos);
691             assert (sub_size < 20);
692             encode_ptr (&dst, sub_size);
693             memcpy (dst, sub_item, sub_size);
694             dst += sub_size;
695             encode_ptr (&dst, sp->pos);
696             
697             p2->size = dst - p2->bytes;
698             pos = p2->pos;  /* return new super page */
699             close_block (b, sp);
700             close_block (b, p2);
701         }
702         else
703             pos = p->pos;   /* return current one (again) */
704         close_block (b, p);
705     }
706     return pos;
707 }
708
709 ISAMB_PP isamb_pp_open_x (ISAMB isamb, ISAMB_P pos, int *level)
710 {
711     ISAMB_PP pp = xmalloc (sizeof(*pp));
712
713     pp->isamb = isamb;
714     pp->block = xmalloc (10 * sizeof(*pp->block));
715
716     pp->pos = pos;
717     pp->level = 0;
718     pp->total_size = 0;
719     pp->no_blocks = 0;
720     while (1)
721     {
722         struct ISAMB_block *p = open_block (isamb, pos);
723         char *src = p->bytes + p->offset;
724         pp->block[pp->level] = p;
725
726         pp->total_size += p->size;
727         pp->no_blocks++;
728
729         if (p->leaf)
730             break;
731
732         decode_ptr (&src, &pos);
733         p->offset = src - p->bytes;
734         pp->level++;
735     }
736     pp->block[pp->level+1] = 0;
737     if (level)
738         *level = pp->level;
739     return pp;
740 }
741
742 ISAMB_PP isamb_pp_open (ISAMB isamb, ISAMB_P pos)
743 {
744     return isamb_pp_open_x (isamb, pos, 0);
745 }
746
747 void isamb_pp_close_x (ISAMB_PP pp, int *size, int *blocks)
748 {
749     int i;
750     if (!pp)
751         return;
752     if (size)
753         *size = pp->total_size;
754     if (blocks)
755         *blocks = pp->no_blocks;
756     for (i = 0; i <= pp->level; i++)
757         close_block (pp->isamb, pp->block[i]);
758     xfree (pp->block);
759     xfree (pp);
760 }
761
762 int isamb_block_info (ISAMB isamb, int cat)
763 {
764     if (cat >= 0 && cat < isamb->no_cat)
765         return isamb->file[cat].head.block_size;
766     return -1;
767 }
768
769 void isamb_pp_close (ISAMB_PP pp)
770 {
771     return isamb_pp_close_x (pp, 0, 0);
772 }
773
774 int isamb_pp_read (ISAMB_PP pp, void *buf)
775 {
776     char *dst = buf;
777     char *src;
778     struct ISAMB_block *p = pp->block[pp->level];
779     if (!p)
780         return 0;
781
782     while (p->offset == p->size)
783     {
784         int pos, item_len;
785         while (p->offset == p->size)
786         {
787             if (pp->level == 0)
788                 return 0;
789             close_block (pp->isamb, pp->block[pp->level]);
790             pp->block[pp->level] = 0;
791             (pp->level)--;
792             p = pp->block[pp->level];
793             assert (!p->leaf);  /* must be int */
794         }
795         src = p->bytes + p->offset;
796         
797         decode_ptr (&src, &item_len);
798         src += item_len;
799         decode_ptr (&src, &pos);
800         
801         p->offset = src - (char*) p->bytes;
802
803         ++(pp->level);
804         
805         while (1)
806         {
807             pp->block[pp->level] = p = open_block (pp->isamb, pos);
808
809             pp->total_size += p->size;
810             pp->no_blocks++;
811             
812             if (p->leaf) /* leaf */
813             {
814                 break;
815             }
816             src = p->bytes + p->offset;
817             decode_ptr (&src, &pos);
818             p->offset = src - (char*) p->bytes;
819             pp->level++;
820         }
821     }
822     assert (p->offset < p->size);
823     assert (p->leaf);
824     src = p->bytes + p->offset;
825     (*pp->isamb->method->code_item)(ISAMC_DECODE, p->decodeClientData,
826                                     &dst, &src);
827     p->offset = src - (char*) p->bytes;
828     return 1;
829 }
830
831 int isamb_pp_num (ISAMB_PP pp)
832 {
833     return 1;
834 }