1 /* $Id: isamb.c,v 1.69 2005-01-15 19:38:31 adam Exp $
2 Copyright (C) 1995-2005
5 This file is part of the Zebra server.
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
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
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
25 #include <yaz/xmalloc.h>
26 #include <idzebra/isamb.h>
34 #define ISAMB_MAJOR_VERSION 3
35 #define ISAMB_MINOR_VERSION 0
47 /* if 1, upper nodes items are encoded; 0 if not encoded */
50 /* maximum size of encoded buffer */
51 #define DST_ITEM_MAX 256
53 #define ISAMB_MAX_LEVEL 10
54 /* approx 2*max page + max size of item */
55 #define DST_BUF_SIZE 16840
57 #define ISAMB_CACHE_ENTRY_SIZE 4096
59 /* CAT_MAX: _must_ be power of 2 */
61 #define CAT_MASK (CAT_MAX-1)
62 /* CAT_NO: <= CAT_MAX */
65 /* Smallest block size */
66 #define ISAMB_MIN_SIZE 32
68 #define ISAMB_FAC_SIZE 4
70 /* ISAMB_PTR_CODEC = 1 var, =0 fixed */
71 #define ISAMB_PTR_CODEC 1
73 struct ISAMB_cache_entry {
78 struct ISAMB_cache_entry *next;
84 struct ISAMB_head head;
85 struct ISAMB_cache_entry *cache_entries;
92 struct ISAMB_file *file;
94 int cache; /* 0 = no cache, 1 = use cache, -1 = dummy isam (for testing only) */
95 int log_io; /* log level for bf_read/bf_write calls */
96 int log_freelist; /* log level for freelist handling */
97 zint skipped_numbers; /* on a leaf node */
98 zint returned_numbers;
99 zint skipped_nodes[ISAMB_MAX_LEVEL]; /* [0]=skipped leaves, 1 = higher etc */
100 zint accessed_nodes[ISAMB_MAX_LEVEL]; /* nodes we did not skip */
111 zint no_items; /* number of nodes in this + children */
115 void *decodeClientData;
123 int maxlevel; /* total depth */
126 zint skipped_numbers; /* on a leaf node */
127 zint returned_numbers;
128 zint skipped_nodes[ISAMB_MAX_LEVEL]; /* [0]=skipped leaves, 1 = higher etc */
129 zint accessed_nodes[ISAMB_MAX_LEVEL]; /* nodes we did not skip */
130 struct ISAMB_block **block;
131 int scope; /* on what level we forward */
135 #define encode_item_len encode_ptr
137 static void encode_ptr(char **dst, zint pos)
139 unsigned char *bp = (unsigned char*) *dst;
143 *bp++ = 128 | (pos & 127);
150 static void encode_ptr(char **dst, zint pos)
152 memcpy(*dst, &pos, sizeof(pos));
153 (*dst) += sizeof(pos);
157 #define decode_item_len decode_ptr
159 static void decode_ptr(const char **src1, zint *pos)
161 const unsigned char **src = (const unsigned char **) src1;
166 while (((c = *(*src)++) & 128))
168 d += ((zint) (c & 127) << r);
171 d += ((zint) c << r);
175 static void decode_ptr(const char **src, zint *pos)
177 memcpy(pos, *src, sizeof(*pos));
178 (*src) += sizeof(*pos);
182 ISAMB isamb_open(BFiles bfs, const char *name, int writeflag, ISAMC_M *method,
185 ISAMB isamb = xmalloc(sizeof(*isamb));
186 int i, b_size = ISAMB_MIN_SIZE;
189 isamb->method = (ISAMC_M *) xmalloc(sizeof(*method));
190 memcpy (isamb->method, method, sizeof(*method));
191 isamb->no_cat = CAT_NO;
193 isamb->log_freelist = 0;
194 isamb->cache = cache;
195 isamb->skipped_numbers = 0;
196 isamb->returned_numbers = 0;
197 for (i = 0;i<ISAMB_MAX_LEVEL;i++)
198 isamb->skipped_nodes[i]= isamb->accessed_nodes[i]=0;
201 isamb->file = xmalloc(sizeof(*isamb->file) * isamb->no_cat);
202 for (i = 0; i < isamb->no_cat; i++)
204 char fname[DST_BUF_SIZE];
205 char hbuf[DST_BUF_SIZE];
206 isamb->file[i].cache_entries = 0;
207 isamb->file[i].head_dirty = 0;
208 sprintf(fname, "%s%c", name, i+'A');
210 isamb->file[i].bf = bf_open(bfs, fname, ISAMB_CACHE_ENTRY_SIZE,
213 isamb->file[i].bf = bf_open(bfs, fname, b_size, writeflag);
215 /* fill-in default values (for empty isamb) */
216 isamb->file[i].head.first_block = ISAMB_CACHE_ENTRY_SIZE/b_size+1;
217 isamb->file[i].head.last_block = isamb->file[i].head.first_block;
218 isamb->file[i].head.block_size = b_size;
220 if (i == isamb->no_cat-1 || b_size > 128)
221 isamb->file[i].head.block_offset = 8;
223 isamb->file[i].head.block_offset = 4;
225 isamb->file[i].head.block_offset = 11;
227 isamb->file[i].head.block_max =
228 b_size - isamb->file[i].head.block_offset;
229 isamb->file[i].head.free_list = 0;
230 if (bf_read(isamb->file[i].bf, 0, 0, 0, hbuf))
232 /* got header assume "isamb"major minor len can fit in 16 bytes */
234 int major, minor, len, pos = 0;
237 if (memcmp(hbuf, "isamb", 5))
239 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
242 if (sscanf(hbuf+5, "%d %d %d", &major, &minor, &len) != 3)
244 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
247 if (major != ISAMB_MAJOR_VERSION)
249 yaz_log(YLOG_WARN, "bad major version for file %s %d, must be %d",
250 fname, major, ISAMB_MAJOR_VERSION);
253 for (left = len - b_size; left > 0; left = left - b_size)
256 if (!bf_read(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size))
258 yaz_log(YLOG_WARN, "truncated isamb header for "
259 "file=%s len=%d pos=%d",
265 decode_ptr(&src, &isamb->file[i].head.first_block);
266 decode_ptr(&src, &isamb->file[i].head.last_block);
267 decode_ptr(&src, &zint_tmp);
268 isamb->file[i].head.block_size = zint_tmp;
269 decode_ptr(&src, &zint_tmp);
270 isamb->file[i].head.block_max = zint_tmp;
271 decode_ptr(&src, &isamb->file[i].head.free_list);
273 assert (isamb->file[i].head.block_size >= isamb->file[i].head.block_offset);
274 isamb->file[i].head_dirty = 0;
275 assert(isamb->file[i].head.block_size == b_size);
276 b_size = b_size * ISAMB_FAC_SIZE;
279 yaz_log(YLOG_WARN, "isamb debug enabled. Things will be slower than usual");
284 static void flush_blocks (ISAMB b, int cat)
286 while (b->file[cat].cache_entries)
288 struct ISAMB_cache_entry *ce_this = b->file[cat].cache_entries;
289 b->file[cat].cache_entries = ce_this->next;
293 yaz_log(b->log_io, "bf_write: flush_blocks");
294 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
301 static int get_block (ISAMB b, ISAMC_P pos, char *userbuf, int wr)
303 int cat = (int) (pos&CAT_MASK);
304 int off = (int) (((pos/CAT_MAX) &
305 (ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size - 1))
306 * b->file[cat].head.block_size);
307 zint norm = pos / (CAT_MASK*ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size);
309 struct ISAMB_cache_entry **ce, *ce_this = 0, **ce_last = 0;
314 assert (ISAMB_CACHE_ENTRY_SIZE >= b->file[cat].head.block_size);
315 for (ce = &b->file[cat].cache_entries; *ce; ce = &(*ce)->next, no++)
318 if ((*ce)->pos == norm)
321 *ce = (*ce)->next; /* remove from list */
323 ce_this->next = b->file[cat].cache_entries; /* move to front */
324 b->file[cat].cache_entries = ce_this;
328 memcpy (ce_this->buf + off, userbuf,
329 b->file[cat].head.block_size);
333 memcpy (userbuf, ce_this->buf + off,
334 b->file[cat].head.block_size);
341 assert (ce_last && *ce_last);
343 *ce_last = 0; /* remove the last entry from list */
346 yaz_log(b->log_io, "bf_write: get_block");
347 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
352 ce_this = xmalloc(sizeof(*ce_this));
353 ce_this->next = b->file[cat].cache_entries;
354 b->file[cat].cache_entries = ce_this;
355 ce_this->buf = xmalloc(ISAMB_CACHE_ENTRY_SIZE);
357 yaz_log(b->log_io, "bf_read: get_block");
358 if (!bf_read(b->file[cat].bf, norm, 0, 0, ce_this->buf))
359 memset (ce_this->buf, 0, ISAMB_CACHE_ENTRY_SIZE);
362 memcpy (ce_this->buf + off, userbuf, b->file[cat].head.block_size);
368 memcpy (userbuf, ce_this->buf + off, b->file[cat].head.block_size);
374 void isamb_close (ISAMB isamb)
377 for (i = 0;isamb->accessed_nodes[i];i++)
378 yaz_log(YLOG_DEBUG, "isamb_close level leaf-%d: "ZINT_FORMAT" read, "
379 ZINT_FORMAT" skipped",
380 i, isamb->accessed_nodes[i], isamb->skipped_nodes[i]);
381 yaz_log(YLOG_DEBUG, "isamb_close returned "ZINT_FORMAT" values, "
382 "skipped "ZINT_FORMAT,
383 isamb->skipped_numbers, isamb->returned_numbers);
384 for (i = 0; i<isamb->no_cat; i++)
386 flush_blocks (isamb, i);
387 if (isamb->file[i].head_dirty)
389 char hbuf[DST_BUF_SIZE];
390 int major = ISAMB_MAJOR_VERSION;
391 int minor = ISAMB_MINOR_VERSION;
393 char *dst = hbuf + 16;
395 int b_size = isamb->file[i].head.block_size;
397 encode_ptr(&dst, isamb->file[i].head.first_block);
398 encode_ptr(&dst, isamb->file[i].head.last_block);
399 encode_ptr(&dst, isamb->file[i].head.block_size);
400 encode_ptr(&dst, isamb->file[i].head.block_max);
401 encode_ptr(&dst, isamb->file[i].head.free_list);
402 memset(dst, '\0', b_size); /* ensure no random bytes are written */
406 /* print exactly 16 bytes (including trailing 0) */
407 sprintf(hbuf, "isamb%02d %02d %02d\r\n", major, minor, len);
409 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf);
411 for (left = len - b_size; left > 0; left = left - b_size)
414 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size);
417 bf_close (isamb->file[i].bf);
420 xfree(isamb->method);
424 /* open_block: read one block at pos.
425 Decode leading sys bytes .. consisting of
427 0: leader byte, != 0 leaf, == 0, non-leaf
428 1-2: used size of block
429 3-7*: number of items and all children
431 * Reserve 5 bytes for large block sizes. 1 for small ones .. Number
432 of items. We can thus have at most 2^40 nodes.
434 static struct ISAMB_block *open_block(ISAMB b, ISAMC_P pos)
436 int cat = (int) (pos&CAT_MASK);
438 int offset = b->file[cat].head.block_offset;
439 struct ISAMB_block *p;
442 p = xmalloc(sizeof(*p));
444 p->cat = (int) (pos & CAT_MASK);
445 p->buf = xmalloc(b->file[cat].head.block_size);
448 if (!get_block (b, pos, p->buf, 0))
450 yaz_log(b->log_io, "bf_read: open_block");
451 if (!bf_read(b->file[cat].bf, pos/CAT_MAX, 0, 0, p->buf))
453 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
454 (long) pos, (long) pos/CAT_MAX);
458 p->bytes = p->buf + offset;
460 p->size = (p->buf[1] + 256 * p->buf[2]) - offset;
463 yaz_log(YLOG_FATAL, "Bad block size %d in pos=" ZINT_FORMAT "\n",
466 assert (p->size >= 0);
468 decode_ptr(&src, &p->no_items);
473 p->decodeClientData = (*b->method->codec.start)();
477 struct ISAMB_block *new_block (ISAMB b, int leaf, int cat)
479 struct ISAMB_block *p;
481 p = xmalloc(sizeof(*p));
482 p->buf = xmalloc(b->file[cat].head.block_size);
484 if (!b->file[cat].head.free_list)
487 block_no = b->file[cat].head.last_block++;
488 p->pos = block_no * CAT_MAX + cat;
492 p->pos = b->file[cat].head.free_list;
493 assert((p->pos & CAT_MASK) == cat);
494 if (!get_block (b, p->pos, p->buf, 0))
496 yaz_log(b->log_io, "bf_read: new_block");
497 if (!bf_read(b->file[cat].bf, p->pos/CAT_MAX, 0, 0, p->buf))
499 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
500 (long) p->pos/CAT_MAX, (long) p->pos/CAT_MAX);
504 yaz_log(b->log_freelist, "got block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT, p->pos,
505 cat, p->pos/CAT_MAX);
506 memcpy (&b->file[cat].head.free_list, p->buf, sizeof(zint));
509 b->file[cat].head_dirty = 1;
510 memset (p->buf, 0, b->file[cat].head.block_size);
511 p->bytes = p->buf + b->file[cat].head.block_offset;
518 p->decodeClientData = (*b->method->codec.start)();
522 struct ISAMB_block *new_leaf (ISAMB b, int cat)
524 return new_block (b, 1, cat);
528 struct ISAMB_block *new_int (ISAMB b, int cat)
530 return new_block (b, 0, cat);
533 static void check_block (ISAMB b, struct ISAMB_block *p)
535 assert(b); /* mostly to make the compiler shut up about unused b */
543 char *startp = p->bytes;
544 const char *src = startp;
545 char *endp = p->bytes + p->size;
547 void *c1 = (*b->method->codec.start)();
549 decode_ptr(&src, &pos);
550 assert ((pos&CAT_MASK) == p->cat);
554 char file_item_buf[DST_ITEM_MAX];
555 char *file_item = file_item_buf;
556 (*b->method->codec.reset)(c1);
557 (*b->method->codec.decode)(c1, &file_item, &src);
560 decode_item_len(&src, &item_len);
561 assert (item_len > 0 && item_len < 80);
564 decode_ptr(&src, &pos);
565 if ((pos&CAT_MASK) != p->cat)
567 assert ((pos&CAT_MASK) == p->cat);
570 (*b->method->codec.stop)(c1);
574 void close_block(ISAMB b, struct ISAMB_block *p)
580 yaz_log(b->log_freelist, "release block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT,
581 p->pos, p->cat, p->pos/CAT_MAX);
582 memcpy (p->buf, &b->file[p->cat].head.free_list, sizeof(zint));
583 b->file[p->cat].head.free_list = p->pos;
584 if (!get_block (b, p->pos, p->buf, 1))
586 yaz_log(b->log_io, "bf_write: close_block (deleted)");
587 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
592 int offset = b->file[p->cat].head.block_offset;
593 int size = p->size + offset;
594 char *dst = p->buf + 3;
595 assert (p->size >= 0);
597 /* memset becuase encode_ptr usually does not write all bytes */
598 memset(p->buf, 0, b->file[p->cat].head.block_offset);
600 p->buf[1] = size & 255;
601 p->buf[2] = size >> 8;
602 encode_ptr(&dst, p->no_items);
604 if (!get_block (b, p->pos, p->buf, 1))
606 yaz_log(b->log_io, "bf_write: close_block");
607 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
610 (*b->method->codec.stop)(p->decodeClientData);
615 int insert_sub (ISAMB b, struct ISAMB_block **p,
616 void *new_item, int *mode,
618 struct ISAMB_block **sp,
619 void *sub_item, int *sub_size,
620 const void *max_item);
622 int insert_int (ISAMB b, struct ISAMB_block *p, void *lookahead_item,
624 ISAMC_I *stream, struct ISAMB_block **sp,
625 void *split_item, int *split_size, const void *last_max_item)
627 char *startp = p->bytes;
628 const char *src = startp;
629 char *endp = p->bytes + p->size;
631 struct ISAMB_block *sub_p1 = 0, *sub_p2 = 0;
632 char sub_item[DST_ITEM_MAX];
636 void *c1 = (*b->method->codec.start)();
640 assert(p->size >= 0);
641 decode_ptr(&src, &pos);
645 const char *src0 = src;
647 char file_item_buf[DST_ITEM_MAX];
648 char *file_item = file_item_buf;
649 (*b->method->codec.reset)(c1);
650 (*b->method->codec.decode)(c1, &file_item, &src);
651 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
654 sub_p1 = open_block(b, pos);
656 diff_terms -= sub_p1->no_items;
657 more = insert_sub (b, &sub_p1, lookahead_item, mode,
659 sub_item, &sub_size, file_item_buf);
660 diff_terms += sub_p1->no_items;
666 decode_item_len(&src, &item_len);
667 d = (*b->method->compare_item)(src, lookahead_item);
670 sub_p1 = open_block(b, pos);
672 diff_terms -= sub_p1->no_items;
673 more = insert_sub (b, &sub_p1, lookahead_item, mode,
675 sub_item, &sub_size, src);
676 diff_terms += sub_p1->no_items;
682 decode_ptr(&src, &pos);
686 /* we reached the end. So lookahead > last item */
687 sub_p1 = open_block(b, pos);
689 diff_terms -= sub_p1->no_items;
690 more = insert_sub (b, &sub_p1, lookahead_item, mode, stream, &sub_p2,
691 sub_item, &sub_size, last_max_item);
692 diff_terms += sub_p1->no_items;
695 diff_terms += sub_p2->no_items;
699 p->no_items += diff_terms;
703 /* there was a split - must insert pointer in this one */
704 char dst_buf[DST_BUF_SIZE];
707 const char *sub_item_ptr = sub_item;
709 assert (sub_size < 80 && sub_size > 1);
711 memcpy (dst, startp, src - startp);
716 (*b->method->codec.reset)(c1);
717 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
719 encode_item_len (&dst, sub_size); /* sub length and item */
720 memcpy (dst, sub_item, sub_size);
724 encode_ptr(&dst, sub_p2->pos); /* pos */
726 if (endp - src) /* remaining data */
728 memcpy (dst, src, endp - src);
731 p->size = dst - dst_buf;
732 assert (p->size >= 0);
735 if (p->size <= b->file[p->cat].head.block_max)
737 /* it fits OK in this block */
738 memcpy (startp, dst_buf, dst - dst_buf);
742 /* must split _this_ block as well .. */
743 struct ISAMB_block *sub_p3;
745 char file_item_buf[DST_ITEM_MAX];
746 char *file_item = file_item_buf;
750 zint no_items_first_half = 0;
756 half = src + b->file[p->cat].head.block_size/2;
757 decode_ptr(&src, &pos);
759 /* read sub block so we can get no_items for it */
760 sub_p3 = open_block(b, pos);
761 no_items_first_half += sub_p3->no_items;
762 close_block(b, sub_p3);
767 file_item = file_item_buf;
768 (*b->method->codec.reset)(c1);
769 (*b->method->codec.decode)(c1, &file_item, &src);
771 decode_item_len(&src, &split_size_tmp);
772 *split_size = (int) split_size_tmp;
775 decode_ptr(&src, &pos);
777 /* read sub block so we can get no_items for it */
778 sub_p3 = open_block(b, pos);
779 no_items_first_half += sub_p3->no_items;
780 close_block(b, sub_p3);
782 /* p is first half */
783 p_new_size = src - dst_buf;
784 memcpy (p->bytes, dst_buf, p_new_size);
787 file_item = file_item_buf;
788 (*b->method->codec.reset)(c1);
789 (*b->method->codec.decode)(c1, &file_item, &src);
790 *split_size = file_item - file_item_buf;
791 memcpy(split_item, file_item_buf, *split_size);
793 decode_item_len(&src, &split_size_tmp);
794 *split_size = (int) split_size_tmp;
795 memcpy (split_item, src, *split_size);
798 /* *sp is second half */
799 *sp = new_int (b, p->cat);
800 (*sp)->size = endp - src;
801 memcpy ((*sp)->bytes, src, (*sp)->size);
803 p->size = p_new_size;
805 /* adjust no_items in first&second half */
806 (*sp)->no_items = p->no_items - no_items_first_half;
807 p->no_items = no_items_first_half;
810 close_block(b, sub_p2);
812 close_block(b, sub_p1);
813 (*b->method->codec.stop)(c1);
817 int insert_leaf (ISAMB b, struct ISAMB_block **sp1, void *lookahead_item,
818 int *lookahead_mode, ISAMC_I *stream,
819 struct ISAMB_block **sp2,
820 void *sub_item, int *sub_size,
821 const void *max_item)
823 struct ISAMB_block *p = *sp1;
826 char dst_buf[DST_BUF_SIZE], *dst = dst_buf;
828 void *c1 = (*b->method->codec.start)();
829 void *c2 = (*b->method->codec.start)();
831 int quater = b->file[b->no_cat-1].head.block_max / 4;
832 char *mid_cut = dst_buf + quater * 2;
833 char *tail_cut = dst_buf + quater * 3;
834 char *maxp = dst_buf + b->file[b->no_cat-1].head.block_max;
837 char cut_item_buf[DST_ITEM_MAX];
838 int cut_item_size = 0;
839 int no_items = 0; /* number of items (total) */
840 int no_items_1 = 0; /* number of items (first half) */
844 char file_item_buf[DST_ITEM_MAX];
845 char *file_item = file_item_buf;
848 endp = p->bytes + p->size;
849 (*b->method->codec.decode)(c1, &file_item, &src);
852 const char *dst_item = 0; /* resulting item to be inserted */
853 char *lookahead_next;
857 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
859 /* d now holds comparison between existing file item and
862 d > 0: lookahead before file
863 d < 0: lookahead after file
867 /* lookahead must be inserted */
868 dst_item = lookahead_item;
869 /* if this is not an insertion, it's really bad .. */
870 if (!*lookahead_mode)
872 yaz_log(YLOG_WARN, "isamb: Inconsistent register (1)");
873 assert (*lookahead_mode);
877 dst_item = file_item_buf;
880 if (!*lookahead_mode && d == 0)
882 /* it's a deletion and they match so there is nothing to be
883 inserted anyway .. But mark the thing bad (file item
884 was part of input.. The item will not be part of output */
887 else if (!half1 && dst > mid_cut)
889 /* we have reached the splitting point for the first time */
890 const char *dst_item_0 = dst_item;
891 half1 = dst; /* candidate for splitting */
893 /* encode the resulting item */
894 (*b->method->codec.encode)(c2, &dst, &dst_item);
896 cut_item_size = dst_item - dst_item_0;
897 assert(cut_item_size > 0);
898 memcpy (cut_item_buf, dst_item_0, cut_item_size);
901 no_items_1 = no_items;
906 /* encode the resulting item */
907 (*b->method->codec.encode)(c2, &dst, &dst_item);
911 /* now move "pointers" .. result has been encoded .. */
914 /* we must move the lookahead pointer */
917 /* no more room. Mark lookahead as "gone".. */
921 /* move it really.. */
922 lookahead_next = lookahead_item;
923 if (!(*stream->read_item)(stream->clientData,
927 /* end of stream reached: no "more" and no lookahead */
931 if (lookahead_item && max_item &&
932 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
934 /* the lookahead goes beyond what we allow in this
935 leaf. Mark it as "gone" */
944 /* exact match .. move both pointers */
946 lookahead_next = lookahead_item;
947 if (!(*stream->read_item)(stream->clientData,
948 &lookahead_next, lookahead_mode))
954 break; /* end of file stream reached .. */
955 file_item = file_item_buf; /* move file pointer */
956 (*b->method->codec.decode)(c1, &file_item, &src);
960 /* file pointer must be moved */
963 file_item = file_item_buf;
964 (*b->method->codec.decode)(c1, &file_item, &src);
968 maxp = dst_buf + b->file[b->no_cat-1].head.block_max + quater;
969 /* this loop runs when we are "appending" to a leaf page. That is
970 either it's empty (new) or all file items have been read in
972 while (lookahead_item)
975 const char *src = lookahead_item;
978 /* compare lookahead with max item */
980 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
982 /* stop if we have reached the value of max item */
985 if (!*lookahead_mode)
987 /* this is append. So a delete is bad */
988 yaz_log(YLOG_WARN, "isamb: Inconsistent register (2)");
991 else if (!half1 && dst > tail_cut)
993 const char *src_0 = src;
994 half1 = dst; /* candidate for splitting */
996 (*b->method->codec.encode)(c2, &dst, &src);
998 cut_item_size = src - src_0;
999 assert(cut_item_size > 0);
1000 memcpy (cut_item_buf, src_0, cut_item_size);
1002 no_items_1 = no_items;
1006 (*b->method->codec.encode)(c2, &dst, &src);
1016 dst_item = lookahead_item;
1017 if (!(*stream->read_item)(stream->clientData, &dst_item,
1024 new_size = dst - dst_buf;
1025 if (p && p->cat != b->no_cat-1 &&
1026 new_size > b->file[p->cat].head.block_max)
1028 /* non-btree block will be removed */
1031 /* delete it too!! */
1032 p = 0; /* make a new one anyway */
1035 { /* must create a new one */
1037 for (i = 0; i < b->no_cat; i++)
1038 if (new_size <= b->file[i].head.block_max)
1042 p = new_leaf (b, i);
1044 if (new_size > b->file[p->cat].head.block_max)
1047 const char *cut_item = cut_item_buf;
1052 assert(cut_item_size > 0);
1055 p->size = half1 - dst_buf;
1056 memcpy (p->bytes, dst_buf, half1 - dst_buf);
1057 p->no_items = no_items_1;
1060 *sp2 = new_leaf (b, p->cat);
1062 (*b->method->codec.reset)(c2);
1064 first_dst = (*sp2)->bytes;
1066 (*b->method->codec.encode)(c2, &first_dst, &cut_item);
1068 memcpy (first_dst, half2, dst - half2);
1070 (*sp2)->size = (first_dst - (*sp2)->bytes) + (dst - half2);
1071 (*sp2)->no_items = no_items - no_items_1;
1074 memcpy (sub_item, cut_item_buf, cut_item_size);
1075 *sub_size = cut_item_size;
1079 memcpy (p->bytes, dst_buf, dst - dst_buf);
1081 p->no_items = no_items;
1083 (*b->method->codec.stop)(c1);
1084 (*b->method->codec.stop)(c2);
1089 int insert_sub (ISAMB b, struct ISAMB_block **p, void *new_item,
1092 struct ISAMB_block **sp,
1093 void *sub_item, int *sub_size,
1094 const void *max_item)
1096 if (!*p || (*p)->leaf)
1097 return insert_leaf (b, p, new_item, mode, stream, sp, sub_item,
1098 sub_size, max_item);
1100 return insert_int (b, *p, new_item, mode, stream, sp, sub_item,
1101 sub_size, max_item);
1104 int isamb_unlink (ISAMB b, ISAMC_P pos)
1106 struct ISAMB_block *p1;
1110 p1 = open_block(b, pos);
1115 const char *src = p1->bytes + p1->offset;
1117 void *c1 = (*b->method->codec.start)();
1119 decode_ptr(&src, &sub_p);
1120 isamb_unlink(b, sub_p);
1122 while (src != p1->bytes + p1->size)
1125 char file_item_buf[DST_ITEM_MAX];
1126 char *file_item = file_item_buf;
1127 (*b->method->codec.reset)(c1);
1128 (*b->method->codec.decode)(c1, &file_item, &src);
1131 decode_item_len(&src, &item_len);
1134 decode_ptr(&src, &sub_p);
1135 isamb_unlink(b, sub_p);
1138 (*b->method->codec.stop)(c1);
1145 ISAMB_P isamb_merge (ISAMB b, ISAMC_P pos, ISAMC_I *stream)
1147 char item_buf[DST_ITEM_MAX];
1151 int must_delete = 0;
1158 item_ptr = item_buf;
1160 (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1164 item_ptr = item_buf;
1165 more = (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1168 struct ISAMB_block *p = 0, *sp = 0;
1169 char sub_item[DST_ITEM_MAX];
1173 p = open_block(b, pos);
1174 more = insert_sub (b, &p, item_buf, &i_mode, stream, &sp,
1175 sub_item, &sub_size, 0);
1177 { /* increase level of tree by one */
1178 struct ISAMB_block *p2 = new_int (b, p->cat);
1179 char *dst = p2->bytes + p2->size;
1181 void *c1 = (*b->method->codec.start)();
1182 const char *sub_item_ptr = sub_item;
1185 encode_ptr(&dst, p->pos);
1186 assert (sub_size < 80 && sub_size > 1);
1188 (*b->method->codec.reset)(c1);
1189 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
1191 encode_item_len (&dst, sub_size);
1192 memcpy (dst, sub_item, sub_size);
1195 encode_ptr(&dst, sp->pos);
1197 p2->size = dst - p2->bytes;
1198 p2->no_items = p->no_items + sp->no_items;
1199 pos = p2->pos; /* return new super page */
1203 (*b->method->codec.stop)(c1);
1208 pos = p->pos; /* return current one (again) */
1210 if (p->no_items == 0)
1218 isamb_unlink(b, pos);
1224 ISAMB_PP isamb_pp_open_x(ISAMB isamb, ISAMB_P pos, int *level, int scope)
1226 ISAMB_PP pp = xmalloc(sizeof(*pp));
1232 pp->block = xmalloc(ISAMB_MAX_LEVEL * sizeof(*pp->block));
1239 pp->skipped_numbers = 0;
1240 pp->returned_numbers = 0;
1242 for (i = 0;i<ISAMB_MAX_LEVEL;i++)
1243 pp->skipped_nodes[i] = pp->accessed_nodes[i]=0;
1246 struct ISAMB_block *p = open_block(isamb, pos);
1247 const char *src = p->bytes + p->offset;
1248 pp->block[pp->level] = p;
1250 pp->total_size += p->size;
1254 decode_ptr(&src, &pos);
1255 p->offset = src - p->bytes;
1257 pp->accessed_nodes[pp->level]++;
1259 pp->block[pp->level+1] = 0;
1260 pp->maxlevel = pp->level;
1266 ISAMB_PP isamb_pp_open (ISAMB isamb, ISAMB_P pos, int scope)
1268 return isamb_pp_open_x(isamb, pos, 0, scope);
1271 void isamb_pp_close_x(ISAMB_PP pp, int *size, int *blocks)
1276 yaz_log(YLOG_DEBUG, "isamb_pp_close lev=%d returned "ZINT_FORMAT" values, "
1277 "skipped "ZINT_FORMAT,
1278 pp->maxlevel, pp->skipped_numbers, pp->returned_numbers);
1279 for (i = pp->maxlevel;i>=0;i--)
1280 if (pp->skipped_nodes[i] || pp->accessed_nodes[i])
1281 yaz_log(YLOG_DEBUG, "isamb_pp_close level leaf-%d: "
1282 ZINT_FORMAT" read, "ZINT_FORMAT" skipped", i,
1283 pp->accessed_nodes[i], pp->skipped_nodes[i]);
1284 pp->isamb->skipped_numbers += pp->skipped_numbers;
1285 pp->isamb->returned_numbers += pp->returned_numbers;
1286 for (i = pp->maxlevel;i>=0;i--)
1288 pp->isamb->accessed_nodes[i] += pp->accessed_nodes[i];
1289 pp->isamb->skipped_nodes[i] += pp->skipped_nodes[i];
1292 *size = pp->total_size;
1294 *blocks = pp->no_blocks;
1295 for (i = 0; i <= pp->level; i++)
1296 close_block(pp->isamb, pp->block[i]);
1301 int isamb_block_info (ISAMB isamb, int cat)
1303 if (cat >= 0 && cat < isamb->no_cat)
1304 return isamb->file[cat].head.block_size;
1308 void isamb_pp_close (ISAMB_PP pp)
1310 isamb_pp_close_x(pp, 0, 0);
1313 /* simple recursive dumper .. */
1314 static void isamb_dump_r (ISAMB b, ISAMB_P pos, void (*pr)(const char *str),
1318 char prefix_str[1024];
1321 struct ISAMB_block *p = open_block(b, pos);
1322 sprintf(prefix_str, "%*s " ZINT_FORMAT " cat=%d size=%d max=%d items="
1323 ZINT_FORMAT, level*2, "",
1324 pos, p->cat, p->size, b->file[p->cat].head.block_max,
1327 sprintf(prefix_str, "%*s " ZINT_FORMAT, level*2, "", pos);
1330 while (p->offset < p->size)
1332 const char *src = p->bytes + p->offset;
1334 (*b->method->codec.decode)(p->decodeClientData, &dst, &src);
1335 (*b->method->log_item)(YLOG_DEBUG, buf, prefix_str);
1336 p->offset = src - (char*) p->bytes;
1338 assert(p->offset == p->size);
1342 const char *src = p->bytes + p->offset;
1345 decode_ptr(&src, &sub);
1346 p->offset = src - (char*) p->bytes;
1348 isamb_dump_r(b, sub, pr, level+1);
1350 while (p->offset < p->size)
1353 char file_item_buf[DST_ITEM_MAX];
1354 char *file_item = file_item_buf;
1355 void *c1 = (*b->method->codec.start)();
1356 (*b->method->codec.decode)(c1, &file_item, &src);
1357 (*b->method->codec.stop)(c1);
1358 (*b->method->log_item)(YLOG_DEBUG, file_item_buf, prefix_str);
1361 decode_item_len(&src, &item_len);
1362 (*b->method->log_item)(YLOG_DEBUG, src, prefix_str);
1365 decode_ptr(&src, &sub);
1367 p->offset = src - (char*) p->bytes;
1369 isamb_dump_r(b, sub, pr, level+1);
1376 void isamb_dump(ISAMB b, ISAMB_P pos, void (*pr)(const char *str))
1378 isamb_dump_r(b, pos, pr, 0);
1381 int isamb_pp_read(ISAMB_PP pp, void *buf)
1383 return isamb_pp_forward(pp, buf, 0);
1387 static int isamb_pp_on_right_node(ISAMB_PP pp, int level, const void *untilbuf)
1388 { /* looks one node higher to see if we should be on this node at all */
1389 /* useful in backing off quickly, and in avoiding tail descends */
1390 /* call with pp->level to begin with */
1391 struct ISAMB_block *p;
1394 ISAMB b = pp->isamb;
1400 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true for root");
1402 return 1; /* we can never skip the root node */
1405 p = pp->block[level];
1406 assert(p->offset <= p->size);
1407 if (p->offset < p->size)
1410 char file_item_buf[DST_ITEM_MAX];
1411 char *file_item = file_item_buf;
1412 void *c1 = (*b->method->codec.start)();
1413 assert(p->offset > 0);
1414 src = p->bytes + p->offset;
1415 (*b->method->codec.decode)(c1, &file_item, &src);
1416 (*b->method->codec.stop)(c1);
1417 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1420 assert(p->offset > 0);
1421 src = p->bytes + p->offset;
1422 decode_item_len(&src, &item_len);
1424 (*b->method->codec.log_item)(YLOG_DEBUG, untilbuf, "on_leaf: until");
1425 (*b->method->codec.log_item)(YLOG_DEBUG, src, "on_leaf: value");
1427 cmp = (*b->method->compare_item)(untilbuf, src);
1429 if (cmp < pp->scope)
1432 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true "
1433 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1440 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning false "
1441 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1448 yaz_log(YLOG_DEBUG, "isamb_pp_on_right at tail, looking higher "
1451 return isamb_pp_on_right_node(pp, level, untilbuf);
1453 } /* isamb_pp_on_right_node */
1455 static int isamb_pp_read_on_leaf(ISAMB_PP pp, void *buf)
1457 /* reads the next item on the current leaf, returns 0 if end of leaf*/
1458 struct ISAMB_block *p = pp->block[pp->level];
1463 if (p->offset == p->size)
1466 yaz_log(YLOG_DEBUG, "isamb_pp_read_on_leaf returning 0 on "
1469 return 0; /* at end of leaf */
1471 src = p->bytes + p->offset;
1473 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1474 p->offset = src - (char*) p->bytes;
1476 (*pp->isamb->method->codec.log_item)(YLOG_DEBUG, buf,
1477 "read_on_leaf returning 1");
1479 pp->returned_numbers++;
1481 } /* read_on_leaf */
1483 static int isamb_pp_forward_on_leaf(ISAMB_PP pp, void *buf, const void *untilbuf)
1484 { /* forwards on the current leaf, returns 0 if not found */
1489 if (!isamb_pp_read_on_leaf(pp, buf))
1491 /* FIXME - this is an extra function call, inline the read? */
1492 cmp=(*pp->isamb->method->compare_item)(untilbuf, buf);
1494 { /* cmp<2 found a good one */
1497 yaz_log(YLOG_DEBUG, "isam_pp_fwd_on_leaf skipped %d items", skips);
1499 pp->returned_numbers++;
1503 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1504 return 0; /* never mind the rest of this leaf */
1505 pp->skipped_numbers++;
1508 } /* forward_on_leaf */
1510 static int isamb_pp_climb_level(ISAMB_PP pp, ISAMB_P *pos)
1511 { /* climbs higher in the tree, until finds a level with data left */
1512 /* returns the node to (consider to) descend to in *pos) */
1513 struct ISAMB_block *p = pp->block[pp->level];
1516 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level starting "
1517 "at level %d node %d ofs=%d sz=%d",
1518 pp->level, p->pos, p->offset, p->size);
1520 assert(pp->level >= 0);
1521 assert(p->offset <= p->size);
1525 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level returning 0 at root");
1529 assert(pp->level>0);
1530 close_block(pp->isamb, pp->block[pp->level]);
1531 pp->block[pp->level]=0;
1533 p = pp->block[pp->level];
1535 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level climbed to level %d node %d ofs=%d",
1536 pp->level, p->pos, p->offset);
1539 assert(p->offset <= p->size);
1540 if (p->offset == p->size)
1542 /* we came from the last pointer, climb on */
1543 if (!isamb_pp_climb_level(pp, pos))
1545 p = pp->block[pp->level];
1550 char file_item_buf[DST_ITEM_MAX];
1551 char *file_item = file_item_buf;
1552 ISAMB b = pp->isamb;
1553 void *c1 = (*b->method->codec.start)();
1557 /* skip the child we just came from */
1559 yaz_log(YLOG_DEBUG, "isam_pp_climb_level: skipping lev=%d ofs=%d sz=%d",
1560 pp->level, p->offset, p->size);
1562 assert (p->offset < p->size);
1563 src = p->bytes + p->offset;
1565 (*b->method->codec.decode)(c1, &file_item, &src);
1566 (*b->method->codec.stop)(c1);
1568 decode_item_len(&src, &item_len);
1571 decode_ptr(&src, pos);
1572 p->offset = src - (char *)p->bytes;
1579 static zint isamb_pp_forward_unode(ISAMB_PP pp, zint pos, const void *untilbuf)
1580 { /* scans a upper node until it finds a child <= untilbuf */
1581 /* pp points to the key value, as always. pos is the child read from */
1583 /* if all values are too small, returns the last child in the node */
1584 /* FIXME - this can be detected, and avoided by looking at the */
1585 /* parent node, but that gets messy. Presumably the cost is */
1586 /* pretty low anyway */
1587 ISAMB b = pp->isamb;
1588 struct ISAMB_block *p = pp->block[pp->level];
1589 const char *src = p->bytes + p->offset;
1594 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode starting "
1595 "at level %d node %d ofs=%di sz=%d",
1596 pp->level, p->pos, p->offset, p->size);
1599 assert(p->offset <= p->size);
1600 if (p->offset == p->size)
1603 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at end "
1604 "at level %d node %d ofs=%di sz=%d",
1605 pp->level, p->pos, p->offset, p->size);
1607 return pos; /* already at the end of it */
1609 while(p->offset < p->size)
1612 char file_item_buf[DST_ITEM_MAX];
1613 char *file_item = file_item_buf;
1614 void *c1 = (*b->method->codec.start)();
1615 (*b->method->codec.decode)(c1, &file_item, &src);
1616 (*b->method->codec.stop)(c1);
1617 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1620 decode_item_len(&src, &item_len);
1621 cmp = (*b->method->compare_item)(untilbuf, src);
1624 decode_ptr(&src, &nxtpos);
1625 if (cmp<pp->scope) /* cmp<2 */
1628 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning a hit "
1629 "at level %d node %d ofs=%d sz=%d",
1630 pp->level, p->pos, p->offset, p->size);
1635 p->offset = src-(char*)p->bytes;
1636 (pp->skipped_nodes[pp->maxlevel - pp->level -1])++;
1642 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at tail "
1643 "at level %d node %d ofs=%d sz=%d skips=%d",
1644 pp->level, p->pos, p->offset, p->size, skips);
1646 return pos; /* that's the last one in the line */
1648 } /* forward_unode */
1650 static void isamb_pp_descend_to_leaf(ISAMB_PP pp, ISAMB_P pos,
1651 const void *untilbuf)
1652 { /* climbs down the tree, from pos, to the leftmost leaf */
1653 struct ISAMB_block *p = pp->block[pp->level];
1657 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1658 "starting at lev %d node %d ofs=%d lf=%d u=%p",
1659 pp->level, p->pos, p->offset, p->leaf, untilbuf);
1662 pos = isamb_pp_forward_unode(pp, pos, untilbuf);
1665 p = open_block(pp->isamb, pos);
1666 pp->block[pp->level] = p;
1667 ++(pp->accessed_nodes[pp->maxlevel-pp->level]);
1670 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1671 "got lev %d node %d lf=%d",
1672 pp->level, p->pos, p->leaf);
1676 assert (p->offset==0);
1677 src = p->bytes + p->offset;
1678 decode_ptr(&src, &pos);
1679 p->offset = src-(char*)p->bytes;
1680 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1682 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1683 "returning at lev %d node %d ofs=%d lf=%d",
1684 pp->level, p->pos, p->offset, p->leaf);
1686 } /* descend_to_leaf */
1688 static int isamb_pp_find_next_leaf(ISAMB_PP pp)
1689 { /* finds the next leaf by climbing up and down */
1691 if (!isamb_pp_climb_level(pp, &pos))
1693 isamb_pp_descend_to_leaf(pp, pos, 0);
1697 static int isamb_pp_climb_desc(ISAMB_PP pp, const void *untilbuf)
1698 { /* climbs up and descends to a leaf where values >= *untilbuf are found */
1701 struct ISAMB_block *p = pp->block[pp->level];
1702 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc starting "
1703 "at level %d node %d ofs=%d sz=%d",
1704 pp->level, p->pos, p->offset, p->size);
1706 if (!isamb_pp_climb_level(pp, &pos))
1708 /* see if it would pay to climb one higher */
1709 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1710 if (!isamb_pp_climb_level(pp, &pos))
1712 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1714 p = pp->block[pp->level];
1715 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc done "
1716 "at level %d node %d ofs=%d sz=%d",
1717 pp->level, p->pos, p->offset, p->size);
1722 int isamb_pp_forward (ISAMB_PP pp, void *buf, const void *untilbuf)
1725 struct ISAMB_block *p = pp->block[pp->level];
1727 yaz_log(YLOG_DEBUG, "isamb_pp_forward starting "
1728 "at level %d node %d ofs=%d sz=%d u=%p sc=%d",
1729 pp->level, p->pos, p->offset, p->size, untilbuf, scope);
1733 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1736 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (A) "
1737 "at level %d node %d ofs=%d sz=%d",
1738 pp->level, p->pos, p->offset, p->size);
1742 if (! isamb_pp_climb_desc(pp, untilbuf))
1745 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning notfound (B) "
1746 "at level %d node %d ofs=%d sz=%d",
1747 pp->level, p->pos, p->offset, p->size);
1749 return 0; /* could not find a leaf */
1752 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1755 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (c) "
1756 "at level %d node %d ofs=%d sz=%d",
1757 pp->level, p->pos, p->offset, p->size);
1761 } while (isamb_pp_find_next_leaf(pp));
1762 return 0; /* could not find at all */
1764 else { /* no untilbuf, a straight read */
1765 /* FIXME - this should be moved
1766 * directly into the pp_read */
1767 /* keeping here now, to keep same
1768 * interface as the old fwd */
1769 if (isamb_pp_read_on_leaf(pp, buf))
1772 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (D) "
1773 "at level %d node %d ofs=%d sz=%d",
1774 pp->level, p->pos, p->offset, p->size);
1778 if (isamb_pp_find_next_leaf(pp))
1781 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (E) "
1782 "at level %d node %d ofs=%d sz=%d",
1783 pp->level, p->pos, p->offset, p->size);
1785 return isamb_pp_read_on_leaf(pp, buf);
1790 } /* isam_pp_forward (new version) */
1792 void isamb_pp_pos(ISAMB_PP pp, double *current, double *total)
1793 { /* return an estimate of the current position and of the total number of */
1794 /* occureences in the isam tree, based on the current leaf */
1795 struct ISAMB_block *p = pp->block[pp->level];
1800 *total = pp->block[0]->no_items;
1801 *current = (double) pp->returned_numbers;
1803 yaz_log(YLOG_LOG, "isamb_pp_pos returning: cur= %0.1f tot=%0.1f rn="
1804 ZINT_FORMAT, *current, *total, pp->returned_numbers);
1808 int isamb_pp_forward2(ISAMB_PP pp, void *buf, const void *untilb)
1812 struct ISAMB_block *p = pp->block[pp->level];
1813 ISAMB b = pp->isamb;
1817 while (p->offset == p->size)
1823 char file_item_buf[DST_ITEM_MAX];
1824 char *file_item = file_item_buf;
1828 while (p->offset == p->size)
1832 close_block (pp->isamb, pp->block[pp->level]);
1833 pp->block[pp->level] = 0;
1835 p = pp->block[pp->level];
1840 src = p->bytes + p->offset;
1843 c1 = (*b->method->codec.start)();
1844 (*b->method->codec.decode)(c1, &file_item, &src);
1846 decode_ptr (&src, &item_len);
1849 decode_ptr (&src, &pos);
1850 p->offset = src - (char*) p->bytes;
1852 src = p->bytes + p->offset;
1856 if (!untilb || p->offset == p->size)
1858 assert(p->offset < p->size);
1861 file_item = file_item_buf;
1862 (*b->method->codec.reset)(c1);
1863 (*b->method->codec.decode)(c1, &file_item, &src);
1864 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1870 decode_item_len(&src, &item_len);
1871 if ((*b->method->compare_item)(untilb, src) <= 1)
1875 decode_ptr (&src, &pos);
1876 p->offset = src - (char*) p->bytes;
1883 pp->block[pp->level] = p = open_block (pp->isamb, pos);
1885 pp->total_size += p->size;
1893 src = p->bytes + p->offset;
1896 decode_ptr (&src, &pos);
1897 p->offset = src - (char*) p->bytes;
1899 if (!untilb || p->offset == p->size)
1901 assert(p->offset < p->size);
1904 file_item = file_item_buf;
1905 (*b->method->codec.reset)(c1);
1906 (*b->method->codec.decode)(c1, &file_item, &src);
1907 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1913 decode_ptr (&src, &item_len);
1914 if ((*b->method->compare_item)(untilb, src) <= 1)
1922 (*b->method->codec.stop)(c1);
1925 assert (p->offset < p->size);
1930 src = p->bytes + p->offset;
1931 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1932 p->offset = src - (char*) p->bytes;
1933 if (!untilb || (*pp->isamb->method->compare_item)(untilb, dst0) <= 1)
1936 if (p->offset == p->size) goto again;