1 /* $Id: isamb.c,v 1.74 2005-03-18 12:05:11 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
26 #include <yaz/xmalloc.h>
27 #include <idzebra/isamb.h>
35 #define ISAMB_MAJOR_VERSION 3
36 #define ISAMB_MINOR_VERSION 0
48 /* if 1, upper nodes items are encoded; 0 if not encoded */
51 /* maximum size of encoded buffer */
52 #define DST_ITEM_MAX 256
54 #define ISAMB_MAX_LEVEL 10
55 /* approx 2*max page + max size of item */
56 #define DST_BUF_SIZE 16840
58 #define ISAMB_CACHE_ENTRY_SIZE 4096
60 /* CAT_MAX: _must_ be power of 2 */
62 #define CAT_MASK (CAT_MAX-1)
63 /* CAT_NO: <= CAT_MAX */
66 /* Smallest block size */
67 #define ISAMB_MIN_SIZE 32
69 #define ISAMB_FAC_SIZE 4
71 /* ISAMB_PTR_CODEC = 1 var, =0 fixed */
72 #define ISAMB_PTR_CODEC 1
74 struct ISAMB_cache_entry {
79 struct ISAMB_cache_entry *next;
85 struct ISAMB_head head;
86 struct ISAMB_cache_entry *cache_entries;
93 struct ISAMB_file *file;
95 int cache; /* 0 = no cache, 1 = use cache, -1 = dummy isam (for testing only) */
96 int log_io; /* log level for bf_read/bf_write calls */
97 int log_freelist; /* log level for freelist handling */
98 zint skipped_numbers; /* on a leaf node */
99 zint returned_numbers;
100 zint skipped_nodes[ISAMB_MAX_LEVEL]; /* [0]=skipped leaves, 1 = higher etc */
101 zint accessed_nodes[ISAMB_MAX_LEVEL]; /* nodes we did not skip */
112 zint no_items; /* number of nodes in this + children */
116 void *decodeClientData;
124 int maxlevel; /* total depth */
127 zint skipped_numbers; /* on a leaf node */
128 zint returned_numbers;
129 zint skipped_nodes[ISAMB_MAX_LEVEL]; /* [0]=skipped leaves, 1 = higher etc */
130 zint accessed_nodes[ISAMB_MAX_LEVEL]; /* nodes we did not skip */
131 struct ISAMB_block **block;
132 int scope; /* on what level we forward */
136 #define encode_item_len encode_ptr
138 static void encode_ptr(char **dst, zint pos)
140 unsigned char *bp = (unsigned char*) *dst;
144 *bp++ = (unsigned char) (128 | (pos & 127));
147 *bp++ = (unsigned char) pos;
151 static void encode_ptr(char **dst, zint pos)
153 memcpy(*dst, &pos, sizeof(pos));
154 (*dst) += sizeof(pos);
158 #define decode_item_len decode_ptr
160 static void decode_ptr(const char **src1, zint *pos)
162 const unsigned char **src = (const unsigned char **) src1;
167 while (((c = *(*src)++) & 128))
169 d += ((zint) (c & 127) << r);
172 d += ((zint) c << r);
176 static void decode_ptr(const char **src, zint *pos)
178 memcpy(pos, *src, sizeof(*pos));
179 (*src) += sizeof(*pos);
183 ISAMB isamb_open(BFiles bfs, const char *name, int writeflag, ISAMC_M *method,
186 ISAMB isamb = xmalloc(sizeof(*isamb));
187 int i, b_size = ISAMB_MIN_SIZE;
190 isamb->method = (ISAMC_M *) xmalloc(sizeof(*method));
191 memcpy(isamb->method, method, sizeof(*method));
192 isamb->no_cat = CAT_NO;
194 isamb->log_freelist = 0;
195 isamb->cache = cache;
196 isamb->skipped_numbers = 0;
197 isamb->returned_numbers = 0;
198 for (i = 0; i<ISAMB_MAX_LEVEL; i++)
199 isamb->skipped_nodes[i] = isamb->accessed_nodes[i] = 0;
202 isamb->file = xmalloc(sizeof(*isamb->file) * isamb->no_cat);
203 for (i = 0; i < isamb->no_cat; i++)
205 char fname[DST_BUF_SIZE];
206 char hbuf[DST_BUF_SIZE];
207 isamb->file[i].cache_entries = 0;
208 isamb->file[i].head_dirty = 0;
209 sprintf(fname, "%s%c", name, i+'A');
211 isamb->file[i].bf = bf_open(bfs, fname, ISAMB_CACHE_ENTRY_SIZE,
214 isamb->file[i].bf = bf_open(bfs, fname, b_size, writeflag);
216 /* fill-in default values (for empty isamb) */
217 isamb->file[i].head.first_block = ISAMB_CACHE_ENTRY_SIZE/b_size+1;
218 isamb->file[i].head.last_block = isamb->file[i].head.first_block;
219 isamb->file[i].head.block_size = b_size;
221 if (i == isamb->no_cat-1 || b_size > 128)
222 isamb->file[i].head.block_offset = 8;
224 isamb->file[i].head.block_offset = 4;
226 isamb->file[i].head.block_offset = 11;
228 isamb->file[i].head.block_max =
229 b_size - isamb->file[i].head.block_offset;
230 isamb->file[i].head.free_list = 0;
231 if (bf_read(isamb->file[i].bf, 0, 0, 0, hbuf))
233 /* got header assume "isamb"major minor len can fit in 16 bytes */
235 int major, minor, len, pos = 0;
238 if (memcmp(hbuf, "isamb", 5))
240 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
243 if (sscanf(hbuf+5, "%d %d %d", &major, &minor, &len) != 3)
245 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
248 if (major != ISAMB_MAJOR_VERSION)
250 yaz_log(YLOG_WARN, "bad major version for file %s %d, must be %d",
251 fname, major, ISAMB_MAJOR_VERSION);
254 for (left = len - b_size; left > 0; left = left - b_size)
257 if (!bf_read(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size))
259 yaz_log(YLOG_WARN, "truncated isamb header for "
260 "file=%s len=%d pos=%d",
266 decode_ptr(&src, &isamb->file[i].head.first_block);
267 decode_ptr(&src, &isamb->file[i].head.last_block);
268 decode_ptr(&src, &zint_tmp);
269 isamb->file[i].head.block_size = (int) zint_tmp;
270 decode_ptr(&src, &zint_tmp);
271 isamb->file[i].head.block_max = (int) zint_tmp;
272 decode_ptr(&src, &isamb->file[i].head.free_list);
274 assert (isamb->file[i].head.block_size >= isamb->file[i].head.block_offset);
275 isamb->file[i].head_dirty = 0;
276 assert(isamb->file[i].head.block_size == b_size);
277 b_size = b_size * ISAMB_FAC_SIZE;
280 yaz_log(YLOG_WARN, "isamb debug enabled. Things will be slower than usual");
285 static void flush_blocks (ISAMB b, int cat)
287 while (b->file[cat].cache_entries)
289 struct ISAMB_cache_entry *ce_this = b->file[cat].cache_entries;
290 b->file[cat].cache_entries = ce_this->next;
294 yaz_log(b->log_io, "bf_write: flush_blocks");
295 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
302 static int cache_block (ISAMB b, ISAMC_P pos, char *userbuf, int wr)
304 int cat = (int) (pos&CAT_MASK);
305 int off = (int) (((pos/CAT_MAX) &
306 (ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size - 1))
307 * b->file[cat].head.block_size);
308 zint norm = pos / (CAT_MASK*ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size);
310 struct ISAMB_cache_entry **ce, *ce_this = 0, **ce_last = 0;
315 assert (ISAMB_CACHE_ENTRY_SIZE >= b->file[cat].head.block_size);
316 for (ce = &b->file[cat].cache_entries; *ce; ce = &(*ce)->next, no++)
319 if ((*ce)->pos == norm)
322 *ce = (*ce)->next; /* remove from list */
324 ce_this->next = b->file[cat].cache_entries; /* move to front */
325 b->file[cat].cache_entries = ce_this;
329 memcpy (ce_this->buf + off, userbuf,
330 b->file[cat].head.block_size);
334 memcpy (userbuf, ce_this->buf + off,
335 b->file[cat].head.block_size);
342 assert (ce_last && *ce_last);
344 *ce_last = 0; /* remove the last entry from list */
347 yaz_log(b->log_io, "bf_write: cache_block");
348 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
353 ce_this = xmalloc(sizeof(*ce_this));
354 ce_this->next = b->file[cat].cache_entries;
355 b->file[cat].cache_entries = ce_this;
356 ce_this->buf = xmalloc(ISAMB_CACHE_ENTRY_SIZE);
358 yaz_log(b->log_io, "bf_read: cache_block");
359 if (!bf_read(b->file[cat].bf, norm, 0, 0, ce_this->buf))
360 memset (ce_this->buf, 0, ISAMB_CACHE_ENTRY_SIZE);
363 memcpy (ce_this->buf + off, userbuf, b->file[cat].head.block_size);
369 memcpy (userbuf, ce_this->buf + off, b->file[cat].head.block_size);
375 void isamb_close (ISAMB isamb)
378 for (i = 0; isamb->accessed_nodes[i]; i++)
379 yaz_log(YLOG_DEBUG, "isamb_close level leaf-%d: "ZINT_FORMAT" read, "
380 ZINT_FORMAT" skipped",
381 i, isamb->accessed_nodes[i], isamb->skipped_nodes[i]);
382 yaz_log(YLOG_DEBUG, "isamb_close returned "ZINT_FORMAT" values, "
383 "skipped "ZINT_FORMAT,
384 isamb->skipped_numbers, isamb->returned_numbers);
385 for (i = 0; i<isamb->no_cat; i++)
387 flush_blocks (isamb, i);
388 if (isamb->file[i].head_dirty)
390 char hbuf[DST_BUF_SIZE];
391 int major = ISAMB_MAJOR_VERSION;
392 int minor = ISAMB_MINOR_VERSION;
394 char *dst = hbuf + 16;
396 int b_size = isamb->file[i].head.block_size;
398 encode_ptr(&dst, isamb->file[i].head.first_block);
399 encode_ptr(&dst, isamb->file[i].head.last_block);
400 encode_ptr(&dst, isamb->file[i].head.block_size);
401 encode_ptr(&dst, isamb->file[i].head.block_max);
402 encode_ptr(&dst, isamb->file[i].head.free_list);
403 memset(dst, '\0', b_size); /* ensure no random bytes are written */
407 /* print exactly 16 bytes (including trailing 0) */
408 sprintf(hbuf, "isamb%02d %02d %02d\r\n", major, minor, len);
410 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf);
412 for (left = len - b_size; left > 0; left = left - b_size)
415 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size);
418 bf_close (isamb->file[i].bf);
421 xfree(isamb->method);
425 /* open_block: read one block at pos.
426 Decode leading sys bytes .. consisting of
428 0: leader byte, != 0 leaf, == 0, non-leaf
429 1-2: used size of block
430 3-7*: number of items and all children
432 * Reserve 5 bytes for large block sizes. 1 for small ones .. Number
433 of items. We can thus have at most 2^40 nodes.
435 static struct ISAMB_block *open_block(ISAMB b, ISAMC_P pos)
437 int cat = (int) (pos&CAT_MASK);
439 int offset = b->file[cat].head.block_offset;
440 struct ISAMB_block *p;
443 p = xmalloc(sizeof(*p));
445 p->cat = (int) (pos & CAT_MASK);
446 p->buf = xmalloc(b->file[cat].head.block_size);
449 if (!cache_block (b, pos, p->buf, 0))
451 yaz_log(b->log_io, "bf_read: open_block");
452 if (!bf_read(b->file[cat].bf, pos/CAT_MAX, 0, 0, p->buf))
454 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
455 (long) pos, (long) pos/CAT_MAX);
459 p->bytes = p->buf + offset;
461 p->size = (p->buf[1] + 256 * p->buf[2]) - offset;
464 yaz_log(YLOG_FATAL, "Bad block size %d in pos=" ZINT_FORMAT "\n",
467 assert (p->size >= 0);
469 decode_ptr(&src, &p->no_items);
474 p->decodeClientData = (*b->method->codec.start)();
478 struct ISAMB_block *new_block (ISAMB b, int leaf, int cat)
480 struct ISAMB_block *p;
482 p = xmalloc(sizeof(*p));
483 p->buf = xmalloc(b->file[cat].head.block_size);
485 if (!b->file[cat].head.free_list)
488 block_no = b->file[cat].head.last_block++;
489 p->pos = block_no * CAT_MAX + cat;
493 p->pos = b->file[cat].head.free_list;
494 assert((p->pos & CAT_MASK) == cat);
495 if (!cache_block (b, p->pos, p->buf, 0))
497 yaz_log(b->log_io, "bf_read: new_block");
498 if (!bf_read(b->file[cat].bf, p->pos/CAT_MAX, 0, 0, p->buf))
500 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
501 (long) p->pos/CAT_MAX, (long) p->pos/CAT_MAX);
505 yaz_log(b->log_freelist, "got block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT, p->pos,
506 cat, p->pos/CAT_MAX);
507 memcpy (&b->file[cat].head.free_list, p->buf, sizeof(zint));
510 b->file[cat].head_dirty = 1;
511 memset (p->buf, 0, b->file[cat].head.block_size);
512 p->bytes = p->buf + b->file[cat].head.block_offset;
519 p->decodeClientData = (*b->method->codec.start)();
523 struct ISAMB_block *new_leaf (ISAMB b, int cat)
525 return new_block (b, 1, cat);
529 struct ISAMB_block *new_int (ISAMB b, int cat)
531 return new_block (b, 0, cat);
534 static void check_block (ISAMB b, struct ISAMB_block *p)
536 assert(b); /* mostly to make the compiler shut up about unused b */
544 char *startp = p->bytes;
545 const char *src = startp;
546 char *endp = p->bytes + p->size;
548 void *c1 = (*b->method->codec.start)();
550 decode_ptr(&src, &pos);
551 assert ((pos&CAT_MASK) == p->cat);
555 char file_item_buf[DST_ITEM_MAX];
556 char *file_item = file_item_buf;
557 (*b->method->codec.reset)(c1);
558 (*b->method->codec.decode)(c1, &file_item, &src);
561 decode_item_len(&src, &item_len);
562 assert (item_len > 0 && item_len < 80);
565 decode_ptr(&src, &pos);
566 if ((pos&CAT_MASK) != p->cat)
568 assert ((pos&CAT_MASK) == p->cat);
571 (*b->method->codec.stop)(c1);
575 void close_block(ISAMB b, struct ISAMB_block *p)
581 yaz_log(b->log_freelist, "release block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT,
582 p->pos, p->cat, p->pos/CAT_MAX);
583 memcpy (p->buf, &b->file[p->cat].head.free_list, sizeof(zint));
584 b->file[p->cat].head.free_list = p->pos;
585 if (!cache_block (b, p->pos, p->buf, 1))
587 yaz_log(b->log_io, "bf_write: close_block (deleted)");
588 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
593 int offset = b->file[p->cat].head.block_offset;
594 int size = p->size + offset;
595 char *dst = p->buf + 3;
596 assert (p->size >= 0);
598 /* memset becuase encode_ptr usually does not write all bytes */
599 memset(p->buf, 0, b->file[p->cat].head.block_offset);
601 p->buf[1] = size & 255;
602 p->buf[2] = size >> 8;
603 encode_ptr(&dst, p->no_items);
605 if (!cache_block (b, p->pos, p->buf, 1))
607 yaz_log(b->log_io, "bf_write: close_block");
608 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
611 (*b->method->codec.stop)(p->decodeClientData);
616 int insert_sub (ISAMB b, struct ISAMB_block **p,
617 void *new_item, int *mode,
619 struct ISAMB_block **sp,
620 void *sub_item, int *sub_size,
621 const void *max_item);
623 int insert_int (ISAMB b, struct ISAMB_block *p, void *lookahead_item,
625 ISAMC_I *stream, struct ISAMB_block **sp,
626 void *split_item, int *split_size, const void *last_max_item)
628 char *startp = p->bytes;
629 const char *src = startp;
630 char *endp = p->bytes + p->size;
632 struct ISAMB_block *sub_p1 = 0, *sub_p2 = 0;
633 char sub_item[DST_ITEM_MAX];
637 void *c1 = (*b->method->codec.start)();
641 assert(p->size >= 0);
642 decode_ptr(&src, &pos);
646 const char *src0 = src;
648 char file_item_buf[DST_ITEM_MAX];
649 char *file_item = file_item_buf;
650 (*b->method->codec.reset)(c1);
651 (*b->method->codec.decode)(c1, &file_item, &src);
652 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
655 sub_p1 = open_block(b, pos);
657 diff_terms -= sub_p1->no_items;
658 more = insert_sub (b, &sub_p1, lookahead_item, mode,
660 sub_item, &sub_size, file_item_buf);
661 diff_terms += sub_p1->no_items;
667 decode_item_len(&src, &item_len);
668 d = (*b->method->compare_item)(src, lookahead_item);
671 sub_p1 = open_block(b, pos);
673 diff_terms -= sub_p1->no_items;
674 more = insert_sub (b, &sub_p1, lookahead_item, mode,
676 sub_item, &sub_size, src);
677 diff_terms += sub_p1->no_items;
683 decode_ptr(&src, &pos);
687 /* we reached the end. So lookahead > last item */
688 sub_p1 = open_block(b, pos);
690 diff_terms -= sub_p1->no_items;
691 more = insert_sub (b, &sub_p1, lookahead_item, mode, stream, &sub_p2,
692 sub_item, &sub_size, last_max_item);
693 diff_terms += sub_p1->no_items;
696 diff_terms += sub_p2->no_items;
700 p->no_items += diff_terms;
704 /* there was a split - must insert pointer in this one */
705 char dst_buf[DST_BUF_SIZE];
708 const char *sub_item_ptr = sub_item;
710 assert (sub_size < 80 && sub_size > 1);
712 memcpy (dst, startp, src - startp);
717 (*b->method->codec.reset)(c1);
718 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
720 encode_item_len (&dst, sub_size); /* sub length and item */
721 memcpy (dst, sub_item, sub_size);
725 encode_ptr(&dst, sub_p2->pos); /* pos */
727 if (endp - src) /* remaining data */
729 memcpy (dst, src, endp - src);
732 p->size = dst - dst_buf;
733 assert (p->size >= 0);
736 if (p->size <= b->file[p->cat].head.block_max)
738 /* it fits OK in this block */
739 memcpy (startp, dst_buf, dst - dst_buf);
743 /* must split _this_ block as well .. */
744 struct ISAMB_block *sub_p3;
746 char file_item_buf[DST_ITEM_MAX];
747 char *file_item = file_item_buf;
751 zint no_items_first_half = 0;
757 half = src + b->file[p->cat].head.block_size/2;
758 decode_ptr(&src, &pos);
760 /* read sub block so we can get no_items for it */
761 sub_p3 = open_block(b, pos);
762 no_items_first_half += sub_p3->no_items;
763 close_block(b, sub_p3);
768 file_item = file_item_buf;
769 (*b->method->codec.reset)(c1);
770 (*b->method->codec.decode)(c1, &file_item, &src);
772 decode_item_len(&src, &split_size_tmp);
773 *split_size = (int) split_size_tmp;
776 decode_ptr(&src, &pos);
778 /* read sub block so we can get no_items for it */
779 sub_p3 = open_block(b, pos);
780 no_items_first_half += sub_p3->no_items;
781 close_block(b, sub_p3);
783 /* p is first half */
784 p_new_size = src - dst_buf;
785 memcpy (p->bytes, dst_buf, p_new_size);
788 file_item = file_item_buf;
789 (*b->method->codec.reset)(c1);
790 (*b->method->codec.decode)(c1, &file_item, &src);
791 *split_size = file_item - file_item_buf;
792 memcpy(split_item, file_item_buf, *split_size);
794 decode_item_len(&src, &split_size_tmp);
795 *split_size = (int) split_size_tmp;
796 memcpy (split_item, src, *split_size);
799 /* *sp is second half */
800 *sp = new_int (b, p->cat);
801 (*sp)->size = endp - src;
802 memcpy ((*sp)->bytes, src, (*sp)->size);
804 p->size = p_new_size;
806 /* adjust no_items in first&second half */
807 (*sp)->no_items = p->no_items - no_items_first_half;
808 p->no_items = no_items_first_half;
811 close_block(b, sub_p2);
813 close_block(b, sub_p1);
814 (*b->method->codec.stop)(c1);
818 int insert_leaf (ISAMB b, struct ISAMB_block **sp1, void *lookahead_item,
819 int *lookahead_mode, ISAMC_I *stream,
820 struct ISAMB_block **sp2,
821 void *sub_item, int *sub_size,
822 const void *max_item)
824 struct ISAMB_block *p = *sp1;
827 char dst_buf[DST_BUF_SIZE], *dst = dst_buf;
829 void *c1 = (*b->method->codec.start)();
830 void *c2 = (*b->method->codec.start)();
832 int quater = b->file[b->no_cat-1].head.block_max / 4;
833 char *mid_cut = dst_buf + quater * 2;
834 char *tail_cut = dst_buf + quater * 3;
835 char *maxp = dst_buf + b->file[b->no_cat-1].head.block_max;
838 char cut_item_buf[DST_ITEM_MAX];
839 int cut_item_size = 0;
840 int no_items = 0; /* number of items (total) */
841 int no_items_1 = 0; /* number of items (first half) */
845 char file_item_buf[DST_ITEM_MAX];
846 char *file_item = file_item_buf;
849 endp = p->bytes + p->size;
850 (*b->method->codec.decode)(c1, &file_item, &src);
853 const char *dst_item = 0; /* resulting item to be inserted */
854 char *lookahead_next;
858 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
860 /* d now holds comparison between existing file item and
863 d > 0: lookahead before file
864 d < 0: lookahead after file
868 /* lookahead must be inserted */
869 dst_item = lookahead_item;
870 /* if this is not an insertion, it's really bad .. */
871 if (!*lookahead_mode)
873 yaz_log(YLOG_WARN, "isamb: Inconsistent register (1)");
874 assert (*lookahead_mode);
878 dst_item = file_item_buf;
881 if (!*lookahead_mode && d == 0)
883 /* it's a deletion and they match so there is nothing to be
884 inserted anyway .. But mark the thing bad (file item
885 was part of input.. The item will not be part of output */
888 else if (!half1 && dst > mid_cut)
890 /* we have reached the splitting point for the first time */
891 const char *dst_item_0 = dst_item;
892 half1 = dst; /* candidate for splitting */
894 /* encode the resulting item */
895 (*b->method->codec.encode)(c2, &dst, &dst_item);
897 cut_item_size = dst_item - dst_item_0;
898 assert(cut_item_size > 0);
899 memcpy (cut_item_buf, dst_item_0, cut_item_size);
902 no_items_1 = no_items;
907 /* encode the resulting item */
908 (*b->method->codec.encode)(c2, &dst, &dst_item);
912 /* now move "pointers" .. result has been encoded .. */
915 /* we must move the lookahead pointer */
918 /* no more room. Mark lookahead as "gone".. */
922 /* move it really.. */
923 lookahead_next = lookahead_item;
924 if (!(*stream->read_item)(stream->clientData,
928 /* end of stream reached: no "more" and no lookahead */
932 if (lookahead_item && max_item &&
933 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
935 /* the lookahead goes beyond what we allow in this
936 leaf. Mark it as "gone" */
945 /* exact match .. move both pointers */
947 lookahead_next = lookahead_item;
948 if (!(*stream->read_item)(stream->clientData,
949 &lookahead_next, lookahead_mode))
955 break; /* end of file stream reached .. */
956 file_item = file_item_buf; /* move file pointer */
957 (*b->method->codec.decode)(c1, &file_item, &src);
961 /* file pointer must be moved */
964 file_item = file_item_buf;
965 (*b->method->codec.decode)(c1, &file_item, &src);
970 /* this loop runs when we are "appending" to a leaf page. That is
971 either it's empty (new) or all file items have been read in
974 /* determine maximum ptr for tail */
977 /* split already. No more splits - fill up to one full (Extra) block */
978 maxp = half2 + b->file[b->no_cat-1].head.block_max;
982 /* no split. Fill up to 1+1/4 block */
983 maxp = dst_buf + b->file[b->no_cat-1].head.block_max + quater;
985 while (lookahead_item)
988 const char *src = lookahead_item;
991 /* if we have a lookahead item, we stop if we exceed the value of it */
993 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
995 /* stop if we have reached the value of max item */
998 if (!*lookahead_mode)
1000 /* this is append. So a delete is bad */
1001 yaz_log(YLOG_WARN, "isamb: Inconsistent register (2)");
1004 else if (!half1 && dst > tail_cut)
1006 const char *src_0 = src;
1007 half1 = dst; /* candidate for splitting */
1009 (*b->method->codec.encode)(c2, &dst, &src);
1011 cut_item_size = src - src_0;
1012 assert(cut_item_size > 0);
1013 memcpy (cut_item_buf, src_0, cut_item_size);
1015 no_items_1 = no_items;
1019 (*b->method->codec.encode)(c2, &dst, &src);
1029 dst_item = lookahead_item;
1030 if (!(*stream->read_item)(stream->clientData, &dst_item,
1037 new_size = dst - dst_buf;
1038 if (p && p->cat != b->no_cat-1 &&
1039 new_size > b->file[p->cat].head.block_max)
1041 /* non-btree block will be removed */
1044 /* delete it too!! */
1045 p = 0; /* make a new one anyway */
1048 { /* must create a new one */
1050 for (i = 0; i < b->no_cat; i++)
1051 if (new_size <= b->file[i].head.block_max)
1055 p = new_leaf (b, i);
1057 if (new_size > b->file[p->cat].head.block_max)
1060 const char *cut_item = cut_item_buf;
1065 assert(cut_item_size > 0);
1068 p->size = half1 - dst_buf;
1069 memcpy (p->bytes, dst_buf, half1 - dst_buf);
1070 p->no_items = no_items_1;
1073 *sp2 = new_leaf (b, p->cat);
1075 (*b->method->codec.reset)(c2);
1077 first_dst = (*sp2)->bytes;
1079 (*b->method->codec.encode)(c2, &first_dst, &cut_item);
1081 memcpy (first_dst, half2, dst - half2);
1083 (*sp2)->size = (first_dst - (*sp2)->bytes) + (dst - half2);
1084 (*sp2)->no_items = no_items - no_items_1;
1087 memcpy (sub_item, cut_item_buf, cut_item_size);
1088 *sub_size = cut_item_size;
1092 memcpy (p->bytes, dst_buf, dst - dst_buf);
1094 p->no_items = no_items;
1096 (*b->method->codec.stop)(c1);
1097 (*b->method->codec.stop)(c2);
1102 int insert_sub (ISAMB b, struct ISAMB_block **p, void *new_item,
1105 struct ISAMB_block **sp,
1106 void *sub_item, int *sub_size,
1107 const void *max_item)
1109 if (!*p || (*p)->leaf)
1110 return insert_leaf (b, p, new_item, mode, stream, sp, sub_item,
1111 sub_size, max_item);
1113 return insert_int (b, *p, new_item, mode, stream, sp, sub_item,
1114 sub_size, max_item);
1117 int isamb_unlink (ISAMB b, ISAMC_P pos)
1119 struct ISAMB_block *p1;
1123 p1 = open_block(b, pos);
1128 const char *src = p1->bytes + p1->offset;
1130 void *c1 = (*b->method->codec.start)();
1132 decode_ptr(&src, &sub_p);
1133 isamb_unlink(b, sub_p);
1135 while (src != p1->bytes + p1->size)
1138 char file_item_buf[DST_ITEM_MAX];
1139 char *file_item = file_item_buf;
1140 (*b->method->codec.reset)(c1);
1141 (*b->method->codec.decode)(c1, &file_item, &src);
1144 decode_item_len(&src, &item_len);
1147 decode_ptr(&src, &sub_p);
1148 isamb_unlink(b, sub_p);
1151 (*b->method->codec.stop)(c1);
1158 ISAMB_P isamb_merge (ISAMB b, ISAMC_P pos, ISAMC_I *stream)
1160 char item_buf[DST_ITEM_MAX];
1164 int must_delete = 0;
1171 item_ptr = item_buf;
1173 (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1177 item_ptr = item_buf;
1178 more = (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1181 struct ISAMB_block *p = 0, *sp = 0;
1182 char sub_item[DST_ITEM_MAX];
1186 p = open_block(b, pos);
1187 more = insert_sub (b, &p, item_buf, &i_mode, stream, &sp,
1188 sub_item, &sub_size, 0);
1190 { /* increase level of tree by one */
1191 struct ISAMB_block *p2 = new_int (b, p->cat);
1192 char *dst = p2->bytes + p2->size;
1194 void *c1 = (*b->method->codec.start)();
1195 const char *sub_item_ptr = sub_item;
1198 encode_ptr(&dst, p->pos);
1199 assert (sub_size < 80 && sub_size > 1);
1201 (*b->method->codec.reset)(c1);
1202 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
1204 encode_item_len (&dst, sub_size);
1205 memcpy (dst, sub_item, sub_size);
1208 encode_ptr(&dst, sp->pos);
1210 p2->size = dst - p2->bytes;
1211 p2->no_items = p->no_items + sp->no_items;
1212 pos = p2->pos; /* return new super page */
1216 (*b->method->codec.stop)(c1);
1221 pos = p->pos; /* return current one (again) */
1223 if (p->no_items == 0)
1231 isamb_unlink(b, pos);
1237 ISAMB_PP isamb_pp_open_x(ISAMB isamb, ISAMB_P pos, int *level, int scope)
1239 ISAMB_PP pp = xmalloc(sizeof(*pp));
1245 pp->block = xmalloc(ISAMB_MAX_LEVEL * sizeof(*pp->block));
1252 pp->skipped_numbers = 0;
1253 pp->returned_numbers = 0;
1255 for (i = 0; i<ISAMB_MAX_LEVEL; i++)
1256 pp->skipped_nodes[i] = pp->accessed_nodes[i] = 0;
1259 struct ISAMB_block *p = open_block(isamb, pos);
1260 const char *src = p->bytes + p->offset;
1261 pp->block[pp->level] = p;
1263 pp->total_size += p->size;
1267 decode_ptr(&src, &pos);
1268 p->offset = src - p->bytes;
1270 pp->accessed_nodes[pp->level]++;
1272 pp->block[pp->level+1] = 0;
1273 pp->maxlevel = pp->level;
1279 ISAMB_PP isamb_pp_open (ISAMB isamb, ISAMB_P pos, int scope)
1281 return isamb_pp_open_x(isamb, pos, 0, scope);
1284 void isamb_pp_close_x(ISAMB_PP pp, zint *size, zint *blocks)
1289 yaz_log(YLOG_DEBUG, "isamb_pp_close lev=%d returned "ZINT_FORMAT" values, "
1290 "skipped "ZINT_FORMAT,
1291 pp->maxlevel, pp->skipped_numbers, pp->returned_numbers);
1292 for (i = pp->maxlevel; i>=0; i--)
1293 if (pp->skipped_nodes[i] || pp->accessed_nodes[i])
1294 yaz_log(YLOG_DEBUG, "isamb_pp_close level leaf-%d: "
1295 ZINT_FORMAT" read, "ZINT_FORMAT" skipped", i,
1296 pp->accessed_nodes[i], pp->skipped_nodes[i]);
1297 pp->isamb->skipped_numbers += pp->skipped_numbers;
1298 pp->isamb->returned_numbers += pp->returned_numbers;
1299 for (i = pp->maxlevel; i>=0; i--)
1301 pp->isamb->accessed_nodes[i] += pp->accessed_nodes[i];
1302 pp->isamb->skipped_nodes[i] += pp->skipped_nodes[i];
1305 *size = pp->total_size;
1307 *blocks = pp->no_blocks;
1308 for (i = 0; i <= pp->level; i++)
1309 close_block(pp->isamb, pp->block[i]);
1314 int isamb_block_info (ISAMB isamb, int cat)
1316 if (cat >= 0 && cat < isamb->no_cat)
1317 return isamb->file[cat].head.block_size;
1321 void isamb_pp_close (ISAMB_PP pp)
1323 isamb_pp_close_x(pp, 0, 0);
1326 /* simple recursive dumper .. */
1327 static void isamb_dump_r (ISAMB b, ISAMB_P pos, void (*pr)(const char *str),
1331 char prefix_str[1024];
1334 struct ISAMB_block *p = open_block(b, pos);
1335 sprintf(prefix_str, "%*s " ZINT_FORMAT " cat=%d size=%d max=%d items="
1336 ZINT_FORMAT, level*2, "",
1337 pos, p->cat, p->size, b->file[p->cat].head.block_max,
1340 sprintf(prefix_str, "%*s " ZINT_FORMAT, level*2, "", pos);
1343 while (p->offset < p->size)
1345 const char *src = p->bytes + p->offset;
1347 (*b->method->codec.decode)(p->decodeClientData, &dst, &src);
1348 (*b->method->log_item)(YLOG_DEBUG, buf, prefix_str);
1349 p->offset = src - (char*) p->bytes;
1351 assert(p->offset == p->size);
1355 const char *src = p->bytes + p->offset;
1358 decode_ptr(&src, &sub);
1359 p->offset = src - (char*) p->bytes;
1361 isamb_dump_r(b, sub, pr, level+1);
1363 while (p->offset < p->size)
1366 char file_item_buf[DST_ITEM_MAX];
1367 char *file_item = file_item_buf;
1368 void *c1 = (*b->method->codec.start)();
1369 (*b->method->codec.decode)(c1, &file_item, &src);
1370 (*b->method->codec.stop)(c1);
1371 (*b->method->log_item)(YLOG_DEBUG, file_item_buf, prefix_str);
1374 decode_item_len(&src, &item_len);
1375 (*b->method->log_item)(YLOG_DEBUG, src, prefix_str);
1378 decode_ptr(&src, &sub);
1380 p->offset = src - (char*) p->bytes;
1382 isamb_dump_r(b, sub, pr, level+1);
1389 void isamb_dump(ISAMB b, ISAMB_P pos, void (*pr)(const char *str))
1391 isamb_dump_r(b, pos, pr, 0);
1394 int isamb_pp_read(ISAMB_PP pp, void *buf)
1396 return isamb_pp_forward(pp, buf, 0);
1400 static int isamb_pp_on_right_node(ISAMB_PP pp, int level, const void *untilbuf)
1401 { /* looks one node higher to see if we should be on this node at all */
1402 /* useful in backing off quickly, and in avoiding tail descends */
1403 /* call with pp->level to begin with */
1404 struct ISAMB_block *p;
1407 ISAMB b = pp->isamb;
1413 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true for root");
1415 return 1; /* we can never skip the root node */
1418 p = pp->block[level];
1419 assert(p->offset <= p->size);
1420 if (p->offset < p->size)
1423 char file_item_buf[DST_ITEM_MAX];
1424 char *file_item = file_item_buf;
1425 void *c1 = (*b->method->codec.start)();
1426 assert(p->offset > 0);
1427 src = p->bytes + p->offset;
1428 (*b->method->codec.decode)(c1, &file_item, &src);
1429 (*b->method->codec.stop)(c1);
1430 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1433 assert(p->offset > 0);
1434 src = p->bytes + p->offset;
1435 decode_item_len(&src, &item_len);
1437 (*b->method->codec.log_item)(YLOG_DEBUG, untilbuf, "on_leaf: until");
1438 (*b->method->codec.log_item)(YLOG_DEBUG, src, "on_leaf: value");
1440 cmp = (*b->method->compare_item)(untilbuf, src);
1442 if (cmp < pp->scope)
1445 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true "
1446 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1453 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning false "
1454 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1461 yaz_log(YLOG_DEBUG, "isamb_pp_on_right at tail, looking higher "
1464 return isamb_pp_on_right_node(pp, level, untilbuf);
1466 } /* isamb_pp_on_right_node */
1468 static int isamb_pp_read_on_leaf(ISAMB_PP pp, void *buf)
1470 /* reads the next item on the current leaf, returns 0 if end of leaf*/
1471 struct ISAMB_block *p = pp->block[pp->level];
1476 if (p->offset == p->size)
1479 yaz_log(YLOG_DEBUG, "isamb_pp_read_on_leaf returning 0 on "
1482 return 0; /* at end of leaf */
1484 src = p->bytes + p->offset;
1486 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1487 p->offset = src - (char*) p->bytes;
1489 (*pp->isamb->method->codec.log_item)(YLOG_DEBUG, buf,
1490 "read_on_leaf returning 1");
1492 pp->returned_numbers++;
1494 } /* read_on_leaf */
1496 static int isamb_pp_forward_on_leaf(ISAMB_PP pp, void *buf, const void *untilbuf)
1497 { /* forwards on the current leaf, returns 0 if not found */
1502 if (!isamb_pp_read_on_leaf(pp, buf))
1504 /* FIXME - this is an extra function call, inline the read? */
1505 cmp=(*pp->isamb->method->compare_item)(untilbuf, buf);
1507 { /* cmp<2 found a good one */
1510 yaz_log(YLOG_DEBUG, "isam_pp_fwd_on_leaf skipped %d items", skips);
1512 pp->returned_numbers++;
1516 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1517 return 0; /* never mind the rest of this leaf */
1518 pp->skipped_numbers++;
1521 } /* forward_on_leaf */
1523 static int isamb_pp_climb_level(ISAMB_PP pp, ISAMB_P *pos)
1524 { /* climbs higher in the tree, until finds a level with data left */
1525 /* returns the node to (consider to) descend to in *pos) */
1526 struct ISAMB_block *p = pp->block[pp->level];
1529 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level starting "
1530 "at level %d node %d ofs=%d sz=%d",
1531 pp->level, p->pos, p->offset, p->size);
1533 assert(pp->level >= 0);
1534 assert(p->offset <= p->size);
1538 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level returning 0 at root");
1542 assert(pp->level>0);
1543 close_block(pp->isamb, pp->block[pp->level]);
1544 pp->block[pp->level] = 0;
1546 p = pp->block[pp->level];
1548 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level climbed to level %d node %d ofs=%d",
1549 pp->level, p->pos, p->offset);
1552 assert(p->offset <= p->size);
1553 if (p->offset == p->size)
1555 /* we came from the last pointer, climb on */
1556 if (!isamb_pp_climb_level(pp, pos))
1558 p = pp->block[pp->level];
1563 char file_item_buf[DST_ITEM_MAX];
1564 char *file_item = file_item_buf;
1565 ISAMB b = pp->isamb;
1566 void *c1 = (*b->method->codec.start)();
1570 /* skip the child we just came from */
1572 yaz_log(YLOG_DEBUG, "isam_pp_climb_level: skipping lev=%d ofs=%d sz=%d",
1573 pp->level, p->offset, p->size);
1575 assert (p->offset < p->size);
1576 src = p->bytes + p->offset;
1578 (*b->method->codec.decode)(c1, &file_item, &src);
1579 (*b->method->codec.stop)(c1);
1581 decode_item_len(&src, &item_len);
1584 decode_ptr(&src, pos);
1585 p->offset = src - (char *)p->bytes;
1592 static zint isamb_pp_forward_unode(ISAMB_PP pp, zint pos, const void *untilbuf)
1593 { /* scans a upper node until it finds a child <= untilbuf */
1594 /* pp points to the key value, as always. pos is the child read from */
1596 /* if all values are too small, returns the last child in the node */
1597 /* FIXME - this can be detected, and avoided by looking at the */
1598 /* parent node, but that gets messy. Presumably the cost is */
1599 /* pretty low anyway */
1600 ISAMB b = pp->isamb;
1601 struct ISAMB_block *p = pp->block[pp->level];
1602 const char *src = p->bytes + p->offset;
1607 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode starting "
1608 "at level %d node %d ofs=%di sz=%d",
1609 pp->level, p->pos, p->offset, p->size);
1612 assert(p->offset <= p->size);
1613 if (p->offset == p->size)
1616 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at end "
1617 "at level %d node %d ofs=%di sz=%d",
1618 pp->level, p->pos, p->offset, p->size);
1620 return pos; /* already at the end of it */
1622 while(p->offset < p->size)
1625 char file_item_buf[DST_ITEM_MAX];
1626 char *file_item = file_item_buf;
1627 void *c1 = (*b->method->codec.start)();
1628 (*b->method->codec.decode)(c1, &file_item, &src);
1629 (*b->method->codec.stop)(c1);
1630 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1633 decode_item_len(&src, &item_len);
1634 cmp = (*b->method->compare_item)(untilbuf, src);
1637 decode_ptr(&src, &nxtpos);
1638 if (cmp<pp->scope) /* cmp<2 */
1641 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning a hit "
1642 "at level %d node %d ofs=%d sz=%d",
1643 pp->level, p->pos, p->offset, p->size);
1648 p->offset = src-(char*)p->bytes;
1649 (pp->skipped_nodes[pp->maxlevel - pp->level -1])++;
1655 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at tail "
1656 "at level %d node %d ofs=%d sz=%d skips=%d",
1657 pp->level, p->pos, p->offset, p->size, skips);
1659 return pos; /* that's the last one in the line */
1661 } /* forward_unode */
1663 static void isamb_pp_descend_to_leaf(ISAMB_PP pp, ISAMB_P pos,
1664 const void *untilbuf)
1665 { /* climbs down the tree, from pos, to the leftmost leaf */
1666 struct ISAMB_block *p = pp->block[pp->level];
1670 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1671 "starting at lev %d node %d ofs=%d lf=%d u=%p",
1672 pp->level, p->pos, p->offset, p->leaf, untilbuf);
1675 pos = isamb_pp_forward_unode(pp, pos, untilbuf);
1678 p = open_block(pp->isamb, pos);
1679 pp->block[pp->level] = p;
1680 ++(pp->accessed_nodes[pp->maxlevel-pp->level]);
1683 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1684 "got lev %d node %d lf=%d",
1685 pp->level, p->pos, p->leaf);
1689 assert (p->offset==0);
1690 src = p->bytes + p->offset;
1691 decode_ptr(&src, &pos);
1692 p->offset = src-(char*)p->bytes;
1693 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1695 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1696 "returning at lev %d node %d ofs=%d lf=%d",
1697 pp->level, p->pos, p->offset, p->leaf);
1699 } /* descend_to_leaf */
1701 static int isamb_pp_find_next_leaf(ISAMB_PP pp)
1702 { /* finds the next leaf by climbing up and down */
1704 if (!isamb_pp_climb_level(pp, &pos))
1706 isamb_pp_descend_to_leaf(pp, pos, 0);
1710 static int isamb_pp_climb_desc(ISAMB_PP pp, const void *untilbuf)
1711 { /* climbs up and descends to a leaf where values >= *untilbuf are found */
1714 struct ISAMB_block *p = pp->block[pp->level];
1715 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc starting "
1716 "at level %d node %d ofs=%d sz=%d",
1717 pp->level, p->pos, p->offset, p->size);
1719 if (!isamb_pp_climb_level(pp, &pos))
1721 /* see if it would pay to climb one higher */
1722 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1723 if (!isamb_pp_climb_level(pp, &pos))
1725 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1727 p = pp->block[pp->level];
1728 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc done "
1729 "at level %d node %d ofs=%d sz=%d",
1730 pp->level, p->pos, p->offset, p->size);
1735 int isamb_pp_forward (ISAMB_PP pp, void *buf, const void *untilbuf)
1738 struct ISAMB_block *p = pp->block[pp->level];
1740 yaz_log(YLOG_DEBUG, "isamb_pp_forward starting "
1741 "at level %d node %d ofs=%d sz=%d u=%p sc=%d",
1742 pp->level, p->pos, p->offset, p->size, untilbuf, scope);
1746 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1749 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (A) "
1750 "at level %d node %d ofs=%d sz=%d",
1751 pp->level, p->pos, p->offset, p->size);
1755 if (! isamb_pp_climb_desc(pp, untilbuf))
1758 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning notfound (B) "
1759 "at level %d node %d ofs=%d sz=%d",
1760 pp->level, p->pos, p->offset, p->size);
1762 return 0; /* could not find a leaf */
1765 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1768 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (c) "
1769 "at level %d node %d ofs=%d sz=%d",
1770 pp->level, p->pos, p->offset, p->size);
1774 } while (isamb_pp_find_next_leaf(pp));
1775 return 0; /* could not find at all */
1777 else { /* no untilbuf, a straight read */
1778 /* FIXME - this should be moved
1779 * directly into the pp_read */
1780 /* keeping here now, to keep same
1781 * interface as the old fwd */
1782 if (isamb_pp_read_on_leaf(pp, buf))
1785 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (D) "
1786 "at level %d node %d ofs=%d sz=%d",
1787 pp->level, p->pos, p->offset, p->size);
1791 if (isamb_pp_find_next_leaf(pp))
1794 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (E) "
1795 "at level %d node %d ofs=%d sz=%d",
1796 pp->level, p->pos, p->offset, p->size);
1798 return isamb_pp_read_on_leaf(pp, buf);
1803 } /* isam_pp_forward (new version) */
1805 void isamb_pp_pos(ISAMB_PP pp, double *current, double *total)
1806 { /* return an estimate of the current position and of the total number of */
1807 /* occureences in the isam tree, based on the current leaf */
1808 struct ISAMB_block *p = pp->block[pp->level];
1813 *total = (double) (pp->block[0]->no_items);
1814 *current = (double) pp->returned_numbers;
1816 yaz_log(YLOG_LOG, "isamb_pp_pos returning: cur= %0.1f tot=%0.1f rn="
1817 ZINT_FORMAT, *current, *total, pp->returned_numbers);
1821 int isamb_pp_forward2(ISAMB_PP pp, void *buf, const void *untilb)
1825 struct ISAMB_block *p = pp->block[pp->level];
1826 ISAMB b = pp->isamb;
1830 while (p->offset == p->size)
1836 char file_item_buf[DST_ITEM_MAX];
1837 char *file_item = file_item_buf;
1841 while (p->offset == p->size)
1845 close_block (pp->isamb, pp->block[pp->level]);
1846 pp->block[pp->level] = 0;
1848 p = pp->block[pp->level];
1853 src = p->bytes + p->offset;
1856 c1 = (*b->method->codec.start)();
1857 (*b->method->codec.decode)(c1, &file_item, &src);
1859 decode_ptr (&src, &item_len);
1862 decode_ptr (&src, &pos);
1863 p->offset = src - (char*) p->bytes;
1865 src = p->bytes + p->offset;
1869 if (!untilb || p->offset == p->size)
1871 assert(p->offset < p->size);
1874 file_item = file_item_buf;
1875 (*b->method->codec.reset)(c1);
1876 (*b->method->codec.decode)(c1, &file_item, &src);
1877 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1883 decode_item_len(&src, &item_len);
1884 if ((*b->method->compare_item)(untilb, src) <= 1)
1888 decode_ptr (&src, &pos);
1889 p->offset = src - (char*) p->bytes;
1896 pp->block[pp->level] = p = open_block (pp->isamb, pos);
1898 pp->total_size += p->size;
1906 src = p->bytes + p->offset;
1909 decode_ptr (&src, &pos);
1910 p->offset = src - (char*) p->bytes;
1912 if (!untilb || p->offset == p->size)
1914 assert(p->offset < p->size);
1917 file_item = file_item_buf;
1918 (*b->method->codec.reset)(c1);
1919 (*b->method->codec.decode)(c1, &file_item, &src);
1920 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1926 decode_ptr (&src, &item_len);
1927 if ((*b->method->compare_item)(untilb, src) <= 1)
1935 (*b->method->codec.stop)(c1);
1938 assert (p->offset < p->size);
1943 src = p->bytes + p->offset;
1944 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1945 p->offset = src - (char*) p->bytes;
1946 if (!untilb || (*pp->isamb->method->compare_item)(untilb, dst0) <= 1)
1949 if (p->offset == p->size) goto again;