Blob


1 /*
2 * Index, mapping scores to log positions.
3 *
4 * The index is made up of some number of index sections, each of
5 * which is typically stored on a different disk. The blocks in all the
6 * index sections are logically numbered, with each index section
7 * responsible for a range of blocks. Blocks are typically 8kB.
8 *
9 * The N index blocks are treated as a giant hash table. The top 32 bits
10 * of score are used as the key for a lookup. Each index block holds
11 * one hash bucket, which is responsible for ceil(2^32 / N) of the key space.
12 *
13 * The index is sized so that a particular bucket is extraordinarily
14 * unlikely to overflow: assuming compressed data blocks are 4kB
15 * on disk, and assuming each block has a 40 byte index entry,
16 * the index data will be 1% of the total data. Since scores are essentially
17 * random, all buckets should be about the same fullness.
18 * A factor of 5 gives us a wide comfort boundary to account for
19 * random variation. So the index disk space should be 5% of the arena disk space.
20 */
22 #include "stdinc.h"
23 #include "dat.h"
24 #include "fns.h"
26 static int initindex1(Index*);
27 static ISect *initisect1(ISect *is);
29 #define KEY(k,d) ((d) ? (k)>>(32-(d)) : 0)
31 static char IndexMagic[] = "venti index configuration";
33 Index*
34 initindex(char *name, ISect **sects, int n)
35 {
36 IFile f;
37 Index *ix;
38 ISect *is;
39 u32int last, blocksize, tabsize;
40 int i;
42 if(n <= 0){
43 fprint(2, "bad n\n");
44 seterr(EOk, "no index sections to initialize index");
45 return nil;
46 }
47 ix = MKZ(Index);
48 if(ix == nil){
49 fprint(2, "no mem\n");
50 seterr(EOk, "can't initialize index: out of memory");
51 freeindex(ix);
52 return nil;
53 }
55 tabsize = sects[0]->tabsize;
56 if(partifile(&f, sects[0]->part, sects[0]->tabbase, tabsize) < 0)
57 return nil;
58 if(parseindex(&f, ix) < 0){
59 freeifile(&f);
60 freeindex(ix);
61 return nil;
62 }
63 freeifile(&f);
64 if(namecmp(ix->name, name) != 0){
65 seterr(ECorrupt, "mismatched index name: found %s expected %s", ix->name, name);
66 return nil;
67 }
68 if(ix->nsects != n){
69 seterr(ECorrupt, "mismatched number index sections: found %d expected %d", n, ix->nsects);
70 freeindex(ix);
71 return nil;
72 }
73 ix->sects = sects;
74 last = 0;
75 blocksize = ix->blocksize;
76 for(i = 0; i < ix->nsects; i++){
77 is = sects[i];
78 if(namecmp(is->index, ix->name) != 0) {
79 seterr(ECorrupt, "%s: index name is %s, not %s",
80 sects[i]->part->name, is->index, ix->name);
81 bad:
82 freeindex(ix);
83 return nil;
84 }
85 if(is->blocksize != blocksize) {
86 seterr(ECorrupt, "%s: blocksize is %d, not %d",
87 sects[i]->part->name, (int)is->blocksize, (int)blocksize);
88 goto bad;
89 }
90 if(is->tabsize != tabsize) {
91 seterr(ECorrupt, "%s: tabsize is %d, not %d",
92 sects[i]->part->name, (int)is->tabsize, (int)tabsize);
93 goto bad;
94 }
95 if(namecmp(is->name, ix->smap[i].name) != 0) {
96 seterr(ECorrupt, "%s: name is %s, not %s",
97 sects[i]->part->name, is->name, ix->smap[i].name);
98 goto bad;
99 }
100 if(is->start != ix->smap[i].start || is->stop != ix->smap[i].stop) {
101 seterr(ECorrupt, "%s: range is %lld,%lld, not %lld,%lld",
102 sects[i]->part->name, is->start, is->stop,
103 ix->smap[i].start, ix->smap[i].stop);
104 goto bad;
106 if(is->start > is->stop) {
107 seterr(ECorrupt, "%s: invalid range %lld,%lld",
108 sects[i]->part->name, is->start, is->stop);
109 goto bad;
111 if(is->start != last || is->start > is->stop) {
112 seterr(ECorrupt, "%s: range %lld-%lld, but last section ended at %lld",
113 sects[i]->part->name, is->start, is->stop, last);
114 goto bad;
116 last = is->stop;
118 ix->tabsize = tabsize;
119 ix->buckets = last;
121 if(initindex1(ix) < 0){
122 freeindex(ix);
123 return nil;
126 ix->arenas = MKNZ(Arena*, ix->narenas);
127 if(maparenas(ix->amap, ix->arenas, ix->narenas, ix->name) < 0){
128 freeindex(ix);
129 return nil;
132 return ix;
135 static int
136 initindex1(Index *ix)
138 u32int buckets;
140 ix->div = (((u64int)1 << 32) + ix->buckets - 1) / ix->buckets;
141 buckets = (((u64int)1 << 32) - 1) / ix->div + 1;
142 if(buckets != ix->buckets){
143 seterr(ECorrupt, "inconsistent math for divisor and buckets in %s", ix->name);
144 return -1;
147 return 0;
150 int
151 wbindex(Index *ix)
153 Fmt f;
154 ZBlock *b;
155 int i;
157 if(ix->nsects == 0){
158 seterr(EOk, "no sections in index %s", ix->name);
159 return -1;
161 b = alloczblock(ix->tabsize, 1, ix->blocksize);
162 if(b == nil){
163 seterr(EOk, "can't write index configuration: out of memory");
164 return -1;
166 fmtzbinit(&f, b);
167 if(outputindex(&f, ix) < 0){
168 seterr(EOk, "can't make index configuration: table storage too small %d", ix->tabsize);
169 freezblock(b);
170 return -1;
172 for(i = 0; i < ix->nsects; i++){
173 if(writepart(ix->sects[i]->part, ix->sects[i]->tabbase, b->data, ix->tabsize) < 0
174 || flushpart(ix->sects[i]->part) < 0){
175 seterr(EOk, "can't write index: %r");
176 freezblock(b);
177 return -1;
180 freezblock(b);
182 for(i = 0; i < ix->nsects; i++)
183 if(wbisect(ix->sects[i]) < 0)
184 return -1;
186 return 0;
189 /*
190 * index: IndexMagic '\n' version '\n' name '\n' blocksize '\n' [V2: bitblocks '\n'] sections arenas
191 * version, blocksize: u32int
192 * name: max. ANameSize string
193 * sections, arenas: AMap
194 */
195 int
196 outputindex(Fmt *f, Index *ix)
198 if(fmtprint(f, "%s\n%ud\n%s\n%ud\n", IndexMagic, ix->version, ix->name, ix->blocksize) < 0
199 || outputamap(f, ix->smap, ix->nsects) < 0
200 || outputamap(f, ix->amap, ix->narenas) < 0)
201 return -1;
202 return 0;
205 int
206 parseindex(IFile *f, Index *ix)
208 AMapN amn;
209 u32int v;
210 char *s;
212 /*
213 * magic
214 */
215 s = ifileline(f);
216 if(s == nil || strcmp(s, IndexMagic) != 0){
217 seterr(ECorrupt, "bad index magic for %s", f->name);
218 return -1;
221 /*
222 * version
223 */
224 if(ifileu32int(f, &v) < 0){
225 seterr(ECorrupt, "syntax error: bad version number in %s", f->name);
226 return -1;
228 ix->version = v;
229 if(ix->version != IndexVersion){
230 seterr(ECorrupt, "bad version number in %s", f->name);
231 return -1;
234 /*
235 * name
236 */
237 if(ifilename(f, ix->name) < 0){
238 seterr(ECorrupt, "syntax error: bad index name in %s", f->name);
239 return -1;
242 /*
243 * block size
244 */
245 if(ifileu32int(f, &v) < 0){
246 seterr(ECorrupt, "syntax error: bad block size number in %s", f->name);
247 return -1;
249 ix->blocksize = v;
251 if(parseamap(f, &amn) < 0)
252 return -1;
253 ix->nsects = amn.n;
254 ix->smap = amn.map;
256 if(parseamap(f, &amn) < 0)
257 return -1;
258 ix->narenas = amn.n;
259 ix->amap = amn.map;
261 return 0;
264 /*
265 * initialize an entirely new index
266 */
267 Index *
268 newindex(char *name, ISect **sects, int n)
270 Index *ix;
271 AMap *smap;
272 u64int nb;
273 u32int div, ub, xb, start, stop, blocksize, tabsize;
274 int i, j;
276 if(n < 1){
277 seterr(EOk, "creating index with no index sections");
278 return nil;
281 /*
282 * compute the total buckets available in the index,
283 * and the total buckets which are used.
284 */
285 nb = 0;
286 blocksize = sects[0]->blocksize;
287 tabsize = sects[0]->tabsize;
288 for(i = 0; i < n; i++){
289 /*
290 * allow index, start, and stop to be set if index is correct
291 * and start and stop are what we would have picked.
292 * this allows calling fmtindex to reformat the index after
293 * replacing a bad index section with a freshly formatted one.
294 * start and stop are checked below.
295 */
296 if(sects[i]->index[0] != '\0' && strcmp(sects[i]->index, name) != 0){
297 seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
298 return nil;
300 if(blocksize != sects[i]->blocksize){
301 seterr(EOk, "%s has block size %d, but %s has %d",
302 sects[0]->part->name, (int)blocksize,
303 sects[i]->part->name, (int)sects[i]->blocksize);
304 return nil;
306 if(tabsize != sects[i]->tabsize){
307 seterr(EOk, "%s has table size %d, but %s has %d",
308 sects[0]->part->name, (int)tabsize,
309 sects[i]->part->name, (int)sects[i]->tabsize);
310 return nil;
312 nb += sects[i]->blocks;
315 /*
316 * check for duplicate names
317 */
318 for(i = 0; i < n; i++){
319 for(j = i + 1; j < n; j++){
320 if(namecmp(sects[i]->name, sects[j]->name) == 0){
321 seterr(EOk, "%s and %s both have section name %s",
322 sects[i]->part->name,
323 sects[j]->part->name,
324 sects[i]->name);
325 return nil;
330 if(nb >= ((u64int)1 << 32)){
331 fprint(2, "%s: index is 2^32 blocks or more; ignoring some of it\n",
332 argv0);
333 nb = ((u64int)1 << 32) - 1;
336 div = (((u64int)1 << 32) + nb - 1) / nb;
337 if(div < 100){
338 fprint(2, "%s: index divisor %d too coarse; "
339 "index larger than needed, ignoring some of it\n",
340 argv0, div);
341 div = 100;
342 nb = (((u64int)1 << 32) - 1) / (100 - 1);
344 ub = (((u64int)1 << 32) - 1) / div + 1;
345 if(ub > nb){
346 seterr(EBug, "index initialization math wrong");
347 return nil;
349 xb = nb - ub;
351 /*
352 * initialize each of the index sections
353 * and the section map table
354 */
355 smap = MKNZ(AMap, n);
356 if(smap == nil){
357 seterr(EOk, "can't create new index: out of memory");
358 return nil;
360 start = 0;
361 for(i = 0; i < n; i++){
362 stop = start + sects[i]->blocks - xb / n;
363 if(i == n - 1)
364 stop = ub;
366 if(sects[i]->start != 0 || sects[i]->stop != 0)
367 if(sects[i]->start != start || sects[i]->stop != stop){
368 seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
369 return nil;
372 sects[i]->start = start;
373 sects[i]->stop = stop;
374 namecp(sects[i]->index, name);
376 smap[i].start = start;
377 smap[i].stop = stop;
378 namecp(smap[i].name, sects[i]->name);
379 start = stop;
382 /*
383 * initialize the index itself
384 */
385 ix = MKZ(Index);
386 if(ix == nil){
387 seterr(EOk, "can't create new index: out of memory");
388 free(smap);
389 return nil;
391 ix->version = IndexVersion;
392 namecp(ix->name, name);
393 ix->sects = sects;
394 ix->smap = smap;
395 ix->nsects = n;
396 ix->blocksize = blocksize;
397 ix->buckets = ub;
398 ix->tabsize = tabsize;
399 ix->div = div;
401 if(initindex1(ix) < 0){
402 free(smap);
403 return nil;
406 return ix;
409 ISect*
410 initisect(Part *part)
412 ISect *is;
413 ZBlock *b;
414 int ok;
416 b = alloczblock(HeadSize, 0, 0);
417 if(b == nil || readpart(part, PartBlank, b->data, HeadSize) < 0){
418 seterr(EAdmin, "can't read index section header: %r");
419 return nil;
422 is = MKZ(ISect);
423 if(is == nil){
424 freezblock(b);
425 return nil;
427 is->part = part;
428 ok = unpackisect(is, b->data);
429 freezblock(b);
430 if(ok < 0){
431 seterr(ECorrupt, "corrupted index section header: %r");
432 freeisect(is);
433 return nil;
436 if(is->version != ISectVersion1 && is->version != ISectVersion2){
437 seterr(EAdmin, "unknown index section version %d", is->version);
438 freeisect(is);
439 return nil;
442 return initisect1(is);
445 ISect*
446 newisect(Part *part, u32int vers, char *name, u32int blocksize, u32int tabsize)
448 ISect *is;
449 u32int tabbase;
451 is = MKZ(ISect);
452 if(is == nil)
453 return nil;
455 namecp(is->name, name);
456 is->version = vers;
457 is->part = part;
458 is->blocksize = blocksize;
459 is->start = 0;
460 is->stop = 0;
461 tabbase = (PartBlank + HeadSize + blocksize - 1) & ~(blocksize - 1);
462 is->blockbase = (tabbase + tabsize + blocksize - 1) & ~(blocksize - 1);
463 is->blocks = is->part->size / blocksize - is->blockbase / blocksize;
464 is->bucketmagic = 0;
465 if(is->version == ISectVersion2){
466 do{
467 is->bucketmagic = fastrand();
468 }while(is->bucketmagic==0);
470 is = initisect1(is);
471 if(is == nil)
472 return nil;
474 return is;
477 /*
478 * initialize the computed parameters for an index
479 */
480 static ISect*
481 initisect1(ISect *is)
483 u64int v;
485 is->buckmax = (is->blocksize - IBucketSize) / IEntrySize;
486 is->blocklog = u64log2(is->blocksize);
487 if(is->blocksize != (1 << is->blocklog)){
488 seterr(ECorrupt, "illegal non-power-of-2 bucket size %d\n", is->blocksize);
489 freeisect(is);
490 return nil;
492 partblocksize(is->part, is->blocksize);
493 is->tabbase = (PartBlank + HeadSize + is->blocksize - 1) & ~(is->blocksize - 1);
494 if(is->tabbase >= is->blockbase){
495 seterr(ECorrupt, "index section config table overlaps bucket storage");
496 freeisect(is);
497 return nil;
499 is->tabsize = is->blockbase - is->tabbase;
500 v = is->part->size & ~(u64int)(is->blocksize - 1);
501 if(is->blockbase + (u64int)is->blocks * is->blocksize != v){
502 seterr(ECorrupt, "invalid blocks in index section %s", is->name);
503 /* ZZZ what to do?
504 freeisect(is);
505 return nil;
506 */
509 if(is->stop - is->start > is->blocks){
510 seterr(ECorrupt, "index section overflows available space");
511 freeisect(is);
512 return nil;
514 if(is->start > is->stop){
515 seterr(ECorrupt, "invalid index section range");
516 freeisect(is);
517 return nil;
520 return is;
523 int
524 wbisect(ISect *is)
526 ZBlock *b;
528 b = alloczblock(HeadSize, 1, 0);
529 if(b == nil){
530 /* ZZZ set error? */
531 return -1;
534 if(packisect(is, b->data) < 0){
535 seterr(ECorrupt, "can't make index section header: %r");
536 freezblock(b);
537 return -1;
539 if(writepart(is->part, PartBlank, b->data, HeadSize) < 0 || flushpart(is->part) < 0){
540 seterr(EAdmin, "can't write index section header: %r");
541 freezblock(b);
542 return -1;
544 freezblock(b);
546 return 0;
549 void
550 freeisect(ISect *is)
552 if(is == nil)
553 return;
554 free(is);
557 void
558 freeindex(Index *ix)
560 int i;
562 if(ix == nil)
563 return;
564 free(ix->amap);
565 free(ix->arenas);
566 if(ix->sects)
567 for(i = 0; i < ix->nsects; i++)
568 freeisect(ix->sects[i]);
569 free(ix->sects);
570 free(ix->smap);
571 free(ix);
574 /*
575 * write a clump to an available arena in the index
576 * and return the address of the clump within the index.
577 ZZZ question: should this distinguish between an arena
578 filling up and real errors writing the clump?
579 */
580 u64int
581 writeiclump(Index *ix, Clump *c, u8int *clbuf)
583 u64int a;
584 int i;
585 IAddr ia;
586 AState as;
588 trace(TraceLump, "writeiclump enter");
589 qlock(&ix->writing);
590 for(i = ix->mapalloc; i < ix->narenas; i++){
591 a = writeaclump(ix->arenas[i], c, clbuf);
592 if(a != TWID64){
593 ix->mapalloc = i;
594 ia.addr = ix->amap[i].start + a;
595 ia.type = c->info.type;
596 ia.size = c->info.uncsize;
597 ia.blocks = (c->info.size + ClumpSize + (1<<ABlockLog) - 1) >> ABlockLog;
598 as.arena = ix->arenas[i];
599 as.aa = ia.addr;
600 as.stats = as.arena->memstats;
601 insertscore(c->info.score, &ia, IEDirty, &as);
602 qunlock(&ix->writing);
603 trace(TraceLump, "writeiclump exit");
604 return ia.addr;
607 qunlock(&ix->writing);
609 seterr(EAdmin, "no space left in arenas");
610 trace(TraceLump, "writeiclump failed");
611 return TWID64;
614 /*
615 * convert an arena index to an relative arena address
616 */
617 Arena*
618 amapitoa(Index *ix, u64int a, u64int *aa)
620 int i, r, l, m;
622 l = 1;
623 r = ix->narenas - 1;
624 while(l <= r){
625 m = (r + l) / 2;
626 if(ix->amap[m].start <= a)
627 l = m + 1;
628 else
629 r = m - 1;
631 l--;
633 if(a > ix->amap[l].stop){
634 for(i=0; i<ix->narenas; i++)
635 print("arena %d: %llux - %llux\n", i, ix->amap[i].start, ix->amap[i].stop);
636 print("want arena %d for %llux\n", l, a);
637 seterr(ECrash, "unmapped address passed to amapitoa");
638 return nil;
641 if(ix->arenas[l] == nil){
642 seterr(ECrash, "unmapped arena selected in amapitoa");
643 return nil;
645 *aa = a - ix->amap[l].start;
646 return ix->arenas[l];
649 /*
650 * convert an arena index to the bounds of the containing arena group.
651 */
652 Arena*
653 amapitoag(Index *ix, u64int a, u64int *gstart, u64int *glimit, int *g)
655 u64int aa;
656 Arena *arena;
658 arena = amapitoa(ix, a, &aa);
659 if(arena == nil)
660 return nil;
661 if(arenatog(arena, aa, gstart, glimit, g) < 0)
662 return nil;
663 *gstart += a - aa;
664 *glimit += a - aa;
665 return arena;
668 int
669 iaddrcmp(IAddr *ia1, IAddr *ia2)
671 return ia1->type != ia2->type
672 || ia1->size != ia2->size
673 || ia1->blocks != ia2->blocks
674 || ia1->addr != ia2->addr;
677 /*
678 * lookup the score in the partition
680 * nothing needs to be explicitly locked:
681 * only static parts of ix are used, and
682 * the bucket is locked by the DBlock lock.
683 */
684 int
685 loadientry(Index *ix, u8int *score, int type, IEntry *ie)
687 ISect *is;
688 DBlock *b;
689 IBucket ib;
690 u32int buck;
691 int h, ok;
693 ok = -1;
695 trace(TraceLump, "loadientry enter");
697 /*
698 qlock(&stats.lock);
699 stats.indexreads++;
700 qunlock(&stats.lock);
701 */
703 if(!inbloomfilter(mainindex->bloom, score)){
704 trace(TraceLump, "loadientry bloomhit");
705 return -1;
708 trace(TraceLump, "loadientry loadibucket");
709 b = loadibucket(ix, score, &is, &buck, &ib);
710 trace(TraceLump, "loadientry loadedibucket");
711 if(b == nil)
712 return -1;
714 if(okibucket(&ib, is) < 0){
715 trace(TraceLump, "loadientry badbucket");
716 goto out;
719 h = bucklook(score, type, ib.data, ib.n);
720 if(h & 1){
721 h ^= 1;
722 trace(TraceLump, "loadientry found");
723 unpackientry(ie, &ib.data[h]);
724 ok = 0;
725 goto out;
727 trace(TraceLump, "loadientry notfound");
728 addstat(StatBloomFalseMiss, 1);
729 out:
730 putdblock(b);
731 trace(TraceLump, "loadientry exit");
732 return ok;
735 int
736 okibucket(IBucket *ib, ISect *is)
738 if(ib->n <= is->buckmax)
739 return 0;
741 seterr(EICorrupt, "corrupted disk index bucket: n=%ud max=%ud, range=[%lud,%lud)",
742 ib->n, is->buckmax, is->start, is->stop);
743 return -1;
746 /*
747 * look for score within data;
748 * return 1 | byte index of matching index,
749 * or 0 | index of least element > score
750 */
751 int
752 bucklook(u8int *score, int otype, u8int *data, int n)
754 int i, r, l, m, h, c, cc, type;
756 if(otype == -1)
757 type = -1;
758 else
759 type = vttodisktype(otype);
760 l = 0;
761 r = n - 1;
762 while(l <= r){
763 m = (r + l) >> 1;
764 h = m * IEntrySize;
765 for(i = 0; i < VtScoreSize; i++){
766 c = score[i];
767 cc = data[h + i];
768 if(c != cc){
769 if(c > cc)
770 l = m + 1;
771 else
772 r = m - 1;
773 goto cont;
776 cc = data[h + IEntryTypeOff];
777 if(type != cc && type != -1){
778 if(type > cc)
779 l = m + 1;
780 else
781 r = m - 1;
782 goto cont;
784 return h | 1;
785 cont:;
788 return l * IEntrySize;
791 /*
792 * compare two IEntries; consistent with bucklook
793 */
794 int
795 ientrycmp(const void *vie1, const void *vie2)
797 u8int *ie1, *ie2;
798 int i, v1, v2;
800 ie1 = (u8int*)vie1;
801 ie2 = (u8int*)vie2;
802 for(i = 0; i < VtScoreSize; i++){
803 v1 = ie1[i];
804 v2 = ie2[i];
805 if(v1 != v2){
806 if(v1 < v2)
807 return -1;
808 return 1;
811 v1 = ie1[IEntryTypeOff];
812 v2 = ie2[IEntryTypeOff];
813 if(v1 != v2){
814 if(v1 < v2)
815 return -1;
816 return 1;
818 return 0;
821 /*
822 * find the number of the index section holding bucket #buck
823 */
824 int
825 indexsect0(Index *ix, u32int buck)
827 int r, l, m;
829 l = 1;
830 r = ix->nsects - 1;
831 while(l <= r){
832 m = (r + l) >> 1;
833 if(ix->sects[m]->start <= buck)
834 l = m + 1;
835 else
836 r = m - 1;
838 return l - 1;
841 /*
842 * load the index block at bucket #buck
843 */
844 static DBlock*
845 loadibucket0(Index *ix, u32int buck, ISect **pis, u32int *pbuck, IBucket *ib, int mode)
847 ISect *is;
848 DBlock *b;
850 is = ix->sects[indexsect0(ix, buck)];
851 if(buck < is->start || is->stop <= buck){
852 seterr(EAdmin, "index lookup out of range: %ud not found in index\n", buck);
853 return nil;
856 buck -= is->start;
857 if((b = getdblock(is->part, is->blockbase + ((u64int)buck << is->blocklog), mode)) == nil)
858 return nil;
860 if(pis)
861 *pis = is;
862 if(pbuck)
863 *pbuck = buck;
864 if(ib)
865 unpackibucket(ib, b->data, is->bucketmagic);
866 return b;
869 /*
870 * find the number of the index section holding score
871 */
872 int
873 indexsect1(Index *ix, u8int *score)
875 return indexsect0(ix, hashbits(score, 32) / ix->div);
878 /*
879 * load the index block responsible for score.
880 */
881 static DBlock*
882 loadibucket1(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
884 return loadibucket0(ix, hashbits(score, 32)/ix->div, pis, pbuck, ib, OREAD);
887 int
888 indexsect(Index *ix, u8int *score)
890 return indexsect1(ix, score);
893 DBlock*
894 loadibucket(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
896 return loadibucket1(ix, score, pis, pbuck, ib);