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