xref: /plan9/sys/src/cmd/venti/srv/index.c (revision ed2a258a218962e0b4e0f5cbbab48c6ce1bcbf02)
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  */
21 
22 #include "stdinc.h"
23 #include "dat.h"
24 #include "fns.h"
25 
26 static int	initindex1(Index*);
27 static ISect	*initisect1(ISect *is);
28 
29 #define KEY(k,d)	((d) ? (k)>>(32-(d)) : 0)
30 
31 static char IndexMagic[] = "venti index configuration";
32 
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;
41 
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 	}
54 
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(ix->name, is->index) != 0
79 		|| is->blocksize != blocksize
80 		|| is->tabsize != tabsize
81 		|| namecmp(is->name, ix->smap[i].name) != 0
82 		|| is->start != ix->smap[i].start
83 		|| is->stop != ix->smap[i].stop
84 		|| last != is->start
85 		|| is->start > is->stop){
86 			seterr(ECorrupt, "inconsistent index sections in %s", ix->name);
87 			freeindex(ix);
88 			return nil;
89 		}
90 		last = is->stop;
91 	}
92 	ix->tabsize = tabsize;
93 	ix->buckets = last;
94 
95 	if(initindex1(ix) < 0){
96 		freeindex(ix);
97 		return nil;
98 	}
99 
100 	ix->arenas = MKNZ(Arena*, ix->narenas);
101 	if(maparenas(ix->amap, ix->arenas, ix->narenas, ix->name) < 0){
102 		freeindex(ix);
103 		return nil;
104 	}
105 
106 	return ix;
107 }
108 
109 static int
110 initindex1(Index *ix)
111 {
112 	u32int buckets;
113 
114 	ix->div = (((u64int)1 << 32) + ix->buckets - 1) / ix->buckets;
115 	buckets = (((u64int)1 << 32) - 1) / ix->div + 1;
116 	if(buckets != ix->buckets){
117 		seterr(ECorrupt, "inconsistent math for divisor and buckets in %s", ix->name);
118 		return -1;
119 	}
120 
121 	return 0;
122 }
123 
124 int
125 wbindex(Index *ix)
126 {
127 	Fmt f;
128 	ZBlock *b;
129 	int i;
130 
131 	if(ix->nsects == 0){
132 		seterr(EOk, "no sections in index %s", ix->name);
133 		return -1;
134 	}
135 	b = alloczblock(ix->tabsize, 1, ix->blocksize);
136 	if(b == nil){
137 		seterr(EOk, "can't write index configuration: out of memory");
138 		return -1;
139 	}
140 	fmtzbinit(&f, b);
141 	if(outputindex(&f, ix) < 0){
142 		seterr(EOk, "can't make index configuration: table storage too small %d", ix->tabsize);
143 		freezblock(b);
144 		return -1;
145 	}
146 	for(i = 0; i < ix->nsects; i++){
147 		if(writepart(ix->sects[i]->part, ix->sects[i]->tabbase, b->data, ix->tabsize) < 0
148 		|| flushpart(ix->sects[i]->part) < 0){
149 			seterr(EOk, "can't write index: %r");
150 			freezblock(b);
151 			return -1;
152 		}
153 	}
154 	freezblock(b);
155 
156 	for(i = 0; i < ix->nsects; i++)
157 		if(wbisect(ix->sects[i]) < 0)
158 			return -1;
159 
160 	return 0;
161 }
162 
163 /*
164  * index: IndexMagic '\n' version '\n' name '\n' blocksize '\n' [V2: bitblocks '\n'] sections arenas
165  * version, blocksize: u32int
166  * name: max. ANameSize string
167  * sections, arenas: AMap
168  */
169 int
170 outputindex(Fmt *f, Index *ix)
171 {
172 	if(fmtprint(f, "%s\n%ud\n%s\n%ud\n", IndexMagic, ix->version, ix->name, ix->blocksize) < 0
173 	|| outputamap(f, ix->smap, ix->nsects) < 0
174 	|| outputamap(f, ix->amap, ix->narenas) < 0)
175 		return -1;
176 	return 0;
177 }
178 
179 int
180 parseindex(IFile *f, Index *ix)
181 {
182 	AMapN amn;
183 	u32int v;
184 	char *s;
185 
186 	/*
187 	 * magic
188 	 */
189 	s = ifileline(f);
190 	if(s == nil || strcmp(s, IndexMagic) != 0){
191 		seterr(ECorrupt, "bad index magic for %s", f->name);
192 		return -1;
193 	}
194 
195 	/*
196 	 * version
197 	 */
198 	if(ifileu32int(f, &v) < 0){
199 		seterr(ECorrupt, "syntax error: bad version number in %s", f->name);
200 		return -1;
201 	}
202 	ix->version = v;
203 	if(ix->version != IndexVersion){
204 		seterr(ECorrupt, "bad version number in %s", f->name);
205 		return -1;
206 	}
207 
208 	/*
209 	 * name
210 	 */
211 	if(ifilename(f, ix->name) < 0){
212 		seterr(ECorrupt, "syntax error: bad index name in %s", f->name);
213 		return -1;
214 	}
215 
216 	/*
217 	 * block size
218 	 */
219 	if(ifileu32int(f, &v) < 0){
220 		seterr(ECorrupt, "syntax error: bad block size number in %s", f->name);
221 		return -1;
222 	}
223 	ix->blocksize = v;
224 
225 	if(parseamap(f, &amn) < 0)
226 		return -1;
227 	ix->nsects = amn.n;
228 	ix->smap = amn.map;
229 
230 	if(parseamap(f, &amn) < 0)
231 		return -1;
232 	ix->narenas = amn.n;
233 	ix->amap = amn.map;
234 
235 	return 0;
236 }
237 
238 /*
239  * initialize an entirely new index
240  */
241 Index *
242 newindex(char *name, ISect **sects, int n)
243 {
244 	Index *ix;
245 	AMap *smap;
246 	u64int nb;
247 	u32int div, ub, xb, start, stop, blocksize, tabsize;
248 	int i, j;
249 
250 	if(n < 1){
251 		seterr(EOk, "creating index with no index sections");
252 		return nil;
253 	}
254 
255 	/*
256 	 * compute the total buckets available in the index,
257 	 * and the total buckets which are used.
258 	 */
259 	nb = 0;
260 	blocksize = sects[0]->blocksize;
261 	tabsize = sects[0]->tabsize;
262 	for(i = 0; i < n; i++){
263 		/*
264 		 * allow index, start, and stop to be set if index is correct
265 		 * and start and stop are what we would have picked.
266 		 * this allows calling fmtindex to reformat the index after
267 		 * replacing a bad index section with a freshly formatted one.
268 		 * start and stop are checked below.
269 		 */
270 		if(sects[i]->index[0] != '\0' && strcmp(sects[i]->index, name) != 0){
271 			seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
272 			return nil;
273 		}
274 		if(blocksize != sects[i]->blocksize){
275 			seterr(EOk, "mismatched block sizes in index sections");
276 			return nil;
277 		}
278 		if(tabsize != sects[i]->tabsize){
279 			seterr(EOk, "mismatched config table sizes in index sections");
280 			return nil;
281 		}
282 		nb += sects[i]->blocks;
283 	}
284 
285 	/*
286 	 * check for duplicate names
287 	 */
288 	for(i = 0; i < n; i++){
289 		for(j = i + 1; j < n; j++){
290 			if(namecmp(sects[i]->name, sects[j]->name) == 0){
291 				seterr(EOk, "duplicate section name %s for index %s", sects[i]->name, name);
292 				return nil;
293 			}
294 		}
295 	}
296 
297 	if(nb >= ((u64int)1 << 32)){
298 		seterr(EBug, "index too large");
299 		return nil;
300 	}
301 
302 	div = (((u64int)1 << 32) + nb - 1) / nb;
303 	ub = (((u64int)1 << 32) - 1) / div + 1;
304 	if(div < 100){
305 		seterr(EBug, "index divisor too coarse");
306 		return nil;
307 	}
308 	if(ub > nb){
309 		seterr(EBug, "index initialization math wrong");
310 		return nil;
311 	}
312 	xb = nb - ub;
313 
314 	/*
315 	 * initialize each of the index sections
316 	 * and the section map table
317 	 */
318 	smap = MKNZ(AMap, n);
319 	if(smap == nil){
320 		seterr(EOk, "can't create new index: out of memory");
321 		return nil;
322 	}
323 	start = 0;
324 	for(i = 0; i < n; i++){
325 		stop = start + sects[i]->blocks - xb / n;
326 		if(i == n - 1)
327 			stop = ub;
328 
329 		if(sects[i]->start != 0 || sects[i]->stop != 0)
330 		if(sects[i]->start != start || sects[i]->stop != stop){
331 			seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
332 			return nil;
333 		}
334 
335 		sects[i]->start = start;
336 		sects[i]->stop = stop;
337 		namecp(sects[i]->index, name);
338 
339 		smap[i].start = start;
340 		smap[i].stop = stop;
341 		namecp(smap[i].name, sects[i]->name);
342 		start = stop;
343 	}
344 
345 	/*
346 	 * initialize the index itself
347 	 */
348 	ix = MKZ(Index);
349 	if(ix == nil){
350 		seterr(EOk, "can't create new index: out of memory");
351 		free(smap);
352 		return nil;
353 	}
354 	ix->version = IndexVersion;
355 	namecp(ix->name, name);
356 	ix->sects = sects;
357 	ix->smap = smap;
358 	ix->nsects = n;
359 	ix->blocksize = blocksize;
360 	ix->buckets = ub;
361 	ix->tabsize = tabsize;
362 	ix->div = div;
363 
364 	if(initindex1(ix) < 0){
365 		free(smap);
366 		return nil;
367 	}
368 
369 	return ix;
370 }
371 
372 ISect*
373 initisect(Part *part)
374 {
375 	ISect *is;
376 	ZBlock *b;
377 	int ok;
378 
379 	b = alloczblock(HeadSize, 0, 0);
380 	if(b == nil || readpart(part, PartBlank, b->data, HeadSize) < 0){
381 		seterr(EAdmin, "can't read index section header: %r");
382 		return nil;
383 	}
384 
385 	is = MKZ(ISect);
386 	if(is == nil){
387 		freezblock(b);
388 		return nil;
389 	}
390 	is->part = part;
391 	ok = unpackisect(is, b->data);
392 	freezblock(b);
393 	if(ok < 0){
394 		seterr(ECorrupt, "corrupted index section header: %r");
395 		freeisect(is);
396 		return nil;
397 	}
398 
399 	if(is->version != ISectVersion1 && is->version != ISectVersion2){
400 		seterr(EAdmin, "unknown index section version %d", is->version);
401 		freeisect(is);
402 		return nil;
403 	}
404 
405 	return initisect1(is);
406 }
407 
408 ISect*
409 newisect(Part *part, u32int vers, char *name, u32int blocksize, u32int tabsize)
410 {
411 	ISect *is;
412 	u32int tabbase;
413 
414 	is = MKZ(ISect);
415 	if(is == nil)
416 		return nil;
417 
418 	namecp(is->name, name);
419 	is->version = vers;
420 	is->part = part;
421 	is->blocksize = blocksize;
422 	is->start = 0;
423 	is->stop = 0;
424 	tabbase = (PartBlank + HeadSize + blocksize - 1) & ~(blocksize - 1);
425 	is->blockbase = (tabbase + tabsize + blocksize - 1) & ~(blocksize - 1);
426 	is->blocks = is->part->size / blocksize - is->blockbase / blocksize;
427 	is->bucketmagic = 0;
428 	if(is->version == ISectVersion2){
429 		do{
430 			is->bucketmagic = fastrand();
431 		}while(is->bucketmagic==0);
432 	}
433 	is = initisect1(is);
434 	if(is == nil)
435 		return nil;
436 
437 	return is;
438 }
439 
440 /*
441  * initialize the computed parameters for an index
442  */
443 static ISect*
444 initisect1(ISect *is)
445 {
446 	u64int v;
447 
448 	is->buckmax = (is->blocksize - IBucketSize) / IEntrySize;
449 	is->blocklog = u64log2(is->blocksize);
450 	if(is->blocksize != (1 << is->blocklog)){
451 		seterr(ECorrupt, "illegal non-power-of-2 bucket size %d\n", is->blocksize);
452 		freeisect(is);
453 		return nil;
454 	}
455 	partblocksize(is->part, is->blocksize);
456 	is->tabbase = (PartBlank + HeadSize + is->blocksize - 1) & ~(is->blocksize - 1);
457 	if(is->tabbase >= is->blockbase){
458 		seterr(ECorrupt, "index section config table overlaps bucket storage");
459 		freeisect(is);
460 		return nil;
461 	}
462 	is->tabsize = is->blockbase - is->tabbase;
463 	v = is->part->size & ~(u64int)(is->blocksize - 1);
464 	if(is->blockbase + (u64int)is->blocks * is->blocksize != v){
465 		seterr(ECorrupt, "invalid blocks in index section %s", is->name);
466 		/* ZZZ what to do?
467 		freeisect(is);
468 		return nil;
469 		*/
470 	}
471 
472 	if(is->stop - is->start > is->blocks){
473 		seterr(ECorrupt, "index section overflows available space");
474 		freeisect(is);
475 		return nil;
476 	}
477 	if(is->start > is->stop){
478 		seterr(ECorrupt, "invalid index section range");
479 		freeisect(is);
480 		return nil;
481 	}
482 
483 	return is;
484 }
485 
486 int
487 wbisect(ISect *is)
488 {
489 	ZBlock *b;
490 
491 	b = alloczblock(HeadSize, 1, 0);
492 	if(b == nil){
493 		/* ZZZ set error? */
494 		return -1;
495 	}
496 
497 	if(packisect(is, b->data) < 0){
498 		seterr(ECorrupt, "can't make index section header: %r");
499 		freezblock(b);
500 		return -1;
501 	}
502 	if(writepart(is->part, PartBlank, b->data, HeadSize) < 0 || flushpart(is->part) < 0){
503 		seterr(EAdmin, "can't write index section header: %r");
504 		freezblock(b);
505 		return -1;
506 	}
507 	freezblock(b);
508 
509 	return 0;
510 }
511 
512 void
513 freeisect(ISect *is)
514 {
515 	if(is == nil)
516 		return;
517 	free(is);
518 }
519 
520 void
521 freeindex(Index *ix)
522 {
523 	int i;
524 
525 	if(ix == nil)
526 		return;
527 	free(ix->amap);
528 	free(ix->arenas);
529 	if(ix->sects)
530 		for(i = 0; i < ix->nsects; i++)
531 			freeisect(ix->sects[i]);
532 	free(ix->sects);
533 	free(ix->smap);
534 	free(ix);
535 }
536 
537 /*
538  * write a clump to an available arena in the index
539  * and return the address of the clump within the index.
540 ZZZ question: should this distinguish between an arena
541 filling up and real errors writing the clump?
542  */
543 u64int
544 writeiclump(Index *ix, Clump *c, u8int *clbuf)
545 {
546 	u64int a;
547 	int i;
548 	IAddr ia;
549 	AState as;
550 
551 	trace(TraceLump, "writeiclump enter");
552 	qlock(&ix->writing);
553 	for(i = ix->mapalloc; i < ix->narenas; i++){
554 		a = writeaclump(ix->arenas[i], c, clbuf);
555 		if(a != TWID64){
556 			ix->mapalloc = i;
557 			ia.addr = ix->amap[i].start + a;
558 			ia.type = c->info.type;
559 			ia.size = c->info.uncsize;
560 			ia.blocks = (c->info.size + ClumpSize + (1<<ABlockLog) - 1) >> ABlockLog;
561 			as.arena = ix->arenas[i];
562 			as.aa = ia.addr;
563 			as.stats = as.arena->memstats;
564 			insertscore(c->info.score, &ia, IEDirty, &as);
565 			qunlock(&ix->writing);
566 			trace(TraceLump, "writeiclump exit");
567 			return ia.addr;
568 		}
569 	}
570 	qunlock(&ix->writing);
571 
572 	seterr(EAdmin, "no space left in arenas");
573 	trace(TraceLump, "writeiclump failed");
574 	return TWID64;
575 }
576 
577 /*
578  * convert an arena index to an relative arena address
579  */
580 Arena*
581 amapitoa(Index *ix, u64int a, u64int *aa)
582 {
583 	int i, r, l, m;
584 
585 	l = 1;
586 	r = ix->narenas - 1;
587 	while(l <= r){
588 		m = (r + l) / 2;
589 		if(ix->amap[m].start <= a)
590 			l = m + 1;
591 		else
592 			r = m - 1;
593 	}
594 	l--;
595 
596 	if(a > ix->amap[l].stop){
597 for(i=0; i<ix->narenas; i++)
598 	print("arena %d: %llux - %llux\n", i, ix->amap[i].start, ix->amap[i].stop);
599 print("want arena %d for %llux\n", l, a);
600 		seterr(ECrash, "unmapped address passed to amapitoa");
601 		return nil;
602 	}
603 
604 	if(ix->arenas[l] == nil){
605 		seterr(ECrash, "unmapped arena selected in amapitoa");
606 		return nil;
607 	}
608 	*aa = a - ix->amap[l].start;
609 	return ix->arenas[l];
610 }
611 
612 /*
613  * convert an arena index to the bounds of the containing arena group.
614  */
615 Arena*
616 amapitoag(Index *ix, u64int a, u64int *gstart, u64int *glimit, int *g)
617 {
618 	u64int aa;
619 	Arena *arena;
620 
621 	arena = amapitoa(ix, a, &aa);
622 	if(arena == nil)
623 		return nil;
624 	if(arenatog(arena, aa, gstart, glimit, g) < 0)
625 		return nil;
626 	*gstart += a - aa;
627 	*glimit += a - aa;
628 	return arena;
629 }
630 
631 int
632 iaddrcmp(IAddr *ia1, IAddr *ia2)
633 {
634 	return ia1->type != ia2->type
635 		|| ia1->size != ia2->size
636 		|| ia1->blocks != ia2->blocks
637 		|| ia1->addr != ia2->addr;
638 }
639 
640 /*
641  * lookup the score in the partition
642  *
643  * nothing needs to be explicitly locked:
644  * only static parts of ix are used, and
645  * the bucket is locked by the DBlock lock.
646  */
647 int
648 loadientry(Index *ix, u8int *score, int type, IEntry *ie)
649 {
650 	ISect *is;
651 	DBlock *b;
652 	IBucket ib;
653 	u32int buck;
654 	int h, ok;
655 
656 	ok = -1;
657 
658 	trace(TraceLump, "loadientry enter");
659 
660 	/*
661 	qlock(&stats.lock);
662 	stats.indexreads++;
663 	qunlock(&stats.lock);
664 	*/
665 
666 	if(!inbloomfilter(mainindex->bloom, score)){
667 		trace(TraceLump, "loadientry bloomhit");
668 		return -1;
669 	}
670 
671 	trace(TraceLump, "loadientry loadibucket");
672 	b = loadibucket(ix, score, &is, &buck, &ib);
673 	trace(TraceLump, "loadientry loadedibucket");
674 	if(b == nil)
675 		return -1;
676 
677 	if(okibucket(&ib, is) < 0){
678 		trace(TraceLump, "loadientry badbucket");
679 		goto out;
680 	}
681 
682 	h = bucklook(score, type, ib.data, ib.n);
683 	if(h & 1){
684 		h ^= 1;
685 		trace(TraceLump, "loadientry found");
686 		unpackientry(ie, &ib.data[h]);
687 		ok = 0;
688 		goto out;
689 	}
690 	trace(TraceLump, "loadientry notfound");
691 	addstat(StatBloomFalseMiss, 1);
692 out:
693 	putdblock(b);
694 	trace(TraceLump, "loadientry exit");
695 	return ok;
696 }
697 
698 int
699 okibucket(IBucket *ib, ISect *is)
700 {
701 	if(ib->n <= is->buckmax)
702 		return 0;
703 
704 	seterr(EICorrupt, "corrupted disk index bucket: n=%ud max=%ud, range=[%lud,%lud)",
705 		ib->n, is->buckmax, is->start, is->stop);
706 	return -1;
707 }
708 
709 /*
710  * look for score within data;
711  * return 1 | byte index of matching index,
712  * or 0 | index of least element > score
713  */
714 int
715 bucklook(u8int *score, int otype, u8int *data, int n)
716 {
717 	int i, r, l, m, h, c, cc, type;
718 
719 	if(otype == -1)
720 		type = -1;
721 	else
722 		type = vttodisktype(otype);
723 	l = 0;
724 	r = n - 1;
725 	while(l <= r){
726 		m = (r + l) >> 1;
727 		h = m * IEntrySize;
728 		for(i = 0; i < VtScoreSize; i++){
729 			c = score[i];
730 			cc = data[h + i];
731 			if(c != cc){
732 				if(c > cc)
733 					l = m + 1;
734 				else
735 					r = m - 1;
736 				goto cont;
737 			}
738 		}
739 		cc = data[h + IEntryTypeOff];
740 		if(type != cc && type != -1){
741 			if(type > cc)
742 				l = m + 1;
743 			else
744 				r = m - 1;
745 			goto cont;
746 		}
747 		return h | 1;
748 	cont:;
749 	}
750 
751 	return l * IEntrySize;
752 }
753 
754 /*
755  * compare two IEntries; consistent with bucklook
756  */
757 int
758 ientrycmp(const void *vie1, const void *vie2)
759 {
760 	u8int *ie1, *ie2;
761 	int i, v1, v2;
762 
763 	ie1 = (u8int*)vie1;
764 	ie2 = (u8int*)vie2;
765 	for(i = 0; i < VtScoreSize; i++){
766 		v1 = ie1[i];
767 		v2 = ie2[i];
768 		if(v1 != v2){
769 			if(v1 < v2)
770 				return -1;
771 			return 1;
772 		}
773 	}
774 	v1 = ie1[IEntryTypeOff];
775 	v2 = ie2[IEntryTypeOff];
776 	if(v1 != v2){
777 		if(v1 < v2)
778 			return -1;
779 		return 1;
780 	}
781 	return 0;
782 }
783 
784 /*
785  * find the number of the index section holding bucket #buck
786  */
787 int
788 indexsect0(Index *ix, u32int buck)
789 {
790 	int r, l, m;
791 
792 	l = 1;
793 	r = ix->nsects - 1;
794 	while(l <= r){
795 		m = (r + l) >> 1;
796 		if(ix->sects[m]->start <= buck)
797 			l = m + 1;
798 		else
799 			r = m - 1;
800 	}
801 	return l - 1;
802 }
803 
804 /*
805  * load the index block at bucket #buck
806  */
807 static DBlock*
808 loadibucket0(Index *ix, u32int buck, ISect **pis, u32int *pbuck, IBucket *ib, int mode)
809 {
810 	ISect *is;
811 	DBlock *b;
812 
813 	is = ix->sects[indexsect0(ix, buck)];
814 	if(buck < is->start || is->stop <= buck){
815 		seterr(EAdmin, "index lookup out of range: %ud not found in index\n", buck);
816 		return nil;
817 	}
818 
819 	buck -= is->start;
820 	if((b = getdblock(is->part, is->blockbase + ((u64int)buck << is->blocklog), mode)) == nil)
821 		return nil;
822 
823 	if(pis)
824 		*pis = is;
825 	if(pbuck)
826 		*pbuck = buck;
827 	if(ib)
828 		unpackibucket(ib, b->data, is->bucketmagic);
829 	return b;
830 }
831 
832 /*
833  * find the number of the index section holding score
834  */
835 int
836 indexsect1(Index *ix, u8int *score)
837 {
838 	return indexsect0(ix, hashbits(score, 32) / ix->div);
839 }
840 
841 /*
842  * load the index block responsible for score.
843  */
844 static DBlock*
845 loadibucket1(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
846 {
847 	return loadibucket0(ix, hashbits(score, 32)/ix->div, pis, pbuck, ib, OREAD);
848 }
849 
850 int
851 indexsect(Index *ix, u8int *score)
852 {
853 	return indexsect1(ix, score);
854 }
855 
856 DBlock*
857 loadibucket(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
858 {
859 	return loadibucket1(ix, score, pis, pbuck, ib);
860 }
861 
862 
863