1 /* $Id: isamb.c,v 1.85 2006-11-14 08:12:08 adam Exp $
2 Copyright (C) 1995-2006
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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
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 **src, zint *pos)
166 while (((c = *(const unsigned char *)((*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);
203 for (i = 0; i < isamb->no_cat; i++)
205 isamb->file[i].bf = 0;
206 isamb->file[i].head_dirty = 0;
207 isamb->file[i].cache_entries = 0;
210 for (i = 0; i < isamb->no_cat; i++)
212 char fname[DST_BUF_SIZE];
213 char hbuf[DST_BUF_SIZE];
215 sprintf(fname, "%s%c", name, i+'A');
217 isamb->file[i].bf = bf_open(bfs, fname, ISAMB_CACHE_ENTRY_SIZE,
220 isamb->file[i].bf = bf_open(bfs, fname, b_size, writeflag);
222 if (!isamb->file[i].bf)
228 /* fill-in default values (for empty isamb) */
229 isamb->file[i].head.first_block = ISAMB_CACHE_ENTRY_SIZE/b_size+1;
230 isamb->file[i].head.last_block = isamb->file[i].head.first_block;
231 isamb->file[i].head.block_size = b_size;
233 if (i == isamb->no_cat-1 || b_size > 128)
234 isamb->file[i].head.block_offset = 8;
236 isamb->file[i].head.block_offset = 4;
238 isamb->file[i].head.block_offset = 11;
240 isamb->file[i].head.block_max =
241 b_size - isamb->file[i].head.block_offset;
242 isamb->file[i].head.free_list = 0;
243 if (bf_read(isamb->file[i].bf, 0, 0, 0, hbuf))
245 /* got header assume "isamb"major minor len can fit in 16 bytes */
247 int major, minor, len, pos = 0;
250 if (memcmp(hbuf, "isamb", 5))
252 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
255 if (sscanf(hbuf+5, "%d %d %d", &major, &minor, &len) != 3)
257 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
260 if (major != ISAMB_MAJOR_VERSION)
262 yaz_log(YLOG_WARN, "bad major version for file %s %d, must be %d",
263 fname, major, ISAMB_MAJOR_VERSION);
266 for (left = len - b_size; left > 0; left = left - b_size)
269 if (!bf_read(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size))
271 yaz_log(YLOG_WARN, "truncated isamb header for "
272 "file=%s len=%d pos=%d",
278 decode_ptr(&src, &isamb->file[i].head.first_block);
279 decode_ptr(&src, &isamb->file[i].head.last_block);
280 decode_ptr(&src, &zint_tmp);
281 isamb->file[i].head.block_size = (int) zint_tmp;
282 decode_ptr(&src, &zint_tmp);
283 isamb->file[i].head.block_max = (int) zint_tmp;
284 decode_ptr(&src, &isamb->file[i].head.free_list);
286 assert (isamb->file[i].head.block_size >= isamb->file[i].head.block_offset);
287 isamb->file[i].head_dirty = 0;
288 assert(isamb->file[i].head.block_size == b_size);
289 b_size = b_size * ISAMB_FAC_SIZE;
292 yaz_log(YLOG_WARN, "isamb debug enabled. Things will be slower than usual");
297 static void flush_blocks (ISAMB b, int cat)
299 while (b->file[cat].cache_entries)
301 struct ISAMB_cache_entry *ce_this = b->file[cat].cache_entries;
302 b->file[cat].cache_entries = ce_this->next;
306 yaz_log(b->log_io, "bf_write: flush_blocks");
307 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
314 static int cache_block (ISAMB b, ISAM_P pos, unsigned char *userbuf, int wr)
316 int cat = (int) (pos&CAT_MASK);
317 int off = (int) (((pos/CAT_MAX) &
318 (ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size - 1))
319 * b->file[cat].head.block_size);
320 zint norm = pos / (CAT_MASK*ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size);
322 struct ISAMB_cache_entry **ce, *ce_this = 0, **ce_last = 0;
327 assert (ISAMB_CACHE_ENTRY_SIZE >= b->file[cat].head.block_size);
328 for (ce = &b->file[cat].cache_entries; *ce; ce = &(*ce)->next, no++)
331 if ((*ce)->pos == norm)
334 *ce = (*ce)->next; /* remove from list */
336 ce_this->next = b->file[cat].cache_entries; /* move to front */
337 b->file[cat].cache_entries = ce_this;
341 memcpy (ce_this->buf + off, userbuf,
342 b->file[cat].head.block_size);
346 memcpy (userbuf, ce_this->buf + off,
347 b->file[cat].head.block_size);
354 assert (ce_last && *ce_last);
356 *ce_last = 0; /* remove the last entry from list */
359 yaz_log(b->log_io, "bf_write: cache_block");
360 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
365 ce_this = xmalloc(sizeof(*ce_this));
366 ce_this->next = b->file[cat].cache_entries;
367 b->file[cat].cache_entries = ce_this;
368 ce_this->buf = xmalloc(ISAMB_CACHE_ENTRY_SIZE);
370 yaz_log(b->log_io, "bf_read: cache_block");
371 if (!bf_read(b->file[cat].bf, norm, 0, 0, ce_this->buf))
372 memset (ce_this->buf, 0, ISAMB_CACHE_ENTRY_SIZE);
375 memcpy (ce_this->buf + off, userbuf, b->file[cat].head.block_size);
381 memcpy (userbuf, ce_this->buf + off, b->file[cat].head.block_size);
387 void isamb_close (ISAMB isamb)
390 for (i = 0; isamb->accessed_nodes[i]; i++)
391 yaz_log(YLOG_DEBUG, "isamb_close level leaf-%d: "ZINT_FORMAT" read, "
392 ZINT_FORMAT" skipped",
393 i, isamb->accessed_nodes[i], isamb->skipped_nodes[i]);
394 yaz_log(YLOG_DEBUG, "isamb_close returned "ZINT_FORMAT" values, "
395 "skipped "ZINT_FORMAT,
396 isamb->skipped_numbers, isamb->returned_numbers);
397 for (i = 0; i<isamb->no_cat; i++)
399 flush_blocks (isamb, i);
400 if (isamb->file[i].head_dirty)
402 char hbuf[DST_BUF_SIZE];
403 int major = ISAMB_MAJOR_VERSION;
404 int minor = ISAMB_MINOR_VERSION;
406 char *dst = hbuf + 16;
408 int b_size = isamb->file[i].head.block_size;
410 encode_ptr(&dst, isamb->file[i].head.first_block);
411 encode_ptr(&dst, isamb->file[i].head.last_block);
412 encode_ptr(&dst, isamb->file[i].head.block_size);
413 encode_ptr(&dst, isamb->file[i].head.block_max);
414 encode_ptr(&dst, isamb->file[i].head.free_list);
415 memset(dst, '\0', b_size); /* ensure no random bytes are written */
419 /* print exactly 16 bytes (including trailing 0) */
420 sprintf(hbuf, "isamb%02d %02d %02d\r\n", major, minor, len);
422 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf);
424 for (left = len - b_size; left > 0; left = left - b_size)
427 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size);
430 if (isamb->file[i].bf)
431 bf_close (isamb->file[i].bf);
434 xfree(isamb->method);
438 /* open_block: read one block at pos.
439 Decode leading sys bytes .. consisting of
441 0: leader byte, != 0 leaf, == 0, non-leaf
442 1-2: used size of block
443 3-7*: number of items and all children
445 * Reserve 5 bytes for large block sizes. 1 for small ones .. Number
446 of items. We can thus have at most 2^40 nodes.
448 static struct ISAMB_block *open_block(ISAMB b, ISAM_P pos)
450 int cat = (int) (pos&CAT_MASK);
452 int offset = b->file[cat].head.block_offset;
453 struct ISAMB_block *p;
456 p = xmalloc(sizeof(*p));
458 p->cat = (int) (pos & CAT_MASK);
459 p->buf = xmalloc(b->file[cat].head.block_size);
462 if (!cache_block (b, pos, p->buf, 0))
464 yaz_log(b->log_io, "bf_read: open_block");
465 if (!bf_read(b->file[cat].bf, pos/CAT_MAX, 0, 0, p->buf))
467 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
468 (long) pos, (long) pos/CAT_MAX);
472 p->bytes = (char *)p->buf + offset;
474 p->size = (p->buf[1] + 256 * p->buf[2]) - offset;
477 yaz_log(YLOG_FATAL, "Bad block size %d in pos=" ZINT_FORMAT "\n",
480 assert (p->size >= 0);
481 src = (char*) p->buf + 3;
482 decode_ptr(&src, &p->no_items);
487 p->decodeClientData = (*b->method->codec.start)();
491 struct ISAMB_block *new_block (ISAMB b, int leaf, int cat)
493 struct ISAMB_block *p;
495 p = xmalloc(sizeof(*p));
496 p->buf = xmalloc(b->file[cat].head.block_size);
498 if (!b->file[cat].head.free_list)
501 block_no = b->file[cat].head.last_block++;
502 p->pos = block_no * CAT_MAX + cat;
506 p->pos = b->file[cat].head.free_list;
507 assert((p->pos & CAT_MASK) == cat);
508 if (!cache_block (b, p->pos, p->buf, 0))
510 yaz_log(b->log_io, "bf_read: new_block");
511 if (!bf_read(b->file[cat].bf, p->pos/CAT_MAX, 0, 0, p->buf))
513 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
514 (long) p->pos/CAT_MAX, (long) p->pos/CAT_MAX);
518 yaz_log(b->log_freelist, "got block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT, p->pos,
519 cat, p->pos/CAT_MAX);
520 memcpy (&b->file[cat].head.free_list, p->buf, sizeof(zint));
523 b->file[cat].head_dirty = 1;
524 memset (p->buf, 0, b->file[cat].head.block_size);
525 p->bytes = (char*)p->buf + b->file[cat].head.block_offset;
532 p->decodeClientData = (*b->method->codec.start)();
536 struct ISAMB_block *new_leaf (ISAMB b, int cat)
538 return new_block (b, 1, cat);
542 struct ISAMB_block *new_int (ISAMB b, int cat)
544 return new_block (b, 0, cat);
547 static void check_block (ISAMB b, struct ISAMB_block *p)
549 assert(b); /* mostly to make the compiler shut up about unused b */
557 char *startp = p->bytes;
558 const char *src = startp;
559 char *endp = p->bytes + p->size;
561 void *c1 = (*b->method->codec.start)();
563 decode_ptr(&src, &pos);
564 assert ((pos&CAT_MASK) == p->cat);
568 char file_item_buf[DST_ITEM_MAX];
569 char *file_item = file_item_buf;
570 (*b->method->codec.reset)(c1);
571 (*b->method->codec.decode)(c1, &file_item, &src);
574 decode_item_len(&src, &item_len);
575 assert (item_len > 0 && item_len < 80);
578 decode_ptr(&src, &pos);
579 if ((pos&CAT_MASK) != p->cat)
581 assert ((pos&CAT_MASK) == p->cat);
584 (*b->method->codec.stop)(c1);
588 void close_block(ISAMB b, struct ISAMB_block *p)
594 yaz_log(b->log_freelist, "release block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT,
595 p->pos, p->cat, p->pos/CAT_MAX);
596 memcpy (p->buf, &b->file[p->cat].head.free_list, sizeof(zint));
597 b->file[p->cat].head.free_list = p->pos;
598 if (!cache_block (b, p->pos, p->buf, 1))
600 yaz_log(b->log_io, "bf_write: close_block (deleted)");
601 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
606 int offset = b->file[p->cat].head.block_offset;
607 int size = p->size + offset;
608 char *dst = (char*)p->buf + 3;
609 assert (p->size >= 0);
611 /* memset becuase encode_ptr usually does not write all bytes */
612 memset(p->buf, 0, b->file[p->cat].head.block_offset);
614 p->buf[1] = size & 255;
615 p->buf[2] = size >> 8;
616 encode_ptr(&dst, p->no_items);
618 if (!cache_block (b, p->pos, p->buf, 1))
620 yaz_log(b->log_io, "bf_write: close_block");
621 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
624 (*b->method->codec.stop)(p->decodeClientData);
629 int insert_sub (ISAMB b, struct ISAMB_block **p,
630 void *new_item, int *mode,
632 struct ISAMB_block **sp,
633 void *sub_item, int *sub_size,
634 const void *max_item);
636 int insert_int (ISAMB b, struct ISAMB_block *p, void *lookahead_item,
638 ISAMC_I *stream, struct ISAMB_block **sp,
639 void *split_item, int *split_size, const void *last_max_item)
641 char *startp = p->bytes;
642 const char *src = startp;
643 char *endp = p->bytes + p->size;
645 struct ISAMB_block *sub_p1 = 0, *sub_p2 = 0;
646 char sub_item[DST_ITEM_MAX];
650 void *c1 = (*b->method->codec.start)();
654 assert(p->size >= 0);
655 decode_ptr(&src, &pos);
659 const char *src0 = src;
661 char file_item_buf[DST_ITEM_MAX];
662 char *file_item = file_item_buf;
663 (*b->method->codec.reset)(c1);
664 (*b->method->codec.decode)(c1, &file_item, &src);
665 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
668 sub_p1 = open_block(b, pos);
670 diff_terms -= sub_p1->no_items;
671 more = insert_sub (b, &sub_p1, lookahead_item, mode,
673 sub_item, &sub_size, file_item_buf);
674 diff_terms += sub_p1->no_items;
680 decode_item_len(&src, &item_len);
681 d = (*b->method->compare_item)(src, lookahead_item);
684 sub_p1 = open_block(b, pos);
686 diff_terms -= sub_p1->no_items;
687 more = insert_sub (b, &sub_p1, lookahead_item, mode,
689 sub_item, &sub_size, src);
690 diff_terms += sub_p1->no_items;
696 decode_ptr(&src, &pos);
700 /* we reached the end. So lookahead > last item */
701 sub_p1 = open_block(b, pos);
703 diff_terms -= sub_p1->no_items;
704 more = insert_sub (b, &sub_p1, lookahead_item, mode, stream, &sub_p2,
705 sub_item, &sub_size, last_max_item);
706 diff_terms += sub_p1->no_items;
709 diff_terms += sub_p2->no_items;
713 p->no_items += diff_terms;
717 /* there was a split - must insert pointer in this one */
718 char dst_buf[DST_BUF_SIZE];
721 const char *sub_item_ptr = sub_item;
723 assert (sub_size < 80 && sub_size > 1);
725 memcpy (dst, startp, src - startp);
730 (*b->method->codec.reset)(c1);
731 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
733 encode_item_len (&dst, sub_size); /* sub length and item */
734 memcpy (dst, sub_item, sub_size);
738 encode_ptr(&dst, sub_p2->pos); /* pos */
740 if (endp - src) /* remaining data */
742 memcpy (dst, src, endp - src);
745 p->size = dst - dst_buf;
746 assert (p->size >= 0);
748 if (p->size <= b->file[p->cat].head.block_max)
750 /* it fits OK in this block */
751 memcpy (startp, dst_buf, dst - dst_buf);
753 close_block(b, sub_p2);
757 /* must split _this_ block as well .. */
758 struct ISAMB_block *sub_p3;
760 char file_item_buf[DST_ITEM_MAX];
761 char *file_item = file_item_buf;
765 zint no_items_first_half = 0;
772 close_block(b, sub_p2);
774 half = src + b->file[p->cat].head.block_size/2;
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);
785 file_item = file_item_buf;
786 (*b->method->codec.reset)(c1);
787 (*b->method->codec.decode)(c1, &file_item, &src);
789 decode_item_len(&src, &split_size_tmp);
790 *split_size = (int) split_size_tmp;
793 decode_ptr(&src, &pos);
795 /* read sub block so we can get no_items for it */
796 sub_p3 = open_block(b, pos);
797 no_items_first_half += sub_p3->no_items;
798 close_block(b, sub_p3);
800 /* p is first half */
801 p_new_size = src - dst_buf;
802 memcpy (p->bytes, dst_buf, p_new_size);
805 file_item = file_item_buf;
806 (*b->method->codec.reset)(c1);
807 (*b->method->codec.decode)(c1, &file_item, &src);
808 *split_size = file_item - file_item_buf;
809 memcpy(split_item, file_item_buf, *split_size);
811 decode_item_len(&src, &split_size_tmp);
812 *split_size = (int) split_size_tmp;
813 memcpy (split_item, src, *split_size);
816 /* *sp is second half */
817 *sp = new_int (b, p->cat);
818 (*sp)->size = endp - src;
819 memcpy ((*sp)->bytes, src, (*sp)->size);
821 p->size = p_new_size;
823 /* adjust no_items in first&second half */
824 (*sp)->no_items = p->no_items - no_items_first_half;
825 p->no_items = no_items_first_half;
829 close_block(b, sub_p1);
830 (*b->method->codec.stop)(c1);
834 int insert_leaf (ISAMB b, struct ISAMB_block **sp1, void *lookahead_item,
835 int *lookahead_mode, ISAMC_I *stream,
836 struct ISAMB_block **sp2,
837 void *sub_item, int *sub_size,
838 const void *max_item)
840 struct ISAMB_block *p = *sp1;
843 char dst_buf[DST_BUF_SIZE], *dst = dst_buf;
845 void *c1 = (*b->method->codec.start)();
846 void *c2 = (*b->method->codec.start)();
848 int quater = b->file[b->no_cat-1].head.block_max / 4;
849 char *mid_cut = dst_buf + quater * 2;
850 char *tail_cut = dst_buf + quater * 3;
851 char *maxp = dst_buf + b->file[b->no_cat-1].head.block_max;
854 char cut_item_buf[DST_ITEM_MAX];
855 int cut_item_size = 0;
856 int no_items = 0; /* number of items (total) */
857 int no_items_1 = 0; /* number of items (first half) */
858 int inserted_dst_bytes = 0;
862 char file_item_buf[DST_ITEM_MAX];
863 char *file_item = file_item_buf;
866 endp = p->bytes + p->size;
867 (*b->method->codec.decode)(c1, &file_item, &src);
870 const char *dst_item = 0; /* resulting item to be inserted */
871 char *lookahead_next;
876 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
878 /* d now holds comparison between existing file item and
881 d > 0: lookahead before file
882 d < 0: lookahead after file
886 /* lookahead must be inserted */
887 dst_item = lookahead_item;
888 /* if this is not an insertion, it's really bad .. */
889 if (!*lookahead_mode)
891 yaz_log(YLOG_WARN, "isamb: Inconsistent register (1)");
892 assert (*lookahead_mode);
896 dst_item = file_item_buf;
898 if (!*lookahead_mode && d == 0)
900 /* it's a deletion and they match so there is nothing to be
901 inserted anyway .. But mark the thing bad (file item
902 was part of input.. The item will not be part of output */
905 else if (!half1 && dst > mid_cut)
907 /* we have reached the splitting point for the first time */
908 const char *dst_item_0 = dst_item;
909 half1 = dst; /* candidate for splitting */
911 /* encode the resulting item */
912 (*b->method->codec.encode)(c2, &dst, &dst_item);
914 cut_item_size = dst_item - dst_item_0;
915 assert(cut_item_size > 0);
916 memcpy (cut_item_buf, dst_item_0, cut_item_size);
919 no_items_1 = no_items;
924 /* encode the resulting item */
925 (*b->method->codec.encode)(c2, &dst, &dst_item);
929 /* now move "pointers" .. result has been encoded .. */
932 /* we must move the lookahead pointer */
934 inserted_dst_bytes += (dst - dst_0);
935 if (inserted_dst_bytes >= quater)
936 /* no more room. Mark lookahead as "gone".. */
940 /* move it really.. */
941 lookahead_next = lookahead_item;
942 if (!(*stream->read_item)(stream->clientData,
946 /* end of stream reached: no "more" and no lookahead */
950 if (lookahead_item && max_item &&
951 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
953 /* the lookahead goes beyond what we allow in this
954 leaf. Mark it as "gone" */
963 /* exact match .. move both pointers */
965 lookahead_next = lookahead_item;
966 if (!(*stream->read_item)(stream->clientData,
967 &lookahead_next, lookahead_mode))
973 break; /* end of file stream reached .. */
974 file_item = file_item_buf; /* move file pointer */
975 (*b->method->codec.decode)(c1, &file_item, &src);
979 /* file pointer must be moved */
982 file_item = file_item_buf;
983 (*b->method->codec.decode)(c1, &file_item, &src);
988 /* this loop runs when we are "appending" to a leaf page. That is
989 either it's empty (new) or all file items have been read in
992 maxp = dst_buf + b->file[b->no_cat-1].head.block_max + quater;
993 while (lookahead_item)
996 const char *src = lookahead_item;
999 /* if we have a lookahead item, we stop if we exceed the value of it */
1001 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
1003 /* stop if we have reached the value of max item */
1006 if (!*lookahead_mode)
1008 /* this is append. So a delete is bad */
1009 yaz_log(YLOG_WARN, "isamb: Inconsistent register (2)");
1012 else if (!half1 && dst > tail_cut)
1014 const char *src_0 = src;
1015 half1 = dst; /* candidate for splitting */
1017 (*b->method->codec.encode)(c2, &dst, &src);
1019 cut_item_size = src - src_0;
1020 assert(cut_item_size > 0);
1021 memcpy (cut_item_buf, src_0, cut_item_size);
1023 no_items_1 = no_items;
1027 (*b->method->codec.encode)(c2, &dst, &src);
1037 dst_item = lookahead_item;
1038 if (!(*stream->read_item)(stream->clientData, &dst_item,
1045 new_size = dst - dst_buf;
1046 if (p && p->cat != b->no_cat-1 &&
1047 new_size > b->file[p->cat].head.block_max)
1049 /* non-btree block will be removed */
1052 /* delete it too!! */
1053 p = 0; /* make a new one anyway */
1056 { /* must create a new one */
1058 for (i = 0; i < b->no_cat; i++)
1059 if (new_size <= b->file[i].head.block_max)
1063 p = new_leaf (b, i);
1065 if (new_size > b->file[p->cat].head.block_max)
1068 const char *cut_item = cut_item_buf;
1073 assert(cut_item_size > 0);
1076 p->size = half1 - dst_buf;
1077 assert(p->size <= b->file[p->cat].head.block_max);
1078 memcpy (p->bytes, dst_buf, half1 - dst_buf);
1079 p->no_items = no_items_1;
1082 *sp2 = new_leaf (b, p->cat);
1084 (*b->method->codec.reset)(c2);
1086 first_dst = (*sp2)->bytes;
1088 (*b->method->codec.encode)(c2, &first_dst, &cut_item);
1090 memcpy (first_dst, half2, dst - half2);
1092 (*sp2)->size = (first_dst - (*sp2)->bytes) + (dst - half2);
1093 assert((*sp2)->size <= b->file[p->cat].head.block_max);
1094 (*sp2)->no_items = no_items - no_items_1;
1097 memcpy (sub_item, cut_item_buf, cut_item_size);
1098 *sub_size = cut_item_size;
1102 memcpy (p->bytes, dst_buf, dst - dst_buf);
1104 p->no_items = no_items;
1106 (*b->method->codec.stop)(c1);
1107 (*b->method->codec.stop)(c2);
1112 int insert_sub (ISAMB b, struct ISAMB_block **p, void *new_item,
1115 struct ISAMB_block **sp,
1116 void *sub_item, int *sub_size,
1117 const void *max_item)
1119 if (!*p || (*p)->leaf)
1120 return insert_leaf (b, p, new_item, mode, stream, sp, sub_item,
1121 sub_size, max_item);
1123 return insert_int (b, *p, new_item, mode, stream, sp, sub_item,
1124 sub_size, max_item);
1127 int isamb_unlink (ISAMB b, ISAM_P pos)
1129 struct ISAMB_block *p1;
1133 p1 = open_block(b, pos);
1138 const char *src = p1->bytes + p1->offset;
1140 void *c1 = (*b->method->codec.start)();
1142 decode_ptr(&src, &sub_p);
1143 isamb_unlink(b, sub_p);
1145 while (src != p1->bytes + p1->size)
1148 char file_item_buf[DST_ITEM_MAX];
1149 char *file_item = file_item_buf;
1150 (*b->method->codec.reset)(c1);
1151 (*b->method->codec.decode)(c1, &file_item, &src);
1154 decode_item_len(&src, &item_len);
1157 decode_ptr(&src, &sub_p);
1158 isamb_unlink(b, sub_p);
1161 (*b->method->codec.stop)(c1);
1168 void isamb_merge(ISAMB b, ISAM_P *pos, ISAMC_I *stream)
1170 char item_buf[DST_ITEM_MAX];
1174 int must_delete = 0;
1181 item_ptr = item_buf;
1183 (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1188 item_ptr = item_buf;
1189 more = (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1192 struct ISAMB_block *p = 0, *sp = 0;
1193 char sub_item[DST_ITEM_MAX];
1197 p = open_block(b, *pos);
1198 more = insert_sub (b, &p, item_buf, &i_mode, stream, &sp,
1199 sub_item, &sub_size, 0);
1201 { /* increase level of tree by one */
1202 struct ISAMB_block *p2 = new_int (b, p->cat);
1203 char *dst = p2->bytes + p2->size;
1205 void *c1 = (*b->method->codec.start)();
1206 const char *sub_item_ptr = sub_item;
1209 encode_ptr(&dst, p->pos);
1210 assert (sub_size < 80 && sub_size > 1);
1212 (*b->method->codec.reset)(c1);
1213 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
1215 encode_item_len (&dst, sub_size);
1216 memcpy (dst, sub_item, sub_size);
1219 encode_ptr(&dst, sp->pos);
1221 p2->size = dst - p2->bytes;
1222 p2->no_items = p->no_items + sp->no_items;
1223 *pos = p2->pos; /* return new super page */
1227 (*b->method->codec.stop)(c1);
1232 *pos = p->pos; /* return current one (again) */
1234 if (p->no_items == 0)
1242 isamb_unlink(b, *pos);
1247 ISAMB_PP isamb_pp_open_x(ISAMB isamb, ISAM_P pos, int *level, int scope)
1249 ISAMB_PP pp = xmalloc(sizeof(*pp));
1255 pp->block = xmalloc(ISAMB_MAX_LEVEL * sizeof(*pp->block));
1262 pp->skipped_numbers = 0;
1263 pp->returned_numbers = 0;
1265 for (i = 0; i<ISAMB_MAX_LEVEL; i++)
1266 pp->skipped_nodes[i] = pp->accessed_nodes[i] = 0;
1269 struct ISAMB_block *p = open_block(isamb, pos);
1270 const char *src = p->bytes + p->offset;
1271 pp->block[pp->level] = p;
1273 pp->total_size += p->size;
1277 decode_ptr(&src, &pos);
1278 p->offset = src - p->bytes;
1280 pp->accessed_nodes[pp->level]++;
1282 pp->block[pp->level+1] = 0;
1283 pp->maxlevel = pp->level;
1289 ISAMB_PP isamb_pp_open (ISAMB isamb, ISAM_P pos, int scope)
1291 return isamb_pp_open_x(isamb, pos, 0, scope);
1294 void isamb_pp_close_x(ISAMB_PP pp, zint *size, zint *blocks)
1299 yaz_log(YLOG_DEBUG, "isamb_pp_close lev=%d returned "ZINT_FORMAT" values, "
1300 "skipped "ZINT_FORMAT,
1301 pp->maxlevel, pp->skipped_numbers, pp->returned_numbers);
1302 for (i = pp->maxlevel; i>=0; i--)
1303 if (pp->skipped_nodes[i] || pp->accessed_nodes[i])
1304 yaz_log(YLOG_DEBUG, "isamb_pp_close level leaf-%d: "
1305 ZINT_FORMAT" read, "ZINT_FORMAT" skipped", i,
1306 pp->accessed_nodes[i], pp->skipped_nodes[i]);
1307 pp->isamb->skipped_numbers += pp->skipped_numbers;
1308 pp->isamb->returned_numbers += pp->returned_numbers;
1309 for (i = pp->maxlevel; i>=0; i--)
1311 pp->isamb->accessed_nodes[i] += pp->accessed_nodes[i];
1312 pp->isamb->skipped_nodes[i] += pp->skipped_nodes[i];
1315 *size = pp->total_size;
1317 *blocks = pp->no_blocks;
1318 for (i = 0; i <= pp->level; i++)
1319 close_block(pp->isamb, pp->block[i]);
1324 int isamb_block_info (ISAMB isamb, int cat)
1326 if (cat >= 0 && cat < isamb->no_cat)
1327 return isamb->file[cat].head.block_size;
1331 void isamb_pp_close (ISAMB_PP pp)
1333 isamb_pp_close_x(pp, 0, 0);
1336 /* simple recursive dumper .. */
1337 static void isamb_dump_r (ISAMB b, ISAM_P pos, void (*pr)(const char *str),
1341 char prefix_str[1024];
1344 struct ISAMB_block *p = open_block(b, pos);
1345 sprintf(prefix_str, "%*s " ZINT_FORMAT " cat=%d size=%d max=%d items="
1346 ZINT_FORMAT, level*2, "",
1347 pos, p->cat, p->size, b->file[p->cat].head.block_max,
1350 sprintf(prefix_str, "%*s " ZINT_FORMAT, level*2, "", pos);
1353 while (p->offset < p->size)
1355 const char *src = p->bytes + p->offset;
1357 (*b->method->codec.decode)(p->decodeClientData, &dst, &src);
1358 (*b->method->log_item)(YLOG_DEBUG, buf, prefix_str);
1359 p->offset = src - (char*) p->bytes;
1361 assert(p->offset == p->size);
1365 const char *src = p->bytes + p->offset;
1368 decode_ptr(&src, &sub);
1369 p->offset = src - (char*) p->bytes;
1371 isamb_dump_r(b, sub, pr, level+1);
1373 while (p->offset < p->size)
1376 char file_item_buf[DST_ITEM_MAX];
1377 char *file_item = file_item_buf;
1378 void *c1 = (*b->method->codec.start)();
1379 (*b->method->codec.decode)(c1, &file_item, &src);
1380 (*b->method->codec.stop)(c1);
1381 (*b->method->log_item)(YLOG_DEBUG, file_item_buf, prefix_str);
1384 decode_item_len(&src, &item_len);
1385 (*b->method->log_item)(YLOG_DEBUG, src, prefix_str);
1388 decode_ptr(&src, &sub);
1390 p->offset = src - (char*) p->bytes;
1392 isamb_dump_r(b, sub, pr, level+1);
1399 void isamb_dump(ISAMB b, ISAM_P pos, void (*pr)(const char *str))
1401 isamb_dump_r(b, pos, pr, 0);
1404 int isamb_pp_read(ISAMB_PP pp, void *buf)
1406 return isamb_pp_forward(pp, buf, 0);
1410 static int isamb_pp_on_right_node(ISAMB_PP pp, int level, const void *untilbuf)
1411 { /* looks one node higher to see if we should be on this node at all */
1412 /* useful in backing off quickly, and in avoiding tail descends */
1413 /* call with pp->level to begin with */
1414 struct ISAMB_block *p;
1417 ISAMB b = pp->isamb;
1423 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true for root");
1425 return 1; /* we can never skip the root node */
1428 p = pp->block[level];
1429 assert(p->offset <= p->size);
1430 if (p->offset < p->size)
1433 char file_item_buf[DST_ITEM_MAX];
1434 char *file_item = file_item_buf;
1435 void *c1 = (*b->method->codec.start)();
1436 assert(p->offset > 0);
1437 src = p->bytes + p->offset;
1438 (*b->method->codec.decode)(c1, &file_item, &src);
1439 (*b->method->codec.stop)(c1);
1440 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1443 assert(p->offset > 0);
1444 src = p->bytes + p->offset;
1445 decode_item_len(&src, &item_len);
1447 (*b->method->codec.log_item)(YLOG_DEBUG, untilbuf, "on_leaf: until");
1448 (*b->method->codec.log_item)(YLOG_DEBUG, src, "on_leaf: value");
1450 cmp = (*b->method->compare_item)(untilbuf, src);
1452 if (cmp < pp->scope)
1455 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true "
1456 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1463 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning false "
1464 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1471 yaz_log(YLOG_DEBUG, "isamb_pp_on_right at tail, looking higher "
1474 return isamb_pp_on_right_node(pp, level, untilbuf);
1476 } /* isamb_pp_on_right_node */
1478 static int isamb_pp_read_on_leaf(ISAMB_PP pp, void *buf)
1480 /* reads the next item on the current leaf, returns 0 if end of leaf*/
1481 struct ISAMB_block *p = pp->block[pp->level];
1486 if (p->offset == p->size)
1489 yaz_log(YLOG_DEBUG, "isamb_pp_read_on_leaf returning 0 on "
1492 return 0; /* at end of leaf */
1494 src = p->bytes + p->offset;
1496 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1497 p->offset = src - (char*) p->bytes;
1499 (*pp->isamb->method->codec.log_item)(YLOG_DEBUG, buf,
1500 "read_on_leaf returning 1");
1502 pp->returned_numbers++;
1504 } /* read_on_leaf */
1506 static int isamb_pp_forward_on_leaf(ISAMB_PP pp, void *buf, const void *untilbuf)
1507 { /* forwards on the current leaf, returns 0 if not found */
1512 if (!isamb_pp_read_on_leaf(pp, buf))
1514 /* FIXME - this is an extra function call, inline the read? */
1515 cmp=(*pp->isamb->method->compare_item)(untilbuf, buf);
1517 { /* cmp<2 found a good one */
1520 yaz_log(YLOG_DEBUG, "isam_pp_fwd_on_leaf skipped %d items", skips);
1522 pp->returned_numbers++;
1526 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1527 return 0; /* never mind the rest of this leaf */
1528 pp->skipped_numbers++;
1531 } /* forward_on_leaf */
1533 static int isamb_pp_climb_level(ISAMB_PP pp, ISAM_P *pos)
1534 { /* climbs higher in the tree, until finds a level with data left */
1535 /* returns the node to (consider to) descend to in *pos) */
1536 struct ISAMB_block *p = pp->block[pp->level];
1539 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level starting "
1540 "at level %d node %d ofs=%d sz=%d",
1541 pp->level, p->pos, p->offset, p->size);
1543 assert(pp->level >= 0);
1544 assert(p->offset <= p->size);
1548 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level returning 0 at root");
1552 assert(pp->level>0);
1553 close_block(pp->isamb, pp->block[pp->level]);
1554 pp->block[pp->level] = 0;
1556 p = pp->block[pp->level];
1558 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level climbed to level %d node %d ofs=%d",
1559 pp->level, p->pos, p->offset);
1562 assert(p->offset <= p->size);
1563 if (p->offset == p->size)
1565 /* we came from the last pointer, climb on */
1566 if (!isamb_pp_climb_level(pp, pos))
1568 p = pp->block[pp->level];
1573 char file_item_buf[DST_ITEM_MAX];
1574 char *file_item = file_item_buf;
1575 ISAMB b = pp->isamb;
1576 void *c1 = (*b->method->codec.start)();
1580 /* skip the child we just came from */
1582 yaz_log(YLOG_DEBUG, "isam_pp_climb_level: skipping lev=%d ofs=%d sz=%d",
1583 pp->level, p->offset, p->size);
1585 assert (p->offset < p->size);
1586 src = p->bytes + p->offset;
1588 (*b->method->codec.decode)(c1, &file_item, &src);
1589 (*b->method->codec.stop)(c1);
1591 decode_item_len(&src, &item_len);
1594 decode_ptr(&src, pos);
1595 p->offset = src - (char *)p->bytes;
1602 static zint isamb_pp_forward_unode(ISAMB_PP pp, zint pos, const void *untilbuf)
1603 { /* scans a upper node until it finds a child <= untilbuf */
1604 /* pp points to the key value, as always. pos is the child read from */
1606 /* if all values are too small, returns the last child in the node */
1607 /* FIXME - this can be detected, and avoided by looking at the */
1608 /* parent node, but that gets messy. Presumably the cost is */
1609 /* pretty low anyway */
1610 ISAMB b = pp->isamb;
1611 struct ISAMB_block *p = pp->block[pp->level];
1612 const char *src = p->bytes + p->offset;
1617 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode starting "
1618 "at level %d node %d ofs=%di sz=%d",
1619 pp->level, p->pos, p->offset, p->size);
1622 assert(p->offset <= p->size);
1623 if (p->offset == p->size)
1626 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at end "
1627 "at level %d node %d ofs=%di sz=%d",
1628 pp->level, p->pos, p->offset, p->size);
1630 return pos; /* already at the end of it */
1632 while(p->offset < p->size)
1635 char file_item_buf[DST_ITEM_MAX];
1636 char *file_item = file_item_buf;
1637 void *c1 = (*b->method->codec.start)();
1638 (*b->method->codec.decode)(c1, &file_item, &src);
1639 (*b->method->codec.stop)(c1);
1640 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1643 decode_item_len(&src, &item_len);
1644 cmp = (*b->method->compare_item)(untilbuf, src);
1647 decode_ptr(&src, &nxtpos);
1648 if (cmp<pp->scope) /* cmp<2 */
1651 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning a hit "
1652 "at level %d node %d ofs=%d sz=%d",
1653 pp->level, p->pos, p->offset, p->size);
1658 p->offset = src-(char*)p->bytes;
1659 (pp->skipped_nodes[pp->maxlevel - pp->level -1])++;
1665 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at tail "
1666 "at level %d node %d ofs=%d sz=%d skips=%d",
1667 pp->level, p->pos, p->offset, p->size, skips);
1669 return pos; /* that's the last one in the line */
1671 } /* forward_unode */
1673 static void isamb_pp_descend_to_leaf(ISAMB_PP pp, ISAM_P pos,
1674 const void *untilbuf)
1675 { /* climbs down the tree, from pos, to the leftmost leaf */
1676 struct ISAMB_block *p = pp->block[pp->level];
1680 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1681 "starting at lev %d node %d ofs=%d lf=%d u=%p",
1682 pp->level, p->pos, p->offset, p->leaf, untilbuf);
1685 pos = isamb_pp_forward_unode(pp, pos, untilbuf);
1688 p = open_block(pp->isamb, pos);
1689 pp->block[pp->level] = p;
1690 ++(pp->accessed_nodes[pp->maxlevel-pp->level]);
1693 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1694 "got lev %d node %d lf=%d",
1695 pp->level, p->pos, p->leaf);
1699 assert (p->offset==0);
1700 src = p->bytes + p->offset;
1701 decode_ptr(&src, &pos);
1702 p->offset = src-(char*)p->bytes;
1703 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1705 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1706 "returning at lev %d node %d ofs=%d lf=%d",
1707 pp->level, p->pos, p->offset, p->leaf);
1709 } /* descend_to_leaf */
1711 static int isamb_pp_find_next_leaf(ISAMB_PP pp)
1712 { /* finds the next leaf by climbing up and down */
1714 if (!isamb_pp_climb_level(pp, &pos))
1716 isamb_pp_descend_to_leaf(pp, pos, 0);
1720 static int isamb_pp_climb_desc(ISAMB_PP pp, const void *untilbuf)
1721 { /* climbs up and descends to a leaf where values >= *untilbuf are found */
1724 struct ISAMB_block *p = pp->block[pp->level];
1725 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc starting "
1726 "at level %d node %d ofs=%d sz=%d",
1727 pp->level, p->pos, p->offset, p->size);
1729 if (!isamb_pp_climb_level(pp, &pos))
1731 /* see if it would pay to climb one higher */
1732 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1733 if (!isamb_pp_climb_level(pp, &pos))
1735 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1737 p = pp->block[pp->level];
1738 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc done "
1739 "at level %d node %d ofs=%d sz=%d",
1740 pp->level, p->pos, p->offset, p->size);
1745 int isamb_pp_forward (ISAMB_PP pp, void *buf, const void *untilbuf)
1748 struct ISAMB_block *p = pp->block[pp->level];
1750 yaz_log(YLOG_DEBUG, "isamb_pp_forward starting "
1751 "at level %d node %d ofs=%d sz=%d u=%p sc=%d",
1752 pp->level, p->pos, p->offset, p->size, untilbuf, scope);
1756 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1759 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (A) "
1760 "at level %d node %d ofs=%d sz=%d",
1761 pp->level, p->pos, p->offset, p->size);
1765 if (! isamb_pp_climb_desc(pp, untilbuf))
1768 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning notfound (B) "
1769 "at level %d node %d ofs=%d sz=%d",
1770 pp->level, p->pos, p->offset, p->size);
1772 return 0; /* could not find a leaf */
1775 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1778 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (c) "
1779 "at level %d node %d ofs=%d sz=%d",
1780 pp->level, p->pos, p->offset, p->size);
1784 } while (isamb_pp_find_next_leaf(pp));
1785 return 0; /* could not find at all */
1787 else { /* no untilbuf, a straight read */
1788 /* FIXME - this should be moved
1789 * directly into the pp_read */
1790 /* keeping here now, to keep same
1791 * interface as the old fwd */
1792 if (isamb_pp_read_on_leaf(pp, buf))
1795 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (D) "
1796 "at level %d node %d ofs=%d sz=%d",
1797 pp->level, p->pos, p->offset, p->size);
1801 if (isamb_pp_find_next_leaf(pp))
1804 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (E) "
1805 "at level %d node %d ofs=%d sz=%d",
1806 pp->level, p->pos, p->offset, p->size);
1808 return isamb_pp_read_on_leaf(pp, buf);
1813 } /* isam_pp_forward (new version) */
1815 void isamb_pp_pos(ISAMB_PP pp, double *current, double *total)
1816 { /* return an estimate of the current position and of the total number of */
1817 /* occureences in the isam tree, based on the current leaf */
1821 /* if end-of-stream PP may not be leaf */
1823 *total = (double) (pp->block[0]->no_items);
1824 *current = (double) pp->returned_numbers;
1826 yaz_log(YLOG_LOG, "isamb_pp_pos returning: cur= %0.1f tot=%0.1f rn="
1827 ZINT_FORMAT, *current, *total, pp->returned_numbers);
1831 int isamb_pp_forward2(ISAMB_PP pp, void *buf, const void *untilb)
1835 struct ISAMB_block *p = pp->block[pp->level];
1836 ISAMB b = pp->isamb;
1840 while (p->offset == p->size)
1846 char file_item_buf[DST_ITEM_MAX];
1847 char *file_item = file_item_buf;
1851 while (p->offset == p->size)
1855 close_block (pp->isamb, pp->block[pp->level]);
1856 pp->block[pp->level] = 0;
1858 p = pp->block[pp->level];
1863 src = p->bytes + p->offset;
1866 c1 = (*b->method->codec.start)();
1867 (*b->method->codec.decode)(c1, &file_item, &src);
1869 decode_ptr (&src, &item_len);
1872 decode_ptr (&src, &pos);
1873 p->offset = src - (char*) p->bytes;
1875 src = p->bytes + p->offset;
1879 if (!untilb || p->offset == p->size)
1881 assert(p->offset < p->size);
1884 file_item = file_item_buf;
1885 (*b->method->codec.reset)(c1);
1886 (*b->method->codec.decode)(c1, &file_item, &src);
1887 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1893 decode_item_len(&src, &item_len);
1894 if ((*b->method->compare_item)(untilb, src) <= 1)
1898 decode_ptr (&src, &pos);
1899 p->offset = src - (char*) p->bytes;
1906 pp->block[pp->level] = p = open_block (pp->isamb, pos);
1908 pp->total_size += p->size;
1916 src = p->bytes + p->offset;
1919 decode_ptr (&src, &pos);
1920 p->offset = src - (char*) p->bytes;
1922 if (!untilb || p->offset == p->size)
1924 assert(p->offset < p->size);
1927 file_item = file_item_buf;
1928 (*b->method->codec.reset)(c1);
1929 (*b->method->codec.decode)(c1, &file_item, &src);
1930 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1936 decode_ptr (&src, &item_len);
1937 if ((*b->method->compare_item)(untilb, src) <= 1)
1945 (*b->method->codec.stop)(c1);
1948 assert (p->offset < p->size);
1953 src = p->bytes + p->offset;
1954 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1955 p->offset = src - (char*) p->bytes;
1956 if (!untilb || (*pp->isamb->method->compare_item)(untilb, dst0) <= 1)
1959 if (p->offset == p->size) goto again;
1966 * indent-tabs-mode: nil
1968 * vim: shiftwidth=4 tabstop=8 expandtab