1 /* $Id: isamb.c,v 1.87 2006-12-07 19:23:56 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 (2*4096+300)
58 /* should be maximum block size of multiple thereof */
59 #define ISAMB_CACHE_ENTRY_SIZE 4096
61 /* CAT_MAX: _must_ be power of 2 */
63 #define CAT_MASK (CAT_MAX-1)
64 /* CAT_NO: <= CAT_MAX */
67 /* Smallest block size */
68 #define ISAMB_MIN_SIZE 32
70 #define ISAMB_FAC_SIZE 4
72 /* ISAMB_PTR_CODEC = 1 var, =0 fixed */
73 #define ISAMB_PTR_CODEC 1
75 struct ISAMB_cache_entry {
80 struct ISAMB_cache_entry *next;
86 struct ISAMB_head head;
87 struct ISAMB_cache_entry *cache_entries;
94 struct ISAMB_file *file;
96 int cache; /* 0 = no cache, 1 = use cache, -1 = dummy isam (for testing only) */
97 int log_io; /* log level for bf_read/bf_write calls */
98 int log_freelist; /* log level for freelist handling */
99 zint skipped_numbers; /* on a leaf node */
100 zint returned_numbers;
101 zint skipped_nodes[ISAMB_MAX_LEVEL]; /* [0]=skipped leaves, 1 = higher etc */
102 zint accessed_nodes[ISAMB_MAX_LEVEL]; /* nodes we did not skip */
113 zint no_items; /* number of nodes in this + children */
117 void *decodeClientData;
125 int maxlevel; /* total depth */
128 zint skipped_numbers; /* on a leaf node */
129 zint returned_numbers;
130 zint skipped_nodes[ISAMB_MAX_LEVEL]; /* [0]=skipped leaves, 1 = higher etc */
131 zint accessed_nodes[ISAMB_MAX_LEVEL]; /* nodes we did not skip */
132 struct ISAMB_block **block;
133 int scope; /* on what level we forward */
137 #define encode_item_len encode_ptr
139 static void encode_ptr(char **dst, zint pos)
141 unsigned char *bp = (unsigned char*) *dst;
145 *bp++ = (unsigned char) (128 | (pos & 127));
148 *bp++ = (unsigned char) pos;
152 static void encode_ptr(char **dst, zint pos)
154 memcpy(*dst, &pos, sizeof(pos));
155 (*dst) += sizeof(pos);
159 #define decode_item_len decode_ptr
161 static void decode_ptr(const char **src, zint *pos)
167 while (((c = *(const unsigned char *)((*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;
203 yaz_log(YLOG_WARN, "isamb_open %s. Degraded TEST mode", name);
207 assert(cache == 0 || cache == 1);
209 isamb->file = xmalloc(sizeof(*isamb->file) * isamb->no_cat);
211 for (i = 0; i < isamb->no_cat; i++)
213 isamb->file[i].bf = 0;
214 isamb->file[i].head_dirty = 0;
215 isamb->file[i].cache_entries = 0;
218 for (i = 0; i < isamb->no_cat; i++)
220 char fname[DST_BUF_SIZE];
221 char hbuf[DST_BUF_SIZE];
223 sprintf(fname, "%s%c", name, i+'A');
225 isamb->file[i].bf = bf_open(bfs, fname, ISAMB_CACHE_ENTRY_SIZE,
228 isamb->file[i].bf = bf_open(bfs, fname, b_size, writeflag);
230 if (!isamb->file[i].bf)
236 /* fill-in default values (for empty isamb) */
237 isamb->file[i].head.first_block = ISAMB_CACHE_ENTRY_SIZE/b_size+1;
238 isamb->file[i].head.last_block = isamb->file[i].head.first_block;
239 isamb->file[i].head.block_size = b_size;
240 assert(b_size <= ISAMB_CACHE_ENTRY_SIZE);
242 if (i == isamb->no_cat-1 || b_size > 128)
243 isamb->file[i].head.block_offset = 8;
245 isamb->file[i].head.block_offset = 4;
247 isamb->file[i].head.block_offset = 11;
249 isamb->file[i].head.block_max =
250 b_size - isamb->file[i].head.block_offset;
251 isamb->file[i].head.free_list = 0;
252 if (bf_read(isamb->file[i].bf, 0, 0, 0, hbuf))
254 /* got header assume "isamb"major minor len can fit in 16 bytes */
256 int major, minor, len, pos = 0;
259 if (memcmp(hbuf, "isamb", 5))
261 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
264 if (sscanf(hbuf+5, "%d %d %d", &major, &minor, &len) != 3)
266 yaz_log(YLOG_WARN, "bad isamb header for file %s", fname);
269 if (major != ISAMB_MAJOR_VERSION)
271 yaz_log(YLOG_WARN, "bad major version for file %s %d, must be %d",
272 fname, major, ISAMB_MAJOR_VERSION);
275 for (left = len - b_size; left > 0; left = left - b_size)
278 if (!bf_read(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size))
280 yaz_log(YLOG_WARN, "truncated isamb header for "
281 "file=%s len=%d pos=%d",
287 decode_ptr(&src, &isamb->file[i].head.first_block);
288 decode_ptr(&src, &isamb->file[i].head.last_block);
289 decode_ptr(&src, &zint_tmp);
290 isamb->file[i].head.block_size = (int) zint_tmp;
291 decode_ptr(&src, &zint_tmp);
292 isamb->file[i].head.block_max = (int) zint_tmp;
293 decode_ptr(&src, &isamb->file[i].head.free_list);
295 assert (isamb->file[i].head.block_size >= isamb->file[i].head.block_offset);
296 isamb->file[i].head_dirty = 0;
297 assert(isamb->file[i].head.block_size == b_size);
298 b_size = b_size * ISAMB_FAC_SIZE;
301 yaz_log(YLOG_WARN, "isamb debug enabled. Things will be slower than usual");
306 static void flush_blocks (ISAMB b, int cat)
308 while (b->file[cat].cache_entries)
310 struct ISAMB_cache_entry *ce_this = b->file[cat].cache_entries;
311 b->file[cat].cache_entries = ce_this->next;
315 yaz_log(b->log_io, "bf_write: flush_blocks");
316 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
323 static int cache_block (ISAMB b, ISAM_P pos, unsigned char *userbuf, int wr)
325 int cat = (int) (pos&CAT_MASK);
326 int off = (int) (((pos/CAT_MAX) &
327 (ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size - 1))
328 * b->file[cat].head.block_size);
329 zint norm = pos / (CAT_MASK*ISAMB_CACHE_ENTRY_SIZE / b->file[cat].head.block_size);
331 struct ISAMB_cache_entry **ce, *ce_this = 0, **ce_last = 0;
336 assert (ISAMB_CACHE_ENTRY_SIZE >= b->file[cat].head.block_size);
337 for (ce = &b->file[cat].cache_entries; *ce; ce = &(*ce)->next, no++)
340 if ((*ce)->pos == norm)
343 *ce = (*ce)->next; /* remove from list */
345 ce_this->next = b->file[cat].cache_entries; /* move to front */
346 b->file[cat].cache_entries = ce_this;
350 memcpy (ce_this->buf + off, userbuf,
351 b->file[cat].head.block_size);
355 memcpy (userbuf, ce_this->buf + off,
356 b->file[cat].head.block_size);
363 assert (ce_last && *ce_last);
365 *ce_last = 0; /* remove the last entry from list */
368 yaz_log(b->log_io, "bf_write: cache_block");
369 bf_write(b->file[cat].bf, ce_this->pos, 0, 0, ce_this->buf);
374 ce_this = xmalloc(sizeof(*ce_this));
375 ce_this->next = b->file[cat].cache_entries;
376 b->file[cat].cache_entries = ce_this;
377 ce_this->buf = xmalloc(ISAMB_CACHE_ENTRY_SIZE);
379 yaz_log(b->log_io, "bf_read: cache_block");
380 if (!bf_read(b->file[cat].bf, norm, 0, 0, ce_this->buf))
381 memset (ce_this->buf, 0, ISAMB_CACHE_ENTRY_SIZE);
384 memcpy (ce_this->buf + off, userbuf, b->file[cat].head.block_size);
390 memcpy (userbuf, ce_this->buf + off, b->file[cat].head.block_size);
396 void isamb_close (ISAMB isamb)
399 for (i = 0; isamb->accessed_nodes[i]; i++)
400 yaz_log(YLOG_DEBUG, "isamb_close level leaf-%d: "ZINT_FORMAT" read, "
401 ZINT_FORMAT" skipped",
402 i, isamb->accessed_nodes[i], isamb->skipped_nodes[i]);
403 yaz_log(YLOG_DEBUG, "isamb_close returned "ZINT_FORMAT" values, "
404 "skipped "ZINT_FORMAT,
405 isamb->skipped_numbers, isamb->returned_numbers);
406 for (i = 0; i<isamb->no_cat; i++)
408 flush_blocks (isamb, i);
409 if (isamb->file[i].head_dirty)
411 char hbuf[DST_BUF_SIZE];
412 int major = ISAMB_MAJOR_VERSION;
413 int minor = ISAMB_MINOR_VERSION;
415 char *dst = hbuf + 16;
417 int b_size = isamb->file[i].head.block_size;
419 encode_ptr(&dst, isamb->file[i].head.first_block);
420 encode_ptr(&dst, isamb->file[i].head.last_block);
421 encode_ptr(&dst, isamb->file[i].head.block_size);
422 encode_ptr(&dst, isamb->file[i].head.block_max);
423 encode_ptr(&dst, isamb->file[i].head.free_list);
424 memset(dst, '\0', b_size); /* ensure no random bytes are written */
428 /* print exactly 16 bytes (including trailing 0) */
429 sprintf(hbuf, "isamb%02d %02d %02d\r\n", major, minor, len);
431 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf);
433 for (left = len - b_size; left > 0; left = left - b_size)
436 bf_write(isamb->file[i].bf, pos, 0, 0, hbuf + pos*b_size);
439 if (isamb->file[i].bf)
440 bf_close (isamb->file[i].bf);
443 xfree(isamb->method);
447 /* open_block: read one block at pos.
448 Decode leading sys bytes .. consisting of
450 0: leader byte, != 0 leaf, == 0, non-leaf
451 1-2: used size of block
452 3-7*: number of items and all children
454 * Reserve 5 bytes for large block sizes. 1 for small ones .. Number
455 of items. We can thus have at most 2^40 nodes.
457 static struct ISAMB_block *open_block(ISAMB b, ISAM_P pos)
459 int cat = (int) (pos&CAT_MASK);
461 int offset = b->file[cat].head.block_offset;
462 struct ISAMB_block *p;
465 p = xmalloc(sizeof(*p));
467 p->cat = (int) (pos & CAT_MASK);
468 p->buf = xmalloc(b->file[cat].head.block_size);
471 if (!cache_block (b, pos, p->buf, 0))
473 yaz_log(b->log_io, "bf_read: open_block");
474 if (bf_read(b->file[cat].bf, pos/CAT_MAX, 0, 0, p->buf) != 1)
476 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
477 (long) pos, (long) pos/CAT_MAX);
478 zebra_exit("isamb:open_block");
481 p->bytes = (char *)p->buf + offset;
483 p->size = (p->buf[1] + 256 * p->buf[2]) - offset;
486 yaz_log(YLOG_FATAL, "Bad block size %d in pos=" ZINT_FORMAT "\n",
489 assert (p->size >= 0);
490 src = (char*) p->buf + 3;
491 decode_ptr(&src, &p->no_items);
496 p->decodeClientData = (*b->method->codec.start)();
500 struct ISAMB_block *new_block (ISAMB b, int leaf, int cat)
502 struct ISAMB_block *p;
504 p = xmalloc(sizeof(*p));
505 p->buf = xmalloc(b->file[cat].head.block_size);
507 if (!b->file[cat].head.free_list)
510 block_no = b->file[cat].head.last_block++;
511 p->pos = block_no * CAT_MAX + cat;
515 p->pos = b->file[cat].head.free_list;
516 assert((p->pos & CAT_MASK) == cat);
517 if (!cache_block (b, p->pos, p->buf, 0))
519 yaz_log(b->log_io, "bf_read: new_block");
520 if (!bf_read(b->file[cat].bf, p->pos/CAT_MAX, 0, 0, p->buf))
522 yaz_log(YLOG_FATAL, "isamb: read fail for pos=%ld block=%ld",
523 (long) p->pos/CAT_MAX, (long) p->pos/CAT_MAX);
524 zebra_exit("isamb:new_block");
527 yaz_log(b->log_freelist, "got block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT, p->pos,
528 cat, p->pos/CAT_MAX);
529 memcpy (&b->file[cat].head.free_list, p->buf, sizeof(zint));
532 b->file[cat].head_dirty = 1;
533 memset (p->buf, 0, b->file[cat].head.block_size);
534 p->bytes = (char*)p->buf + b->file[cat].head.block_offset;
541 p->decodeClientData = (*b->method->codec.start)();
545 struct ISAMB_block *new_leaf (ISAMB b, int cat)
547 return new_block (b, 1, cat);
551 struct ISAMB_block *new_int (ISAMB b, int cat)
553 return new_block (b, 0, cat);
556 static void check_block (ISAMB b, struct ISAMB_block *p)
558 assert(b); /* mostly to make the compiler shut up about unused b */
566 char *startp = p->bytes;
567 const char *src = startp;
568 char *endp = p->bytes + p->size;
570 void *c1 = (*b->method->codec.start)();
572 decode_ptr(&src, &pos);
573 assert ((pos&CAT_MASK) == p->cat);
577 char file_item_buf[DST_ITEM_MAX];
578 char *file_item = file_item_buf;
579 (*b->method->codec.reset)(c1);
580 (*b->method->codec.decode)(c1, &file_item, &src);
583 decode_item_len(&src, &item_len);
584 assert (item_len > 0 && item_len < 80);
587 decode_ptr(&src, &pos);
588 if ((pos&CAT_MASK) != p->cat)
590 assert ((pos&CAT_MASK) == p->cat);
593 (*b->method->codec.stop)(c1);
597 void close_block(ISAMB b, struct ISAMB_block *p)
603 yaz_log(b->log_freelist, "release block " ZINT_FORMAT " from freelist %d:" ZINT_FORMAT,
604 p->pos, p->cat, p->pos/CAT_MAX);
605 memcpy (p->buf, &b->file[p->cat].head.free_list, sizeof(zint));
606 b->file[p->cat].head.free_list = p->pos;
607 if (!cache_block (b, p->pos, p->buf, 1))
609 yaz_log(b->log_io, "bf_write: close_block (deleted)");
610 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
615 int offset = b->file[p->cat].head.block_offset;
616 int size = p->size + offset;
617 char *dst = (char*)p->buf + 3;
618 assert (p->size >= 0);
620 /* memset becuase encode_ptr usually does not write all bytes */
621 memset(p->buf, 0, b->file[p->cat].head.block_offset);
623 p->buf[1] = size & 255;
624 p->buf[2] = size >> 8;
625 encode_ptr(&dst, p->no_items);
627 if (!cache_block (b, p->pos, p->buf, 1))
629 yaz_log(b->log_io, "bf_write: close_block");
630 bf_write(b->file[p->cat].bf, p->pos/CAT_MAX, 0, 0, p->buf);
633 (*b->method->codec.stop)(p->decodeClientData);
638 int insert_sub (ISAMB b, struct ISAMB_block **p,
639 void *new_item, int *mode,
641 struct ISAMB_block **sp,
642 void *sub_item, int *sub_size,
643 const void *max_item);
645 int insert_int (ISAMB b, struct ISAMB_block *p, void *lookahead_item,
647 ISAMC_I *stream, struct ISAMB_block **sp,
648 void *split_item, int *split_size, const void *last_max_item)
650 char *startp = p->bytes;
651 const char *src = startp;
652 char *endp = p->bytes + p->size;
654 struct ISAMB_block *sub_p1 = 0, *sub_p2 = 0;
655 char sub_item[DST_ITEM_MAX];
659 void *c1 = (*b->method->codec.start)();
663 assert(p->size >= 0);
664 decode_ptr(&src, &pos);
668 const char *src0 = src;
670 char file_item_buf[DST_ITEM_MAX];
671 char *file_item = file_item_buf;
672 (*b->method->codec.reset)(c1);
673 (*b->method->codec.decode)(c1, &file_item, &src);
674 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
677 sub_p1 = open_block(b, pos);
679 diff_terms -= sub_p1->no_items;
680 more = insert_sub (b, &sub_p1, lookahead_item, mode,
682 sub_item, &sub_size, file_item_buf);
683 diff_terms += sub_p1->no_items;
689 decode_item_len(&src, &item_len);
690 d = (*b->method->compare_item)(src, lookahead_item);
693 sub_p1 = open_block(b, pos);
695 diff_terms -= sub_p1->no_items;
696 more = insert_sub (b, &sub_p1, lookahead_item, mode,
698 sub_item, &sub_size, src);
699 diff_terms += sub_p1->no_items;
705 decode_ptr(&src, &pos);
709 /* we reached the end. So lookahead > last item */
710 sub_p1 = open_block(b, pos);
712 diff_terms -= sub_p1->no_items;
713 more = insert_sub (b, &sub_p1, lookahead_item, mode, stream, &sub_p2,
714 sub_item, &sub_size, last_max_item);
715 diff_terms += sub_p1->no_items;
718 diff_terms += sub_p2->no_items;
722 p->no_items += diff_terms;
726 /* there was a split - must insert pointer in this one */
727 char dst_buf[DST_BUF_SIZE];
730 const char *sub_item_ptr = sub_item;
732 assert (sub_size < 80 && sub_size > 1);
734 memcpy (dst, startp, src - startp);
739 (*b->method->codec.reset)(c1);
740 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
742 encode_item_len (&dst, sub_size); /* sub length and item */
743 memcpy (dst, sub_item, sub_size);
747 encode_ptr(&dst, sub_p2->pos); /* pos */
749 if (endp - src) /* remaining data */
751 memcpy (dst, src, endp - src);
754 p->size = dst - dst_buf;
755 assert (p->size >= 0);
757 if (p->size <= b->file[p->cat].head.block_max)
759 /* it fits OK in this block */
760 memcpy (startp, dst_buf, dst - dst_buf);
762 close_block(b, sub_p2);
766 /* must split _this_ block as well .. */
767 struct ISAMB_block *sub_p3;
769 char file_item_buf[DST_ITEM_MAX];
770 char *file_item = file_item_buf;
774 zint no_items_first_half = 0;
781 close_block(b, sub_p2);
783 half = src + b->file[p->cat].head.block_size/2;
784 decode_ptr(&src, &pos);
786 /* read sub block so we can get no_items for it */
787 sub_p3 = open_block(b, pos);
788 no_items_first_half += sub_p3->no_items;
789 close_block(b, sub_p3);
794 file_item = file_item_buf;
795 (*b->method->codec.reset)(c1);
796 (*b->method->codec.decode)(c1, &file_item, &src);
798 decode_item_len(&src, &split_size_tmp);
799 *split_size = (int) split_size_tmp;
802 decode_ptr(&src, &pos);
804 /* read sub block so we can get no_items for it */
805 sub_p3 = open_block(b, pos);
806 no_items_first_half += sub_p3->no_items;
807 close_block(b, sub_p3);
809 /* p is first half */
810 p_new_size = src - dst_buf;
811 memcpy (p->bytes, dst_buf, p_new_size);
814 file_item = file_item_buf;
815 (*b->method->codec.reset)(c1);
816 (*b->method->codec.decode)(c1, &file_item, &src);
817 *split_size = file_item - file_item_buf;
818 memcpy(split_item, file_item_buf, *split_size);
820 decode_item_len(&src, &split_size_tmp);
821 *split_size = (int) split_size_tmp;
822 memcpy (split_item, src, *split_size);
825 /* *sp is second half */
826 *sp = new_int (b, p->cat);
827 (*sp)->size = endp - src;
828 memcpy ((*sp)->bytes, src, (*sp)->size);
830 p->size = p_new_size;
832 /* adjust no_items in first&second half */
833 (*sp)->no_items = p->no_items - no_items_first_half;
834 p->no_items = no_items_first_half;
838 close_block(b, sub_p1);
839 (*b->method->codec.stop)(c1);
843 int insert_leaf (ISAMB b, struct ISAMB_block **sp1, void *lookahead_item,
844 int *lookahead_mode, ISAMC_I *stream,
845 struct ISAMB_block **sp2,
846 void *sub_item, int *sub_size,
847 const void *max_item)
849 struct ISAMB_block *p = *sp1;
852 char dst_buf[DST_BUF_SIZE], *dst = dst_buf;
854 void *c1 = (*b->method->codec.start)();
855 void *c2 = (*b->method->codec.start)();
857 int quater = b->file[b->no_cat-1].head.block_max / 4;
858 char *mid_cut = dst_buf + quater * 2;
859 char *tail_cut = dst_buf + quater * 3;
860 char *maxp = dst_buf + b->file[b->no_cat-1].head.block_max;
863 char cut_item_buf[DST_ITEM_MAX];
864 int cut_item_size = 0;
865 int no_items = 0; /* number of items (total) */
866 int no_items_1 = 0; /* number of items (first half) */
867 int inserted_dst_bytes = 0;
871 char file_item_buf[DST_ITEM_MAX];
872 char *file_item = file_item_buf;
875 endp = p->bytes + p->size;
876 (*b->method->codec.decode)(c1, &file_item, &src);
879 const char *dst_item = 0; /* resulting item to be inserted */
880 char *lookahead_next;
885 d = (*b->method->compare_item)(file_item_buf, lookahead_item);
887 /* d now holds comparison between existing file item and
890 d > 0: lookahead before file
891 d < 0: lookahead after file
895 /* lookahead must be inserted */
896 dst_item = lookahead_item;
897 /* if this is not an insertion, it's really bad .. */
898 if (!*lookahead_mode)
900 yaz_log(YLOG_WARN, "isamb: Inconsistent register (1)");
901 assert(*lookahead_mode);
905 dst_item = file_item_buf;
907 if (!*lookahead_mode && d == 0)
909 /* it's a deletion and they match so there is nothing to be
910 inserted anyway .. But mark the thing bad (file item
911 was part of input.. The item will not be part of output */
914 else if (!half1 && dst > mid_cut)
916 /* we have reached the splitting point for the first time */
917 const char *dst_item_0 = dst_item;
918 half1 = dst; /* candidate for splitting */
920 /* encode the resulting item */
921 (*b->method->codec.encode)(c2, &dst, &dst_item);
923 cut_item_size = dst_item - dst_item_0;
924 assert(cut_item_size > 0);
925 memcpy (cut_item_buf, dst_item_0, cut_item_size);
928 no_items_1 = no_items;
933 /* encode the resulting item */
934 (*b->method->codec.encode)(c2, &dst, &dst_item);
938 /* now move "pointers" .. result has been encoded .. */
941 /* we must move the lookahead pointer */
943 inserted_dst_bytes += (dst - dst_0);
944 if (inserted_dst_bytes >= quater)
945 /* no more room. Mark lookahead as "gone".. */
949 /* move it really.. */
950 lookahead_next = lookahead_item;
951 if (!(*stream->read_item)(stream->clientData,
955 /* end of stream reached: no "more" and no lookahead */
959 if (lookahead_item && max_item &&
960 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
962 /* the lookahead goes beyond what we allow in this
963 leaf. Mark it as "gone" */
972 /* exact match .. move both pointers */
974 lookahead_next = lookahead_item;
975 if (!(*stream->read_item)(stream->clientData,
976 &lookahead_next, lookahead_mode))
982 break; /* end of file stream reached .. */
983 file_item = file_item_buf; /* move file pointer */
984 (*b->method->codec.decode)(c1, &file_item, &src);
988 /* file pointer must be moved */
991 file_item = file_item_buf;
992 (*b->method->codec.decode)(c1, &file_item, &src);
997 /* this loop runs when we are "appending" to a leaf page. That is
998 either it's empty (new) or all file items have been read in
1001 maxp = dst_buf + b->file[b->no_cat-1].head.block_max + quater;
1002 while (lookahead_item)
1005 const char *src = lookahead_item;
1008 /* if we have a lookahead item, we stop if we exceed the value of it */
1010 (*b->method->compare_item)(max_item, lookahead_item) <= 0)
1012 /* stop if we have reached the value of max item */
1015 if (!*lookahead_mode)
1017 /* this is append. So a delete is bad */
1018 yaz_log(YLOG_WARN, "isamb: Inconsistent register (2)");
1019 assert(*lookahead_mode);
1021 else if (!half1 && dst > tail_cut)
1023 const char *src_0 = src;
1024 half1 = dst; /* candidate for splitting */
1026 (*b->method->codec.encode)(c2, &dst, &src);
1028 cut_item_size = src - src_0;
1029 assert(cut_item_size > 0);
1030 memcpy (cut_item_buf, src_0, cut_item_size);
1032 no_items_1 = no_items;
1036 (*b->method->codec.encode)(c2, &dst, &src);
1046 dst_item = lookahead_item;
1047 if (!(*stream->read_item)(stream->clientData, &dst_item,
1054 new_size = dst - dst_buf;
1055 if (p && p->cat != b->no_cat-1 &&
1056 new_size > b->file[p->cat].head.block_max)
1058 /* non-btree block will be removed */
1061 /* delete it too!! */
1062 p = 0; /* make a new one anyway */
1065 { /* must create a new one */
1067 for (i = 0; i < b->no_cat; i++)
1068 if (new_size <= b->file[i].head.block_max)
1072 p = new_leaf (b, i);
1074 if (new_size > b->file[p->cat].head.block_max)
1077 const char *cut_item = cut_item_buf;
1082 assert(cut_item_size > 0);
1085 p->size = half1 - dst_buf;
1086 assert(p->size <= b->file[p->cat].head.block_max);
1087 memcpy (p->bytes, dst_buf, half1 - dst_buf);
1088 p->no_items = no_items_1;
1091 *sp2 = new_leaf (b, p->cat);
1093 (*b->method->codec.reset)(c2);
1095 first_dst = (*sp2)->bytes;
1097 (*b->method->codec.encode)(c2, &first_dst, &cut_item);
1099 memcpy (first_dst, half2, dst - half2);
1101 (*sp2)->size = (first_dst - (*sp2)->bytes) + (dst - half2);
1102 assert((*sp2)->size <= b->file[p->cat].head.block_max);
1103 (*sp2)->no_items = no_items - no_items_1;
1106 memcpy (sub_item, cut_item_buf, cut_item_size);
1107 *sub_size = cut_item_size;
1111 memcpy (p->bytes, dst_buf, dst - dst_buf);
1113 p->no_items = no_items;
1115 (*b->method->codec.stop)(c1);
1116 (*b->method->codec.stop)(c2);
1121 int insert_sub (ISAMB b, struct ISAMB_block **p, void *new_item,
1124 struct ISAMB_block **sp,
1125 void *sub_item, int *sub_size,
1126 const void *max_item)
1128 if (!*p || (*p)->leaf)
1129 return insert_leaf (b, p, new_item, mode, stream, sp, sub_item,
1130 sub_size, max_item);
1132 return insert_int (b, *p, new_item, mode, stream, sp, sub_item,
1133 sub_size, max_item);
1136 int isamb_unlink (ISAMB b, ISAM_P pos)
1138 struct ISAMB_block *p1;
1142 p1 = open_block(b, pos);
1147 const char *src = p1->bytes + p1->offset;
1149 void *c1 = (*b->method->codec.start)();
1151 decode_ptr(&src, &sub_p);
1152 isamb_unlink(b, sub_p);
1154 while (src != p1->bytes + p1->size)
1157 char file_item_buf[DST_ITEM_MAX];
1158 char *file_item = file_item_buf;
1159 (*b->method->codec.reset)(c1);
1160 (*b->method->codec.decode)(c1, &file_item, &src);
1163 decode_item_len(&src, &item_len);
1166 decode_ptr(&src, &sub_p);
1167 isamb_unlink(b, sub_p);
1170 (*b->method->codec.stop)(c1);
1177 void isamb_merge(ISAMB b, ISAM_P *pos, ISAMC_I *stream)
1179 char item_buf[DST_ITEM_MAX];
1183 int must_delete = 0;
1190 item_ptr = item_buf;
1192 (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1197 item_ptr = item_buf;
1198 more = (*stream->read_item)(stream->clientData, &item_ptr, &i_mode);
1201 struct ISAMB_block *p = 0, *sp = 0;
1202 char sub_item[DST_ITEM_MAX];
1206 p = open_block(b, *pos);
1207 more = insert_sub (b, &p, item_buf, &i_mode, stream, &sp,
1208 sub_item, &sub_size, 0);
1210 { /* increase level of tree by one */
1211 struct ISAMB_block *p2 = new_int (b, p->cat);
1212 char *dst = p2->bytes + p2->size;
1214 void *c1 = (*b->method->codec.start)();
1215 const char *sub_item_ptr = sub_item;
1218 encode_ptr(&dst, p->pos);
1219 assert (sub_size < 80 && sub_size > 1);
1221 (*b->method->codec.reset)(c1);
1222 (*b->method->codec.encode)(c1, &dst, &sub_item_ptr);
1224 encode_item_len (&dst, sub_size);
1225 memcpy (dst, sub_item, sub_size);
1228 encode_ptr(&dst, sp->pos);
1230 p2->size = dst - p2->bytes;
1231 p2->no_items = p->no_items + sp->no_items;
1232 *pos = p2->pos; /* return new super page */
1236 (*b->method->codec.stop)(c1);
1241 *pos = p->pos; /* return current one (again) */
1243 if (p->no_items == 0)
1251 isamb_unlink(b, *pos);
1256 ISAMB_PP isamb_pp_open_x(ISAMB isamb, ISAM_P pos, int *level, int scope)
1258 ISAMB_PP pp = xmalloc(sizeof(*pp));
1264 pp->block = xmalloc(ISAMB_MAX_LEVEL * sizeof(*pp->block));
1271 pp->skipped_numbers = 0;
1272 pp->returned_numbers = 0;
1274 for (i = 0; i<ISAMB_MAX_LEVEL; i++)
1275 pp->skipped_nodes[i] = pp->accessed_nodes[i] = 0;
1278 struct ISAMB_block *p = open_block(isamb, pos);
1279 const char *src = p->bytes + p->offset;
1280 pp->block[pp->level] = p;
1282 pp->total_size += p->size;
1286 decode_ptr(&src, &pos);
1287 p->offset = src - p->bytes;
1289 pp->accessed_nodes[pp->level]++;
1291 pp->block[pp->level+1] = 0;
1292 pp->maxlevel = pp->level;
1298 ISAMB_PP isamb_pp_open (ISAMB isamb, ISAM_P pos, int scope)
1300 return isamb_pp_open_x(isamb, pos, 0, scope);
1303 void isamb_pp_close_x(ISAMB_PP pp, zint *size, zint *blocks)
1308 yaz_log(YLOG_DEBUG, "isamb_pp_close lev=%d returned "ZINT_FORMAT" values, "
1309 "skipped "ZINT_FORMAT,
1310 pp->maxlevel, pp->skipped_numbers, pp->returned_numbers);
1311 for (i = pp->maxlevel; i>=0; i--)
1312 if (pp->skipped_nodes[i] || pp->accessed_nodes[i])
1313 yaz_log(YLOG_DEBUG, "isamb_pp_close level leaf-%d: "
1314 ZINT_FORMAT" read, "ZINT_FORMAT" skipped", i,
1315 pp->accessed_nodes[i], pp->skipped_nodes[i]);
1316 pp->isamb->skipped_numbers += pp->skipped_numbers;
1317 pp->isamb->returned_numbers += pp->returned_numbers;
1318 for (i = pp->maxlevel; i>=0; i--)
1320 pp->isamb->accessed_nodes[i] += pp->accessed_nodes[i];
1321 pp->isamb->skipped_nodes[i] += pp->skipped_nodes[i];
1324 *size = pp->total_size;
1326 *blocks = pp->no_blocks;
1327 for (i = 0; i <= pp->level; i++)
1328 close_block(pp->isamb, pp->block[i]);
1333 int isamb_block_info (ISAMB isamb, int cat)
1335 if (cat >= 0 && cat < isamb->no_cat)
1336 return isamb->file[cat].head.block_size;
1340 void isamb_pp_close (ISAMB_PP pp)
1342 isamb_pp_close_x(pp, 0, 0);
1345 /* simple recursive dumper .. */
1346 static void isamb_dump_r (ISAMB b, ISAM_P pos, void (*pr)(const char *str),
1350 char prefix_str[1024];
1353 struct ISAMB_block *p = open_block(b, pos);
1354 sprintf(prefix_str, "%*s " ZINT_FORMAT " cat=%d size=%d max=%d items="
1355 ZINT_FORMAT, level*2, "",
1356 pos, p->cat, p->size, b->file[p->cat].head.block_max,
1359 sprintf(prefix_str, "%*s " ZINT_FORMAT, level*2, "", pos);
1362 while (p->offset < p->size)
1364 const char *src = p->bytes + p->offset;
1366 (*b->method->codec.decode)(p->decodeClientData, &dst, &src);
1367 (*b->method->log_item)(YLOG_DEBUG, buf, prefix_str);
1368 p->offset = src - (char*) p->bytes;
1370 assert(p->offset == p->size);
1374 const char *src = p->bytes + p->offset;
1377 decode_ptr(&src, &sub);
1378 p->offset = src - (char*) p->bytes;
1380 isamb_dump_r(b, sub, pr, level+1);
1382 while (p->offset < p->size)
1385 char file_item_buf[DST_ITEM_MAX];
1386 char *file_item = file_item_buf;
1387 void *c1 = (*b->method->codec.start)();
1388 (*b->method->codec.decode)(c1, &file_item, &src);
1389 (*b->method->codec.stop)(c1);
1390 (*b->method->log_item)(YLOG_DEBUG, file_item_buf, prefix_str);
1393 decode_item_len(&src, &item_len);
1394 (*b->method->log_item)(YLOG_DEBUG, src, prefix_str);
1397 decode_ptr(&src, &sub);
1399 p->offset = src - (char*) p->bytes;
1401 isamb_dump_r(b, sub, pr, level+1);
1408 void isamb_dump(ISAMB b, ISAM_P pos, void (*pr)(const char *str))
1410 isamb_dump_r(b, pos, pr, 0);
1413 int isamb_pp_read(ISAMB_PP pp, void *buf)
1415 return isamb_pp_forward(pp, buf, 0);
1419 static int isamb_pp_on_right_node(ISAMB_PP pp, int level, const void *untilbuf)
1420 { /* looks one node higher to see if we should be on this node at all */
1421 /* useful in backing off quickly, and in avoiding tail descends */
1422 /* call with pp->level to begin with */
1423 struct ISAMB_block *p;
1426 ISAMB b = pp->isamb;
1432 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true for root");
1434 return 1; /* we can never skip the root node */
1437 p = pp->block[level];
1438 assert(p->offset <= p->size);
1439 if (p->offset < p->size)
1442 char file_item_buf[DST_ITEM_MAX];
1443 char *file_item = file_item_buf;
1444 void *c1 = (*b->method->codec.start)();
1445 assert(p->offset > 0);
1446 src = p->bytes + p->offset;
1447 (*b->method->codec.decode)(c1, &file_item, &src);
1448 (*b->method->codec.stop)(c1);
1449 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1452 assert(p->offset > 0);
1453 src = p->bytes + p->offset;
1454 decode_item_len(&src, &item_len);
1456 (*b->method->codec.log_item)(YLOG_DEBUG, untilbuf, "on_leaf: until");
1457 (*b->method->codec.log_item)(YLOG_DEBUG, src, "on_leaf: value");
1459 cmp = (*b->method->compare_item)(untilbuf, src);
1461 if (cmp < pp->scope)
1464 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning true "
1465 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1472 yaz_log(YLOG_DEBUG, "isamb_pp_on_right returning false "
1473 "cmp=%d lev=%d ofs=%d", cmp, level, p->offset);
1480 yaz_log(YLOG_DEBUG, "isamb_pp_on_right at tail, looking higher "
1483 return isamb_pp_on_right_node(pp, level, untilbuf);
1485 } /* isamb_pp_on_right_node */
1487 static int isamb_pp_read_on_leaf(ISAMB_PP pp, void *buf)
1489 /* reads the next item on the current leaf, returns 0 if end of leaf*/
1490 struct ISAMB_block *p = pp->block[pp->level];
1495 if (p->offset == p->size)
1498 yaz_log(YLOG_DEBUG, "isamb_pp_read_on_leaf returning 0 on "
1501 return 0; /* at end of leaf */
1503 src = p->bytes + p->offset;
1505 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1506 p->offset = src - (char*) p->bytes;
1508 (*pp->isamb->method->codec.log_item)(YLOG_DEBUG, buf,
1509 "read_on_leaf returning 1");
1511 pp->returned_numbers++;
1513 } /* read_on_leaf */
1515 static int isamb_pp_forward_on_leaf(ISAMB_PP pp, void *buf, const void *untilbuf)
1516 { /* forwards on the current leaf, returns 0 if not found */
1521 if (!isamb_pp_read_on_leaf(pp, buf))
1523 /* FIXME - this is an extra function call, inline the read? */
1524 cmp=(*pp->isamb->method->compare_item)(untilbuf, buf);
1526 { /* cmp<2 found a good one */
1529 yaz_log(YLOG_DEBUG, "isam_pp_fwd_on_leaf skipped %d items", skips);
1531 pp->returned_numbers++;
1535 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1536 return 0; /* never mind the rest of this leaf */
1537 pp->skipped_numbers++;
1540 } /* forward_on_leaf */
1542 static int isamb_pp_climb_level(ISAMB_PP pp, ISAM_P *pos)
1543 { /* climbs higher in the tree, until finds a level with data left */
1544 /* returns the node to (consider to) descend to in *pos) */
1545 struct ISAMB_block *p = pp->block[pp->level];
1548 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level starting "
1549 "at level %d node %d ofs=%d sz=%d",
1550 pp->level, p->pos, p->offset, p->size);
1552 assert(pp->level >= 0);
1553 assert(p->offset <= p->size);
1557 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level returning 0 at root");
1561 assert(pp->level>0);
1562 close_block(pp->isamb, pp->block[pp->level]);
1563 pp->block[pp->level] = 0;
1565 p = pp->block[pp->level];
1567 yaz_log(YLOG_DEBUG, "isamb_pp_climb_level climbed to level %d node %d ofs=%d",
1568 pp->level, p->pos, p->offset);
1571 assert(p->offset <= p->size);
1572 if (p->offset == p->size)
1574 /* we came from the last pointer, climb on */
1575 if (!isamb_pp_climb_level(pp, pos))
1577 p = pp->block[pp->level];
1582 char file_item_buf[DST_ITEM_MAX];
1583 char *file_item = file_item_buf;
1584 ISAMB b = pp->isamb;
1585 void *c1 = (*b->method->codec.start)();
1589 /* skip the child we just came from */
1591 yaz_log(YLOG_DEBUG, "isam_pp_climb_level: skipping lev=%d ofs=%d sz=%d",
1592 pp->level, p->offset, p->size);
1594 assert (p->offset < p->size);
1595 src = p->bytes + p->offset;
1597 (*b->method->codec.decode)(c1, &file_item, &src);
1598 (*b->method->codec.stop)(c1);
1600 decode_item_len(&src, &item_len);
1603 decode_ptr(&src, pos);
1604 p->offset = src - (char *)p->bytes;
1611 static zint isamb_pp_forward_unode(ISAMB_PP pp, zint pos, const void *untilbuf)
1612 { /* scans a upper node until it finds a child <= untilbuf */
1613 /* pp points to the key value, as always. pos is the child read from */
1615 /* if all values are too small, returns the last child in the node */
1616 /* FIXME - this can be detected, and avoided by looking at the */
1617 /* parent node, but that gets messy. Presumably the cost is */
1618 /* pretty low anyway */
1619 ISAMB b = pp->isamb;
1620 struct ISAMB_block *p = pp->block[pp->level];
1621 const char *src = p->bytes + p->offset;
1626 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode starting "
1627 "at level %d node %d ofs=%di sz=%d",
1628 pp->level, p->pos, p->offset, p->size);
1631 assert(p->offset <= p->size);
1632 if (p->offset == p->size)
1635 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at end "
1636 "at level %d node %d ofs=%di sz=%d",
1637 pp->level, p->pos, p->offset, p->size);
1639 return pos; /* already at the end of it */
1641 while(p->offset < p->size)
1644 char file_item_buf[DST_ITEM_MAX];
1645 char *file_item = file_item_buf;
1646 void *c1 = (*b->method->codec.start)();
1647 (*b->method->codec.decode)(c1, &file_item, &src);
1648 (*b->method->codec.stop)(c1);
1649 cmp = (*b->method->compare_item)(untilbuf, file_item_buf);
1652 decode_item_len(&src, &item_len);
1653 cmp = (*b->method->compare_item)(untilbuf, src);
1656 decode_ptr(&src, &nxtpos);
1657 if (cmp<pp->scope) /* cmp<2 */
1660 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning a hit "
1661 "at level %d node %d ofs=%d sz=%d",
1662 pp->level, p->pos, p->offset, p->size);
1667 p->offset = src-(char*)p->bytes;
1668 (pp->skipped_nodes[pp->maxlevel - pp->level -1])++;
1674 yaz_log(YLOG_DEBUG, "isamb_pp_forward_unode returning at tail "
1675 "at level %d node %d ofs=%d sz=%d skips=%d",
1676 pp->level, p->pos, p->offset, p->size, skips);
1678 return pos; /* that's the last one in the line */
1680 } /* forward_unode */
1682 static void isamb_pp_descend_to_leaf(ISAMB_PP pp, ISAM_P pos,
1683 const void *untilbuf)
1684 { /* climbs down the tree, from pos, to the leftmost leaf */
1685 struct ISAMB_block *p = pp->block[pp->level];
1689 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1690 "starting at lev %d node %d ofs=%d lf=%d u=%p",
1691 pp->level, p->pos, p->offset, p->leaf, untilbuf);
1694 pos = isamb_pp_forward_unode(pp, pos, untilbuf);
1697 p = open_block(pp->isamb, pos);
1698 pp->block[pp->level] = p;
1699 ++(pp->accessed_nodes[pp->maxlevel-pp->level]);
1702 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1703 "got lev %d node %d lf=%d",
1704 pp->level, p->pos, p->leaf);
1708 assert (p->offset==0);
1709 src = p->bytes + p->offset;
1710 decode_ptr(&src, &pos);
1711 p->offset = src-(char*)p->bytes;
1712 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1714 yaz_log(YLOG_DEBUG, "isamb_pp_descend_to_leaf "
1715 "returning at lev %d node %d ofs=%d lf=%d",
1716 pp->level, p->pos, p->offset, p->leaf);
1718 } /* descend_to_leaf */
1720 static int isamb_pp_find_next_leaf(ISAMB_PP pp)
1721 { /* finds the next leaf by climbing up and down */
1723 if (!isamb_pp_climb_level(pp, &pos))
1725 isamb_pp_descend_to_leaf(pp, pos, 0);
1729 static int isamb_pp_climb_desc(ISAMB_PP pp, const void *untilbuf)
1730 { /* climbs up and descends to a leaf where values >= *untilbuf are found */
1733 struct ISAMB_block *p = pp->block[pp->level];
1734 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc starting "
1735 "at level %d node %d ofs=%d sz=%d",
1736 pp->level, p->pos, p->offset, p->size);
1738 if (!isamb_pp_climb_level(pp, &pos))
1740 /* see if it would pay to climb one higher */
1741 if (!isamb_pp_on_right_node(pp, pp->level, untilbuf))
1742 if (!isamb_pp_climb_level(pp, &pos))
1744 isamb_pp_descend_to_leaf(pp, pos, untilbuf);
1746 p = pp->block[pp->level];
1747 yaz_log(YLOG_DEBUG, "isamb_pp_climb_desc done "
1748 "at level %d node %d ofs=%d sz=%d",
1749 pp->level, p->pos, p->offset, p->size);
1754 int isamb_pp_forward (ISAMB_PP pp, void *buf, const void *untilbuf)
1757 struct ISAMB_block *p = pp->block[pp->level];
1759 yaz_log(YLOG_DEBUG, "isamb_pp_forward starting "
1760 "at level %d node %d ofs=%d sz=%d u=%p sc=%d",
1761 pp->level, p->pos, p->offset, p->size, untilbuf, scope);
1765 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1768 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (A) "
1769 "at level %d node %d ofs=%d sz=%d",
1770 pp->level, p->pos, p->offset, p->size);
1774 if (! isamb_pp_climb_desc(pp, untilbuf))
1777 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning notfound (B) "
1778 "at level %d node %d ofs=%d sz=%d",
1779 pp->level, p->pos, p->offset, p->size);
1781 return 0; /* could not find a leaf */
1784 if (isamb_pp_forward_on_leaf(pp, buf, untilbuf))
1787 yaz_log(YLOG_DEBUG, "isamb_pp_forward (f) returning (c) "
1788 "at level %d node %d ofs=%d sz=%d",
1789 pp->level, p->pos, p->offset, p->size);
1793 } while (isamb_pp_find_next_leaf(pp));
1794 return 0; /* could not find at all */
1796 else { /* no untilbuf, a straight read */
1797 /* FIXME - this should be moved
1798 * directly into the pp_read */
1799 /* keeping here now, to keep same
1800 * interface as the old fwd */
1801 if (isamb_pp_read_on_leaf(pp, buf))
1804 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (D) "
1805 "at level %d node %d ofs=%d sz=%d",
1806 pp->level, p->pos, p->offset, p->size);
1810 if (isamb_pp_find_next_leaf(pp))
1813 yaz_log(YLOG_DEBUG, "isamb_pp_forward (read) returning (E) "
1814 "at level %d node %d ofs=%d sz=%d",
1815 pp->level, p->pos, p->offset, p->size);
1817 return isamb_pp_read_on_leaf(pp, buf);
1822 } /* isam_pp_forward (new version) */
1824 void isamb_pp_pos(ISAMB_PP pp, double *current, double *total)
1825 { /* return an estimate of the current position and of the total number of */
1826 /* occureences in the isam tree, based on the current leaf */
1830 /* if end-of-stream PP may not be leaf */
1832 *total = (double) (pp->block[0]->no_items);
1833 *current = (double) pp->returned_numbers;
1835 yaz_log(YLOG_LOG, "isamb_pp_pos returning: cur= %0.1f tot=%0.1f rn="
1836 ZINT_FORMAT, *current, *total, pp->returned_numbers);
1840 int isamb_pp_forward2(ISAMB_PP pp, void *buf, const void *untilb)
1844 struct ISAMB_block *p = pp->block[pp->level];
1845 ISAMB b = pp->isamb;
1849 while (p->offset == p->size)
1855 char file_item_buf[DST_ITEM_MAX];
1856 char *file_item = file_item_buf;
1860 while (p->offset == p->size)
1864 close_block (pp->isamb, pp->block[pp->level]);
1865 pp->block[pp->level] = 0;
1867 p = pp->block[pp->level];
1872 src = p->bytes + p->offset;
1875 c1 = (*b->method->codec.start)();
1876 (*b->method->codec.decode)(c1, &file_item, &src);
1878 decode_ptr (&src, &item_len);
1881 decode_ptr (&src, &pos);
1882 p->offset = src - (char*) p->bytes;
1884 src = p->bytes + p->offset;
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_item_len(&src, &item_len);
1903 if ((*b->method->compare_item)(untilb, src) <= 1)
1907 decode_ptr (&src, &pos);
1908 p->offset = src - (char*) p->bytes;
1915 pp->block[pp->level] = p = open_block (pp->isamb, pos);
1917 pp->total_size += p->size;
1925 src = p->bytes + p->offset;
1928 decode_ptr (&src, &pos);
1929 p->offset = src - (char*) p->bytes;
1931 if (!untilb || p->offset == p->size)
1933 assert(p->offset < p->size);
1936 file_item = file_item_buf;
1937 (*b->method->codec.reset)(c1);
1938 (*b->method->codec.decode)(c1, &file_item, &src);
1939 if ((*b->method->compare_item)(untilb, file_item_buf) <= 1)
1945 decode_ptr (&src, &item_len);
1946 if ((*b->method->compare_item)(untilb, src) <= 1)
1954 (*b->method->codec.stop)(c1);
1957 assert (p->offset < p->size);
1962 src = p->bytes + p->offset;
1963 (*pp->isamb->method->codec.decode)(p->decodeClientData, &dst, &src);
1964 p->offset = src - (char*) p->bytes;
1965 if (!untilb || (*pp->isamb->method->compare_item)(untilb, dst0) <= 1)
1968 if (p->offset == p->size) goto again;
1975 * indent-tabs-mode: nil
1977 * vim: shiftwidth=4 tabstop=8 expandtab