1 /* $Id: isamb.c,v 1.67 2005-01-15 18:43:05 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, interior 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 assert (sub_size < 80 && sub_size > 1);
709 memcpy (dst, startp, src - startp);
714 const char *sub_item_ptr = sub_item;
715 (*b->method->codec.reset)(c1);
716 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
718 encode_item_len (&dst, sub_size); /* sub length and item */
719 memcpy (dst, sub_item, sub_size);
723 encode_ptr(&dst, sub_p2->pos); /* pos */
725 if (endp - src) /* remaining data */
727 memcpy (dst, src, endp - src);
730 p->size = dst - dst_buf;
731 assert (p->size >= 0);
734 if (p->size <= b->file[p->cat].head.block_max)
736 /* it fits OK in this block */
737 memcpy (startp, dst_buf, dst - dst_buf);
741 /* must split _this_ block as well .. */
742 struct ISAMB_block *sub_p3;
747 zint no_items_first_half = 0;
753 half = src + b->file[p->cat].head.block_size/2;
754 decode_ptr(&src, &pos);
756 /* read sub block so we can get no_items for it */
757 sub_p3 = open_block(b, pos);
758 no_items_first_half += sub_p3->no_items;
759 close_block(b, sub_p3);
764 char file_item_buf[DST_ITEM_MAX];
765 char *file_item = file_item_buf;
766 (*b->method->codec.reset)(c1);
767 (*b->method->codec.decode)(c1, &file_item, &src);
769 decode_item_len(&src, &split_size_tmp);
770 *split_size = (int) split_size_tmp;
773 decode_ptr(&src, &pos);
775 /* read sub block so we can get no_items for it */
776 sub_p3 = open_block(b, pos);
777 no_items_first_half += sub_p3->no_items;
778 close_block(b, sub_p3);
780 /* p is first half */
781 p_new_size = src - dst_buf;
782 memcpy (p->bytes, dst_buf, p_new_size);
785 char file_item_buf[DST_ITEM_MAX];
786 char *file_item = file_item_buf;
787 (*b->method->codec.reset)(c1);
788 (*b->method->codec.decode)(c1, &file_item, &src);
789 *split_size = file_item - file_item_buf;
790 memcpy(split_item, file_item_buf, *split_size);
792 decode_item_len(&src, &split_size_tmp);
793 *split_size = (int) split_size_tmp;
794 memcpy (split_item, src, *split_size);
797 /* *sp is second half */
798 *sp = new_int (b, p->cat);
799 (*sp)->size = endp - src;
800 memcpy ((*sp)->bytes, src, (*sp)->size);
802 p->size = p_new_size;
804 /* adjust no_items in first&second half */
805 (*sp)->no_items = p->no_items - no_items_first_half;
806 p->no_items = no_items_first_half;
809 close_block(b, sub_p2);
811 close_block(b, sub_p1);
812 (*b->method->codec.stop)(c1);
816 int insert_leaf (ISAMB b, struct ISAMB_block **sp1, void *lookahead_item,
817 int *lookahead_mode, ISAMC_I *stream,
818 struct ISAMB_block **sp2,
819 void *sub_item, int *sub_size,
820 const void *max_item)
822 struct ISAMB_block *p = *sp1;
825 char dst_buf[DST_BUF_SIZE], *dst = dst_buf;
827 void *c1 = (*b->method->codec.start)();
828 void *c2 = (*b->method->codec.start)();
830 int quater = b->file[b->no_cat-1].head.block_max / 4;
831 char *mid_cut = dst_buf + quater * 2;
832 char *tail_cut = dst_buf + quater * 3;
833 char *maxp = dst_buf + b->file[b->no_cat-1].head.block_max;
836 char cut_item_buf[DST_ITEM_MAX];
837 int cut_item_size = 0;
838 int no_items = 0; /* number of items (total) */
839 int no_items_1 = 0; /* number of items (first half) */
843 char file_item_buf[DST_ITEM_MAX];
844 char *file_item = file_item_buf;
847 endp = p->bytes + p->size;
848 (*b->method->codec.decode)(c1, &file_item, &src);
851 const char *dst_item = 0; /* resulting item to be inserted */
852 char *lookahead_next;
856 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
858 /* d now holds comparison between existing file item and
861 d > 0: lookahead before file
862 d < 0: lookahead after file
866 /* lookahead must be inserted */
867 dst_item = lookahead_item;
868 /* if this is not an insertion, it's really bad .. */
869 if (!*lookahead_mode)
871 yaz_log(YLOG_WARN, "isamb: Inconsistent register (1)");
872 assert (*lookahead_mode);
876 dst_item = file_item_buf;
879 if (!*lookahead_mode && d == 0)
881 /* it's a deletion and they match so there is nothing to be
882 inserted anyway .. But mark the thing bad (file item
883 was part of input.. The item will not be part of output */
886 else if (!half1 && dst > mid_cut)
888 /* we have reached the splitting point for the first time */
889 const char *dst_item_0 = dst_item;
890 half1 = dst; /* candidate for splitting */
892 /* encode the resulting item */
893 (*b->method->codec.encode)(c2, &dst, &dst_item);
895 cut_item_size = dst_item - dst_item_0;
896 assert(cut_item_size > 0);
897 memcpy (cut_item_buf, dst_item_0, cut_item_size);
900 no_items_1 = no_items;
905 /* encode the resulting item */
906 (*b->method->codec.encode)(c2, &dst, &dst_item);
910 /* now move "pointers" .. result has been encoded .. */
913 /* we must move the lookahead pointer */
916 /* no more room. Mark lookahead as "gone".. */
920 /* move it really.. */
921 lookahead_next = lookahead_item;
922 if (!(*stream->read_item)(stream->clientData,
926 /* end of stream reached: no "more" and no lookahead */
930 if (lookahead_item && max_item &&
931 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
933 /* the lookahead goes beyond what we allow in this
934 leaf. Mark it as "gone" */
943 /* exact match .. move both pointers */
945 lookahead_next = lookahead_item;
946 if (!(*stream->read_item)(stream->clientData,
947 &lookahead_next, lookahead_mode))
953 break; /* end of file stream reached .. */
954 file_item = file_item_buf; /* move file pointer */
955 (*b->method->codec.decode)(c1, &file_item, &src);
959 /* file pointer must be moved */
962 file_item = file_item_buf;
963 (*b->method->codec.decode)(c1, &file_item, &src);
967 maxp = dst_buf + b->file[b->no_cat-1].head.block_max + quater;
968 /* this loop runs when we are "appending" to a leaf page. That is
969 either it's empty (new) or all file items have been read in
971 while (lookahead_item)
974 const char *src = lookahead_item;
977 /* compare lookahead with max item */
979 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
981 /* stop if we have reached the value of max item */
984 if (!*lookahead_mode)
986 /* this is append. So a delete is bad */
987 yaz_log(YLOG_WARN, "isamb: Inconsistent register (2)");
990 else if (!half1 && dst > tail_cut)
992 const char *src_0 = src;
993 half1 = dst; /* candidate for splitting */
995 (*b->method->codec.encode)(c2, &dst, &src);
997 cut_item_size = src - src_0;
998 assert(cut_item_size > 0);
999 memcpy (cut_item_buf, src_0, cut_item_size);
1001 no_items_1 = no_items;
1005 (*b->method->codec.encode)(c2, &dst, &src);
1015 dst_item = lookahead_item;
1016 if (!(*stream->read_item)(stream->clientData, &dst_item,
1023 new_size = dst - dst_buf;
1024 if (p && p->cat != b->no_cat-1 &&
1025 new_size > b->file[p->cat].head.block_max)
1027 /* non-btree block will be removed */
1030 /* delete it too!! */
1031 p = 0; /* make a new one anyway */
1034 { /* must create a new one */
1036 for (i = 0; i < b->no_cat; i++)
1037 if (new_size <= b->file[i].head.block_max)
1041 p = new_leaf (b, i);
1043 if (new_size > b->file[p->cat].head.block_max)
1046 const char *cut_item = cut_item_buf;
1051 assert(cut_item_size > 0);
1054 p->size = half1 - dst_buf;
1055 memcpy (p->bytes, dst_buf, half1 - dst_buf);
1056 p->no_items = no_items_1;
1059 *sp2 = new_leaf (b, p->cat);
1061 (*b->method->codec.reset)(c2);
1063 first_dst = (*sp2)->bytes;
1065 (*b->method->codec.encode)(c2, &first_dst, &cut_item);
1067 memcpy (first_dst, half2, dst - half2);
1069 (*sp2)->size = (first_dst - (*sp2)->bytes) + (dst - half2);
1070 (*sp2)->no_items = no_items - no_items_1;
1073 memcpy (sub_item, cut_item_buf, cut_item_size);
1074 *sub_size = cut_item_size;
1078 memcpy (p->bytes, dst_buf, dst - dst_buf);
1080 p->no_items = no_items;
1082 (*b->method->codec.stop)(c1);
1083 (*b->method->codec.stop)(c2);
1088 int insert_sub (ISAMB b, struct ISAMB_block **p, void *new_item,
1091 struct ISAMB_block **sp,
1092 void *sub_item, int *sub_size,
1093 const void *max_item)
1095 if (!*p || (*p)->leaf)
1096 return insert_leaf (b, p, new_item, mode, stream, sp, sub_item,
1097 sub_size, max_item);
1099 return insert_int (b, *p, new_item, mode, stream, sp, sub_item,
1100 sub_size, max_item);
1103 int isamb_unlink (ISAMB b, ISAMC_P pos)
1105 struct ISAMB_block *p1;
1109 p1 = open_block(b, pos);
1114 const char *src = p1->bytes + p1->offset;
1116 decode_ptr(&src, &sub_p);
1117 isamb_unlink(b, sub_p);
1118 void *c1 = (*b->method->codec.start)();
1120 while (src != p1->bytes + p1->size)
1123 char file_item_buf[DST_ITEM_MAX];
1124 char *file_item = file_item_buf;
1125 (*b->method->codec.reset)(c1);
1126 (*b->method->codec.decode)(c1, &file_item, &src);
1129 decode_item_len(&src, &item_len);
1132 decode_ptr(&src, &sub_p);
1133 isamb_unlink(b, sub_p);
1135 (*b->method->codec.stop)(c1);
1141 ISAMB_P isamb_merge (ISAMB b, ISAMC_P pos, ISAMC_I *stream)
1143 char item_buf[DST_ITEM_MAX];
1147 int must_delete = 0;
1154 item_ptr = item_buf;
1156 (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1160 item_ptr = item_buf;
1161 more = (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1164 struct ISAMB_block *p = 0, *sp = 0;
1165 char sub_item[DST_ITEM_MAX];
1169 p = open_block(b, pos);
1170 more = insert_sub (b, &p, item_buf, &i_mode, stream, &sp,
1171 sub_item, &sub_size, 0);
1173 { /* increase level of tree by one */
1174 struct ISAMB_block *p2 = new_int (b, p->cat);
1175 char *dst = p2->bytes + p2->size;
1176 void *c1 = (*b->method->codec.start)();
1178 encode_ptr(&dst, p->pos);
1179 assert (sub_size < 80 && sub_size > 1);
1181 const char *sub_item_ptr = sub_item;
1182 (*b->method->codec.reset)(c1);
1183 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
1185 encode_item_len (&dst, sub_size);
1186 memcpy (dst, sub_item, sub_size);
1189 encode_ptr(&dst, sp->pos);
1191 p2->size = dst - p2->bytes;
1192 p2->no_items = p->no_items + sp->no_items;
1193 pos = p2->pos; /* return new super page */
1196 (*b->method->codec.stop)(c1);
1200 pos = p->pos; /* return current one (again) */
1202 if (p->no_items == 0)
1210 isamb_unlink(b, pos);
1216 ISAMB_PP isamb_pp_open_x(ISAMB isamb, ISAMB_P pos, int *level, int scope)
1218 ISAMB_PP pp = xmalloc(sizeof(*pp));
1224 pp->block = xmalloc(ISAMB_MAX_LEVEL * sizeof(*pp->block));
1231 pp->skipped_numbers = 0;
1232 pp->returned_numbers = 0;
1234 for (i = 0;i<ISAMB_MAX_LEVEL;i++)
1235 pp->skipped_nodes[i] = pp->accessed_nodes[i]=0;
1238 struct ISAMB_block *p = open_block(isamb, pos);
1239 const char *src = p->bytes + p->offset;
1240 pp->block[pp->level] = p;
1242 pp->total_size += p->size;
1246 decode_ptr(&src, &pos);
1247 p->offset = src - p->bytes;
1249 pp->accessed_nodes[pp->level]++;
1251 pp->block[pp->level+1] = 0;
1252 pp->maxlevel = pp->level;
1258 ISAMB_PP isamb_pp_open (ISAMB isamb, ISAMB_P pos, int scope)
1260 return isamb_pp_open_x(isamb, pos, 0, scope);
1263 void isamb_pp_close_x(ISAMB_PP pp, int *size, int *blocks)
1268 yaz_log(YLOG_DEBUG, "isamb_pp_close lev=%d returned "ZINT_FORMAT" values, "
1269 "skipped "ZINT_FORMAT,
1270 pp->maxlevel, pp->skipped_numbers, pp->returned_numbers);
1271 for (i = pp->maxlevel;i>=0;i--)
1272 if (pp->skipped_nodes[i] || pp->accessed_nodes[i])
1273 yaz_log(YLOG_DEBUG, "isamb_pp_close level leaf-%d: "
1274 ZINT_FORMAT" read, "ZINT_FORMAT" skipped", i,
1275 pp->accessed_nodes[i], pp->skipped_nodes[i]);
1276 pp->isamb->skipped_numbers += pp->skipped_numbers;
1277 pp->isamb->returned_numbers += pp->returned_numbers;
1278 for (i = pp->maxlevel;i>=0;i--)
1280 pp->isamb->accessed_nodes[i] += pp->accessed_nodes[i];
1281 pp->isamb->skipped_nodes[i] += pp->skipped_nodes[i];
1284 *size = pp->total_size;
1286 *blocks = pp->no_blocks;
1287 for (i = 0; i <= pp->level; i++)
1288 close_block(pp->isamb, pp->block[i]);
1293 int isamb_block_info (ISAMB isamb, int cat)
1295 if (cat >= 0 && cat < isamb->no_cat)
1296 return isamb->file[cat].head.block_size;
1300 void isamb_pp_close (ISAMB_PP pp)
1302 isamb_pp_close_x(pp, 0, 0);
1305 /* simple recursive dumper .. */
1306 static void isamb_dump_r (ISAMB b, ISAMB_P pos, void (*pr)(const char *str),
1310 char prefix_str[1024];
1313 struct ISAMB_block *p = open_block(b, pos);
1314 sprintf(prefix_str, "%*s " ZINT_FORMAT " cat=%d size=%d max=%d items="
1315 ZINT_FORMAT, level*2, "",
1316 pos, p->cat, p->size, b->file[p->cat].head.block_max,
1319 sprintf(prefix_str, "%*s " ZINT_FORMAT, level*2, "", pos);
1322 while (p->offset < p->size)
1324 const char *src = p->bytes + p->offset;
1326 (*b->method->codec.decode)(p->decodeClientData, &dst, &src);
1327 (*b->method->log_item)(YLOG_DEBUG, buf, prefix_str);
1328 p->offset = src - (char*) p->bytes;
1330 assert(p->offset == p->size);
1334 const char *src = p->bytes + p->offset;
1337 decode_ptr(&src, &sub);
1338 p->offset = src - (char*) p->bytes;
1340 isamb_dump_r(b, sub, pr, level+1);
1342 while (p->offset < p->size)
1345 char file_item_buf[DST_ITEM_MAX];
1346 char *file_item = file_item_buf;
1347 void *c1 = (*b->method->codec.start)();
1348 (*b->method->codec.decode)(c1, &file_item, &src);
1349 (*b->method->codec.stop)(c1);
1350 (*b->method->log_item)(YLOG_DEBUG, file_item_buf, prefix_str);
1353 decode_item_len(&src, &item_len);
1354 (*b->method->log_item)(YLOG_DEBUG, src, prefix_str);
1357 decode_ptr(&src, &sub);
1359 p->offset = src - (char*) p->bytes;
1361 isamb_dump_r(b, sub, pr, level+1);
1368 void isamb_dump(ISAMB b, ISAMB_P pos, void (*pr)(const char *str))
1370 isamb_dump_r(b, pos, pr, 0);
1373 int isamb_pp_read(ISAMB_PP pp, void *buf)
1375 return isamb_pp_forward(pp, buf, 0);
1379 static int isamb_pp_on_right_node(ISAMB_PP pp, int level, const void *untilbuf)
1380 { /* looks one node higher to see if we should be on this node at all */
1381 /* useful in backing off quickly, and in avoiding tail descends */
1382 /* call with pp->level to begin with */
1383 struct ISAMB_block *p;
1386 ISAMB b = pp->isamb;
1392 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true for root");
1394 return 1; /* we can never skip the root node */
1397 p = pp->block[level];
1398 assert(p->offset <= p->size);
1399 if (p->offset < p->size)
1402 char file_item_buf[DST_ITEM_MAX];
1403 char *file_item = file_item_buf;
1404 void *c1 = (*b->method->codec.start)();
1405 assert(p->offset > 0);
1406 src = p->bytes + p->offset;
1407 (*b->method->codec.decode)(c1, &file_item, &src);
1408 (*b->method->codec.stop)(c1);
1409 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1412 assert(p->offset > 0);
1413 src = p->bytes + p->offset;
1414 decode_item_len(&src, &item_len);
1416 (*b->method->codec.log_item)(YLOG_DEBUG, untilbuf, "on_leaf: until");
1417 (*b->method->codec.log_item)(YLOG_DEBUG, src, "on_leaf: value");
1419 cmp = (*b->method->compare_item)(untilbuf, src);
1421 if (cmp < pp->scope)
1424 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true "
1425 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1432 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning false "
1433 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1440 yaz_log(YLOG_DEBUG, "isamb_pp_on_right at tail, looking higher "
1443 return isamb_pp_on_right_node(pp, level, untilbuf);
1445 } /* isamb_pp_on_right_node */
1447 static int isamb_pp_read_on_leaf(ISAMB_PP pp, void *buf)
1449 /* reads the next item on the current leaf, returns 0 if end of leaf*/
1450 struct ISAMB_block *p = pp->block[pp->level];
1455 if (p->offset == p->size)
1458 yaz_log(YLOG_DEBUG, "isamb_pp_read_on_leaf returning 0 on "
1461 return 0; /* at end of leaf */
1463 src = p->bytes + p->offset;
1465 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1466 p->offset = src - (char*) p->bytes;
1468 (*pp->isamb->method->codec.log_item)(YLOG_DEBUG, buf,
1469 "read_on_leaf returning 1");
1471 pp->returned_numbers++;
1473 } /* read_on_leaf */
1475 static int isamb_pp_forward_on_leaf(ISAMB_PP pp, void *buf, const void *untilbuf)
1476 { /* forwards on the current leaf, returns 0 if not found */
1481 if (!isamb_pp_read_on_leaf(pp, buf))
1483 /* FIXME - this is an extra function call, inline the read? */
1484 cmp=(*pp->isamb->method->compare_item)(untilbuf, buf);
1486 { /* cmp<2 found a good one */
1489 yaz_log(YLOG_DEBUG, "isam_pp_fwd_on_leaf skipped %d items", skips);
1491 pp->returned_numbers++;
1495 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1496 return 0; /* never mind the rest of this leaf */
1497 pp->skipped_numbers++;
1500 } /* forward_on_leaf */
1502 static int isamb_pp_climb_level(ISAMB_PP pp, ISAMB_P *pos)
1503 { /* climbs higher in the tree, until finds a level with data left */
1504 /* returns the node to (consider to) descend to in *pos) */
1505 struct ISAMB_block *p = pp->block[pp->level];
1507 ISAMB b = pp->isamb;
1509 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level starting "
1510 "at level %d node %d ofs=%d sz=%d",
1511 pp->level, p->pos, p->offset, p->size);
1513 assert(pp->level >= 0);
1514 assert(p->offset <= p->size);
1518 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level returning 0 at root");
1522 assert(pp->level>0);
1523 close_block(pp->isamb, pp->block[pp->level]);
1524 pp->block[pp->level]=0;
1526 p = pp->block[pp->level];
1528 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level climbed to level %d node %d ofs=%d",
1529 pp->level, p->pos, p->offset);
1532 assert(p->offset <= p->size);
1533 if (p->offset == p->size)
1535 /* we came from the last pointer, climb on */
1536 if (!isamb_pp_climb_level(pp, pos))
1538 p = pp->block[pp->level];
1542 /* skip the child we just came from */
1544 yaz_log(YLOG_DEBUG, "isam_pp_climb_level: skipping lev=%d ofs=%d sz=%d",
1545 pp->level, p->offset, p->size);
1547 assert (p->offset < p->size);
1548 src = p->bytes + p->offset;
1550 char file_item_buf[DST_ITEM_MAX];
1551 char *file_item = file_item_buf;
1552 void *c1 = (*b->method->codec.start)();
1553 (*b->method->codec.decode)(c1, &file_item, &src);
1554 (*b->method->codec.stop)(c1);
1557 decode_item_len(&src, &item_len);
1560 decode_ptr(&src, pos);
1561 p->offset = src - (char *)p->bytes;
1568 static zint isamb_pp_forward_unode(ISAMB_PP pp, zint pos, const void *untilbuf)
1569 { /* scans a upper node until it finds a child <= untilbuf */
1570 /* pp points to the key value, as always. pos is the child read from */
1572 /* if all values are too small, returns the last child in the node */
1573 /* FIXME - this can be detected, and avoided by looking at the */
1574 /* parent node, but that gets messy. Presumably the cost is */
1575 /* pretty low anyway */
1576 ISAMB b = pp->isamb;
1577 struct ISAMB_block *p = pp->block[pp->level];
1578 const char *src = p->bytes + p->offset;
1583 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode starting "
1584 "at level %d node %d ofs=%di sz=%d",
1585 pp->level, p->pos, p->offset, p->size);
1588 assert(p->offset <= p->size);
1589 if (p->offset == p->size)
1592 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at end "
1593 "at level %d node %d ofs=%di sz=%d",
1594 pp->level, p->pos, p->offset, p->size);
1596 return pos; /* already at the end of it */
1598 while(p->offset < p->size)
1601 char file_item_buf[DST_ITEM_MAX];
1602 char *file_item = file_item_buf;
1603 void *c1 = (*b->method->codec.start)();
1604 (*b->method->codec.decode)(c1, &file_item, &src);
1605 (*b->method->codec.stop)(c1);
1606 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1609 decode_item_len(&src, &item_len);
1610 cmp = (*b->method->compare_item)(untilbuf, src);
1613 decode_ptr(&src, &nxtpos);
1614 if (cmp<pp->scope) /* cmp<2 */
1617 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning a hit "
1618 "at level %d node %d ofs=%d sz=%d",
1619 pp->level, p->pos, p->offset, p->size);
1624 p->offset = src-(char*)p->bytes;
1625 (pp->skipped_nodes[pp->maxlevel - pp->level -1])++;
1631 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at tail "
1632 "at level %d node %d ofs=%d sz=%d skips=%d",
1633 pp->level, p->pos, p->offset, p->size, skips);
1635 return pos; /* that's the last one in the line */
1637 } /* forward_unode */
1639 static void isamb_pp_descend_to_leaf(ISAMB_PP pp, ISAMB_P pos,
1640 const void *untilbuf)
1641 { /* climbs down the tree, from pos, to the leftmost leaf */
1642 struct ISAMB_block *p = pp->block[pp->level];
1646 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1647 "starting at lev %d node %d ofs=%d lf=%d u=%p",
1648 pp->level, p->pos, p->offset, p->leaf, untilbuf);
1651 pos = isamb_pp_forward_unode(pp, pos, untilbuf);
1654 p = open_block(pp->isamb, pos);
1655 pp->block[pp->level] = p;
1656 ++(pp->accessed_nodes[pp->maxlevel-pp->level]);
1659 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1660 "got lev %d node %d lf=%d",
1661 pp->level, p->pos, p->leaf);
1665 assert (p->offset==0);
1666 src = p->bytes + p->offset;
1667 decode_ptr(&src, &pos);
1668 p->offset = src-(char*)p->bytes;
1669 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1671 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1672 "returning at lev %d node %d ofs=%d lf=%d",
1673 pp->level, p->pos, p->offset, p->leaf);
1675 } /* descend_to_leaf */
1677 static int isamb_pp_find_next_leaf(ISAMB_PP pp)
1678 { /* finds the next leaf by climbing up and down */
1680 if (!isamb_pp_climb_level(pp, &pos))
1682 isamb_pp_descend_to_leaf(pp, pos, 0);
1686 static int isamb_pp_climb_desc(ISAMB_PP pp, const void *untilbuf)
1687 { /* climbs up and descends to a leaf where values >= *untilbuf are found */
1690 struct ISAMB_block *p = pp->block[pp->level];
1691 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc starting "
1692 "at level %d node %d ofs=%d sz=%d",
1693 pp->level, p->pos, p->offset, p->size);
1695 if (!isamb_pp_climb_level(pp, &pos))
1697 /* see if it would pay to climb one higher */
1698 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1699 if (!isamb_pp_climb_level(pp, &pos))
1701 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1703 p = pp->block[pp->level];
1704 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc done "
1705 "at level %d node %d ofs=%d sz=%d",
1706 pp->level, p->pos, p->offset, p->size);
1711 int isamb_pp_forward (ISAMB_PP pp, void *buf, const void *untilbuf)
1714 struct ISAMB_block *p = pp->block[pp->level];
1716 yaz_log(YLOG_DEBUG, "isamb_pp_forward starting "
1717 "at level %d node %d ofs=%d sz=%d u=%p sc=%d",
1718 pp->level, p->pos, p->offset, p->size, untilbuf, scope);
1722 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1725 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (A) "
1726 "at level %d node %d ofs=%d sz=%d",
1727 pp->level, p->pos, p->offset, p->size);
1731 if (! isamb_pp_climb_desc(pp, untilbuf))
1734 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning notfound (B) "
1735 "at level %d node %d ofs=%d sz=%d",
1736 pp->level, p->pos, p->offset, p->size);
1738 return 0; /* could not find a leaf */
1741 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1744 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (C) "
1745 "at level %d node %d ofs=%d sz=%d",
1746 pp->level, p->pos, p->offset, p->size);
1750 } while (isamb_pp_find_next_leaf(pp));
1751 return 0; /* could not find at all */
1753 else { /* no untilbuf, a straight read */
1754 /* FIXME - this should be moved
1755 * directly into the pp_read */
1756 /* keeping here now, to keep same
1757 * interface as the old fwd */
1758 if (isamb_pp_read_on_leaf(pp, buf))
1761 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (D) "
1762 "at level %d node %d ofs=%d sz=%d",
1763 pp->level, p->pos, p->offset, p->size);
1767 if (isamb_pp_find_next_leaf(pp))
1770 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (E) "
1771 "at level %d node %d ofs=%d sz=%d",
1772 pp->level, p->pos, p->offset, p->size);
1774 return isamb_pp_read_on_leaf(pp, buf);
1779 } /* isam_pp_forward (new version) */
1781 void isamb_pp_pos(ISAMB_PP pp, double *current, double *total)
1782 { /* return an estimate of the current position and of the total number of */
1783 /* occureences in the isam tree, based on the current leaf */
1784 struct ISAMB_block *p = pp->block[pp->level];
1789 *total = pp->block[0]->no_items;
1790 *current = (double) pp->returned_numbers;
1792 yaz_log(YLOG_LOG, "isamb_pp_pos returning: cur= %0.1f tot=%0.1f rn="
1793 ZINT_FORMAT, *current, *total, pp->returned_numbers);
1797 int isamb_pp_forward2(ISAMB_PP pp, void *buf, const void *untilb)
1801 struct ISAMB_block *p = pp->block[pp->level];
1802 ISAMB b = pp->isamb;
1806 while (p->offset == p->size)
1812 char file_item_buf[DST_ITEM_MAX];
1813 char *file_item = file_item_buf;
1817 while (p->offset == p->size)
1821 close_block (pp->isamb, pp->block[pp->level]);
1822 pp->block[pp->level] = 0;
1824 p = pp->block[pp->level];
1829 src = p->bytes + p->offset;
1832 c1 = (*b->method->codec.start)();
1833 (*b->method->codec.decode)(c1, &file_item, &src);
1835 decode_ptr (&src, &item_len);
1838 decode_ptr (&src, &pos);
1839 p->offset = src - (char*) p->bytes;
1841 src = p->bytes + p->offset;
1845 if (!untilb || p->offset == p->size)
1847 assert(p->offset < p->size);
1850 file_item = file_item_buf;
1851 (*b->method->codec.reset)(c1);
1852 (*b->method->codec.decode)(c1, &file_item, &src);
1853 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1859 decode_item_len(&src, &item_len);
1860 if ((*b->method->compare_item)(untilb, src) <= 1)
1864 decode_ptr (&src, &pos);
1865 p->offset = src - (char*) p->bytes;
1872 pp->block[pp->level] = p = open_block (pp->isamb, pos);
1874 pp->total_size += p->size;
1882 src = p->bytes + p->offset;
1885 decode_ptr (&src, &pos);
1886 p->offset = src - (char*) p->bytes;
1888 if (!untilb || p->offset == p->size)
1890 assert(p->offset < p->size);
1893 file_item = file_item_buf;
1894 (*b->method->codec.reset)(c1);
1895 (*b->method->codec.decode)(c1, &file_item, &src);
1896 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1902 decode_ptr (&src, &item_len);
1903 if ((*b->method->compare_item)(untilb, src) <= 1)
1911 (*b->method->codec.stop)(c1);
1914 assert (p->offset < p->size);
1919 src = p->bytes + p->offset;
1920 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1921 p->offset = src - (char*) p->bytes;
1922 if (!untilb || (*pp->isamb->method->compare_item)(untilb, dst0) <= 1)
1925 if (p->offset == p->size) goto again;