xref: /plan9/sys/src/cmd/venti/srv/index.c (revision 92b836f472dc5c766dd22dd5565eac9f3e49077c)
1368c31abSDavid du Colombier /*
2368c31abSDavid du Colombier  * Index, mapping scores to log positions.
3368c31abSDavid du Colombier  *
4368c31abSDavid du Colombier  * The index is made up of some number of index sections, each of
5368c31abSDavid du Colombier  * which is typically stored on a different disk.  The blocks in all the
6368c31abSDavid du Colombier  * index sections are logically numbered, with each index section
7368c31abSDavid du Colombier  * responsible for a range of blocks.  Blocks are typically 8kB.
8368c31abSDavid du Colombier  *
9368c31abSDavid du Colombier  * The N index blocks are treated as a giant hash table.  The top 32 bits
10368c31abSDavid du Colombier  * of score are used as the key for a lookup.  Each index block holds
11368c31abSDavid du Colombier  * one hash bucket, which is responsible for ceil(2^32 / N) of the key space.
12368c31abSDavid du Colombier  *
13368c31abSDavid du Colombier  * The index is sized so that a particular bucket is extraordinarily
14368c31abSDavid du Colombier  * unlikely to overflow: assuming compressed data blocks are 4kB
15368c31abSDavid du Colombier  * on disk, and assuming each block has a 40 byte index entry,
16368c31abSDavid du Colombier  * the index data will be 1% of the total data.  Since scores are essentially
17368c31abSDavid du Colombier  * random, all buckets should be about the same fullness.
18368c31abSDavid du Colombier  * A factor of 5 gives us a wide comfort boundary to account for
19368c31abSDavid du Colombier  * random variation.  So the index disk space should be 5% of the arena disk space.
20368c31abSDavid du Colombier  */
21368c31abSDavid du Colombier 
22368c31abSDavid du Colombier #include "stdinc.h"
23368c31abSDavid du Colombier #include "dat.h"
24368c31abSDavid du Colombier #include "fns.h"
25368c31abSDavid du Colombier 
26368c31abSDavid du Colombier static int	initindex1(Index*);
27368c31abSDavid du Colombier static ISect	*initisect1(ISect *is);
28368c31abSDavid du Colombier 
29368c31abSDavid du Colombier #define KEY(k,d)	((d) ? (k)>>(32-(d)) : 0)
30368c31abSDavid du Colombier 
31368c31abSDavid du Colombier static char IndexMagic[] = "venti index configuration";
32368c31abSDavid du Colombier 
33368c31abSDavid du Colombier Index*
initindex(char * name,ISect ** sects,int n)34368c31abSDavid du Colombier initindex(char *name, ISect **sects, int n)
35368c31abSDavid du Colombier {
36368c31abSDavid du Colombier 	IFile f;
37368c31abSDavid du Colombier 	Index *ix;
38368c31abSDavid du Colombier 	ISect *is;
39368c31abSDavid du Colombier 	u32int last, blocksize, tabsize;
40368c31abSDavid du Colombier 	int i;
41368c31abSDavid du Colombier 
42368c31abSDavid du Colombier 	if(n <= 0){
43368c31abSDavid du Colombier fprint(2, "bad n\n");
44368c31abSDavid du Colombier 		seterr(EOk, "no index sections to initialize index");
45368c31abSDavid du Colombier 		return nil;
46368c31abSDavid du Colombier 	}
47368c31abSDavid du Colombier 	ix = MKZ(Index);
48368c31abSDavid du Colombier 	if(ix == nil){
49368c31abSDavid du Colombier fprint(2, "no mem\n");
50368c31abSDavid du Colombier 		seterr(EOk, "can't initialize index: out of memory");
51368c31abSDavid du Colombier 		freeindex(ix);
52368c31abSDavid du Colombier 		return nil;
53368c31abSDavid du Colombier 	}
54368c31abSDavid du Colombier 
55368c31abSDavid du Colombier 	tabsize = sects[0]->tabsize;
56368c31abSDavid du Colombier 	if(partifile(&f, sects[0]->part, sects[0]->tabbase, tabsize) < 0)
57368c31abSDavid du Colombier 		return nil;
58368c31abSDavid du Colombier 	if(parseindex(&f, ix) < 0){
59368c31abSDavid du Colombier 		freeifile(&f);
60368c31abSDavid du Colombier 		freeindex(ix);
61368c31abSDavid du Colombier 		return nil;
62368c31abSDavid du Colombier 	}
63368c31abSDavid du Colombier 	freeifile(&f);
64368c31abSDavid du Colombier 	if(namecmp(ix->name, name) != 0){
65368c31abSDavid du Colombier 		seterr(ECorrupt, "mismatched index name: found %s expected %s", ix->name, name);
66368c31abSDavid du Colombier 		return nil;
67368c31abSDavid du Colombier 	}
68368c31abSDavid du Colombier 	if(ix->nsects != n){
69368c31abSDavid du Colombier 		seterr(ECorrupt, "mismatched number index sections: found %d expected %d", n, ix->nsects);
70368c31abSDavid du Colombier 		freeindex(ix);
71368c31abSDavid du Colombier 		return nil;
72368c31abSDavid du Colombier 	}
73368c31abSDavid du Colombier 	ix->sects = sects;
74368c31abSDavid du Colombier 	last = 0;
75368c31abSDavid du Colombier 	blocksize = ix->blocksize;
76368c31abSDavid du Colombier 	for(i = 0; i < ix->nsects; i++){
77368c31abSDavid du Colombier 		is = sects[i];
78368c31abSDavid du Colombier 		if(namecmp(ix->name, is->index) != 0
79368c31abSDavid du Colombier 		|| is->blocksize != blocksize
80368c31abSDavid du Colombier 		|| is->tabsize != tabsize
81368c31abSDavid du Colombier 		|| namecmp(is->name, ix->smap[i].name) != 0
82368c31abSDavid du Colombier 		|| is->start != ix->smap[i].start
83368c31abSDavid du Colombier 		|| is->stop != ix->smap[i].stop
84368c31abSDavid du Colombier 		|| last != is->start
85368c31abSDavid du Colombier 		|| is->start > is->stop){
86368c31abSDavid du Colombier 			seterr(ECorrupt, "inconsistent index sections in %s", ix->name);
87368c31abSDavid du Colombier 			freeindex(ix);
88368c31abSDavid du Colombier 			return nil;
89368c31abSDavid du Colombier 		}
90368c31abSDavid du Colombier 		last = is->stop;
91368c31abSDavid du Colombier 	}
92368c31abSDavid du Colombier 	ix->tabsize = tabsize;
93368c31abSDavid du Colombier 	ix->buckets = last;
94368c31abSDavid du Colombier 
95368c31abSDavid du Colombier 	if(initindex1(ix) < 0){
96368c31abSDavid du Colombier 		freeindex(ix);
97368c31abSDavid du Colombier 		return nil;
98368c31abSDavid du Colombier 	}
99368c31abSDavid du Colombier 
100368c31abSDavid du Colombier 	ix->arenas = MKNZ(Arena*, ix->narenas);
101368c31abSDavid du Colombier 	if(maparenas(ix->amap, ix->arenas, ix->narenas, ix->name) < 0){
102368c31abSDavid du Colombier 		freeindex(ix);
103368c31abSDavid du Colombier 		return nil;
104368c31abSDavid du Colombier 	}
105368c31abSDavid du Colombier 
106368c31abSDavid du Colombier 	return ix;
107368c31abSDavid du Colombier }
108368c31abSDavid du Colombier 
109368c31abSDavid du Colombier static int
initindex1(Index * ix)110368c31abSDavid du Colombier initindex1(Index *ix)
111368c31abSDavid du Colombier {
112368c31abSDavid du Colombier 	u32int buckets;
113368c31abSDavid du Colombier 
114368c31abSDavid du Colombier 	ix->div = (((u64int)1 << 32) + ix->buckets - 1) / ix->buckets;
115368c31abSDavid du Colombier 	buckets = (((u64int)1 << 32) - 1) / ix->div + 1;
116368c31abSDavid du Colombier 	if(buckets != ix->buckets){
117368c31abSDavid du Colombier 		seterr(ECorrupt, "inconsistent math for divisor and buckets in %s", ix->name);
118368c31abSDavid du Colombier 		return -1;
119368c31abSDavid du Colombier 	}
120368c31abSDavid du Colombier 
121368c31abSDavid du Colombier 	return 0;
122368c31abSDavid du Colombier }
123368c31abSDavid du Colombier 
124368c31abSDavid du Colombier int
wbindex(Index * ix)125368c31abSDavid du Colombier wbindex(Index *ix)
126368c31abSDavid du Colombier {
127368c31abSDavid du Colombier 	Fmt f;
128368c31abSDavid du Colombier 	ZBlock *b;
129368c31abSDavid du Colombier 	int i;
130368c31abSDavid du Colombier 
131368c31abSDavid du Colombier 	if(ix->nsects == 0){
132368c31abSDavid du Colombier 		seterr(EOk, "no sections in index %s", ix->name);
133368c31abSDavid du Colombier 		return -1;
134368c31abSDavid du Colombier 	}
135368c31abSDavid du Colombier 	b = alloczblock(ix->tabsize, 1, ix->blocksize);
136368c31abSDavid du Colombier 	if(b == nil){
137368c31abSDavid du Colombier 		seterr(EOk, "can't write index configuration: out of memory");
138368c31abSDavid du Colombier 		return -1;
139368c31abSDavid du Colombier 	}
140368c31abSDavid du Colombier 	fmtzbinit(&f, b);
141368c31abSDavid du Colombier 	if(outputindex(&f, ix) < 0){
142368c31abSDavid du Colombier 		seterr(EOk, "can't make index configuration: table storage too small %d", ix->tabsize);
143368c31abSDavid du Colombier 		freezblock(b);
144368c31abSDavid du Colombier 		return -1;
145368c31abSDavid du Colombier 	}
146368c31abSDavid du Colombier 	for(i = 0; i < ix->nsects; i++){
147368c31abSDavid du Colombier 		if(writepart(ix->sects[i]->part, ix->sects[i]->tabbase, b->data, ix->tabsize) < 0
148368c31abSDavid du Colombier 		|| flushpart(ix->sects[i]->part) < 0){
149368c31abSDavid du Colombier 			seterr(EOk, "can't write index: %r");
150368c31abSDavid du Colombier 			freezblock(b);
151368c31abSDavid du Colombier 			return -1;
152368c31abSDavid du Colombier 		}
153368c31abSDavid du Colombier 	}
154368c31abSDavid du Colombier 	freezblock(b);
155368c31abSDavid du Colombier 
156368c31abSDavid du Colombier 	for(i = 0; i < ix->nsects; i++)
157368c31abSDavid du Colombier 		if(wbisect(ix->sects[i]) < 0)
158368c31abSDavid du Colombier 			return -1;
159368c31abSDavid du Colombier 
160368c31abSDavid du Colombier 	return 0;
161368c31abSDavid du Colombier }
162368c31abSDavid du Colombier 
163368c31abSDavid du Colombier /*
164368c31abSDavid du Colombier  * index: IndexMagic '\n' version '\n' name '\n' blocksize '\n' [V2: bitblocks '\n'] sections arenas
165368c31abSDavid du Colombier  * version, blocksize: u32int
166368c31abSDavid du Colombier  * name: max. ANameSize string
167368c31abSDavid du Colombier  * sections, arenas: AMap
168368c31abSDavid du Colombier  */
169368c31abSDavid du Colombier int
outputindex(Fmt * f,Index * ix)170368c31abSDavid du Colombier outputindex(Fmt *f, Index *ix)
171368c31abSDavid du Colombier {
172368c31abSDavid du Colombier 	if(fmtprint(f, "%s\n%ud\n%s\n%ud\n", IndexMagic, ix->version, ix->name, ix->blocksize) < 0
173368c31abSDavid du Colombier 	|| outputamap(f, ix->smap, ix->nsects) < 0
174368c31abSDavid du Colombier 	|| outputamap(f, ix->amap, ix->narenas) < 0)
175368c31abSDavid du Colombier 		return -1;
176368c31abSDavid du Colombier 	return 0;
177368c31abSDavid du Colombier }
178368c31abSDavid du Colombier 
179368c31abSDavid du Colombier int
parseindex(IFile * f,Index * ix)180368c31abSDavid du Colombier parseindex(IFile *f, Index *ix)
181368c31abSDavid du Colombier {
182368c31abSDavid du Colombier 	AMapN amn;
183368c31abSDavid du Colombier 	u32int v;
184368c31abSDavid du Colombier 	char *s;
185368c31abSDavid du Colombier 
186368c31abSDavid du Colombier 	/*
187368c31abSDavid du Colombier 	 * magic
188368c31abSDavid du Colombier 	 */
189368c31abSDavid du Colombier 	s = ifileline(f);
190368c31abSDavid du Colombier 	if(s == nil || strcmp(s, IndexMagic) != 0){
191368c31abSDavid du Colombier 		seterr(ECorrupt, "bad index magic for %s", f->name);
192368c31abSDavid du Colombier 		return -1;
193368c31abSDavid du Colombier 	}
194368c31abSDavid du Colombier 
195368c31abSDavid du Colombier 	/*
196368c31abSDavid du Colombier 	 * version
197368c31abSDavid du Colombier 	 */
198368c31abSDavid du Colombier 	if(ifileu32int(f, &v) < 0){
199368c31abSDavid du Colombier 		seterr(ECorrupt, "syntax error: bad version number in %s", f->name);
200368c31abSDavid du Colombier 		return -1;
201368c31abSDavid du Colombier 	}
202368c31abSDavid du Colombier 	ix->version = v;
203368c31abSDavid du Colombier 	if(ix->version != IndexVersion){
204368c31abSDavid du Colombier 		seterr(ECorrupt, "bad version number in %s", f->name);
205368c31abSDavid du Colombier 		return -1;
206368c31abSDavid du Colombier 	}
207368c31abSDavid du Colombier 
208368c31abSDavid du Colombier 	/*
209368c31abSDavid du Colombier 	 * name
210368c31abSDavid du Colombier 	 */
211368c31abSDavid du Colombier 	if(ifilename(f, ix->name) < 0){
212368c31abSDavid du Colombier 		seterr(ECorrupt, "syntax error: bad index name in %s", f->name);
213368c31abSDavid du Colombier 		return -1;
214368c31abSDavid du Colombier 	}
215368c31abSDavid du Colombier 
216368c31abSDavid du Colombier 	/*
217368c31abSDavid du Colombier 	 * block size
218368c31abSDavid du Colombier 	 */
219368c31abSDavid du Colombier 	if(ifileu32int(f, &v) < 0){
220368c31abSDavid du Colombier 		seterr(ECorrupt, "syntax error: bad block size number in %s", f->name);
221368c31abSDavid du Colombier 		return -1;
222368c31abSDavid du Colombier 	}
223368c31abSDavid du Colombier 	ix->blocksize = v;
224368c31abSDavid du Colombier 
225368c31abSDavid du Colombier 	if(parseamap(f, &amn) < 0)
226368c31abSDavid du Colombier 		return -1;
227368c31abSDavid du Colombier 	ix->nsects = amn.n;
228368c31abSDavid du Colombier 	ix->smap = amn.map;
229368c31abSDavid du Colombier 
230368c31abSDavid du Colombier 	if(parseamap(f, &amn) < 0)
231368c31abSDavid du Colombier 		return -1;
232368c31abSDavid du Colombier 	ix->narenas = amn.n;
233368c31abSDavid du Colombier 	ix->amap = amn.map;
234368c31abSDavid du Colombier 
235368c31abSDavid du Colombier 	return 0;
236368c31abSDavid du Colombier }
237368c31abSDavid du Colombier 
238368c31abSDavid du Colombier /*
239368c31abSDavid du Colombier  * initialize an entirely new index
240368c31abSDavid du Colombier  */
241368c31abSDavid du Colombier Index *
newindex(char * name,ISect ** sects,int n)242368c31abSDavid du Colombier newindex(char *name, ISect **sects, int n)
243368c31abSDavid du Colombier {
244368c31abSDavid du Colombier 	Index *ix;
245368c31abSDavid du Colombier 	AMap *smap;
246368c31abSDavid du Colombier 	u64int nb;
247368c31abSDavid du Colombier 	u32int div, ub, xb, start, stop, blocksize, tabsize;
248368c31abSDavid du Colombier 	int i, j;
249368c31abSDavid du Colombier 
250368c31abSDavid du Colombier 	if(n < 1){
251368c31abSDavid du Colombier 		seterr(EOk, "creating index with no index sections");
252368c31abSDavid du Colombier 		return nil;
253368c31abSDavid du Colombier 	}
254368c31abSDavid du Colombier 
255368c31abSDavid du Colombier 	/*
256368c31abSDavid du Colombier 	 * compute the total buckets available in the index,
257368c31abSDavid du Colombier 	 * and the total buckets which are used.
258368c31abSDavid du Colombier 	 */
259368c31abSDavid du Colombier 	nb = 0;
260368c31abSDavid du Colombier 	blocksize = sects[0]->blocksize;
261368c31abSDavid du Colombier 	tabsize = sects[0]->tabsize;
262368c31abSDavid du Colombier 	for(i = 0; i < n; i++){
263368c31abSDavid du Colombier 		/*
264368c31abSDavid du Colombier 		 * allow index, start, and stop to be set if index is correct
265368c31abSDavid du Colombier 		 * and start and stop are what we would have picked.
266368c31abSDavid du Colombier 		 * this allows calling fmtindex to reformat the index after
267368c31abSDavid du Colombier 		 * replacing a bad index section with a freshly formatted one.
268368c31abSDavid du Colombier 		 * start and stop are checked below.
269368c31abSDavid du Colombier 		 */
270368c31abSDavid du Colombier 		if(sects[i]->index[0] != '\0' && strcmp(sects[i]->index, name) != 0){
271368c31abSDavid du Colombier 			seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
272368c31abSDavid du Colombier 			return nil;
273368c31abSDavid du Colombier 		}
274368c31abSDavid du Colombier 		if(blocksize != sects[i]->blocksize){
275368c31abSDavid du Colombier 			seterr(EOk, "mismatched block sizes in index sections");
276368c31abSDavid du Colombier 			return nil;
277368c31abSDavid du Colombier 		}
278368c31abSDavid du Colombier 		if(tabsize != sects[i]->tabsize){
279368c31abSDavid du Colombier 			seterr(EOk, "mismatched config table sizes in index sections");
280368c31abSDavid du Colombier 			return nil;
281368c31abSDavid du Colombier 		}
282368c31abSDavid du Colombier 		nb += sects[i]->blocks;
283368c31abSDavid du Colombier 	}
284368c31abSDavid du Colombier 
285368c31abSDavid du Colombier 	/*
286368c31abSDavid du Colombier 	 * check for duplicate names
287368c31abSDavid du Colombier 	 */
288368c31abSDavid du Colombier 	for(i = 0; i < n; i++){
289368c31abSDavid du Colombier 		for(j = i + 1; j < n; j++){
290368c31abSDavid du Colombier 			if(namecmp(sects[i]->name, sects[j]->name) == 0){
291368c31abSDavid du Colombier 				seterr(EOk, "duplicate section name %s for index %s", sects[i]->name, name);
292368c31abSDavid du Colombier 				return nil;
293368c31abSDavid du Colombier 			}
294368c31abSDavid du Colombier 		}
295368c31abSDavid du Colombier 	}
296368c31abSDavid du Colombier 
297368c31abSDavid du Colombier 	if(nb >= ((u64int)1 << 32)){
298*92b836f4SDavid du Colombier 		fprint(2, "%s: index is 2^32 blocks or more; ignoring some of it\n",
299*92b836f4SDavid du Colombier 			argv0);
300*92b836f4SDavid du Colombier 		nb = ((u64int)1 << 32) - 1;
301368c31abSDavid du Colombier 	}
302368c31abSDavid du Colombier 
303368c31abSDavid du Colombier 	div = (((u64int)1 << 32) + nb - 1) / nb;
304368c31abSDavid du Colombier 	if(div < 100){
305*92b836f4SDavid du Colombier 		fprint(2, "%s: index divisor %d too coarse; "
306*92b836f4SDavid du Colombier 			"index larger than needed, ignoring some of it\n",
307*92b836f4SDavid du Colombier 			argv0, div);
308*92b836f4SDavid du Colombier 		div = 100;
309*92b836f4SDavid du Colombier 		nb = (((u64int)1 << 32) - 1) / (100 - 1);
310368c31abSDavid du Colombier 	}
311*92b836f4SDavid du Colombier 	ub = (((u64int)1 << 32) - 1) / div + 1;
312368c31abSDavid du Colombier 	if(ub > nb){
313368c31abSDavid du Colombier 		seterr(EBug, "index initialization math wrong");
314368c31abSDavid du Colombier 		return nil;
315368c31abSDavid du Colombier 	}
316368c31abSDavid du Colombier 	xb = nb - ub;
317368c31abSDavid du Colombier 
318368c31abSDavid du Colombier 	/*
319368c31abSDavid du Colombier 	 * initialize each of the index sections
320368c31abSDavid du Colombier 	 * and the section map table
321368c31abSDavid du Colombier 	 */
322368c31abSDavid du Colombier 	smap = MKNZ(AMap, n);
323368c31abSDavid du Colombier 	if(smap == nil){
324368c31abSDavid du Colombier 		seterr(EOk, "can't create new index: out of memory");
325368c31abSDavid du Colombier 		return nil;
326368c31abSDavid du Colombier 	}
327368c31abSDavid du Colombier 	start = 0;
328368c31abSDavid du Colombier 	for(i = 0; i < n; i++){
329368c31abSDavid du Colombier 		stop = start + sects[i]->blocks - xb / n;
330368c31abSDavid du Colombier 		if(i == n - 1)
331368c31abSDavid du Colombier 			stop = ub;
332368c31abSDavid du Colombier 
333368c31abSDavid du Colombier 		if(sects[i]->start != 0 || sects[i]->stop != 0)
334368c31abSDavid du Colombier 		if(sects[i]->start != start || sects[i]->stop != stop){
335368c31abSDavid du Colombier 			seterr(EOk, "creating new index using non-empty section %s", sects[i]->name);
336368c31abSDavid du Colombier 			return nil;
337368c31abSDavid du Colombier 		}
338368c31abSDavid du Colombier 
339368c31abSDavid du Colombier 		sects[i]->start = start;
340368c31abSDavid du Colombier 		sects[i]->stop = stop;
341368c31abSDavid du Colombier 		namecp(sects[i]->index, name);
342368c31abSDavid du Colombier 
343368c31abSDavid du Colombier 		smap[i].start = start;
344368c31abSDavid du Colombier 		smap[i].stop = stop;
345368c31abSDavid du Colombier 		namecp(smap[i].name, sects[i]->name);
346368c31abSDavid du Colombier 		start = stop;
347368c31abSDavid du Colombier 	}
348368c31abSDavid du Colombier 
349368c31abSDavid du Colombier 	/*
350368c31abSDavid du Colombier 	 * initialize the index itself
351368c31abSDavid du Colombier 	 */
352368c31abSDavid du Colombier 	ix = MKZ(Index);
353368c31abSDavid du Colombier 	if(ix == nil){
354368c31abSDavid du Colombier 		seterr(EOk, "can't create new index: out of memory");
355368c31abSDavid du Colombier 		free(smap);
356368c31abSDavid du Colombier 		return nil;
357368c31abSDavid du Colombier 	}
358368c31abSDavid du Colombier 	ix->version = IndexVersion;
359368c31abSDavid du Colombier 	namecp(ix->name, name);
360368c31abSDavid du Colombier 	ix->sects = sects;
361368c31abSDavid du Colombier 	ix->smap = smap;
362368c31abSDavid du Colombier 	ix->nsects = n;
363368c31abSDavid du Colombier 	ix->blocksize = blocksize;
364368c31abSDavid du Colombier 	ix->buckets = ub;
365368c31abSDavid du Colombier 	ix->tabsize = tabsize;
366368c31abSDavid du Colombier 	ix->div = div;
367368c31abSDavid du Colombier 
368368c31abSDavid du Colombier 	if(initindex1(ix) < 0){
369368c31abSDavid du Colombier 		free(smap);
370368c31abSDavid du Colombier 		return nil;
371368c31abSDavid du Colombier 	}
372368c31abSDavid du Colombier 
373368c31abSDavid du Colombier 	return ix;
374368c31abSDavid du Colombier }
375368c31abSDavid du Colombier 
376368c31abSDavid du Colombier ISect*
initisect(Part * part)377368c31abSDavid du Colombier initisect(Part *part)
378368c31abSDavid du Colombier {
379368c31abSDavid du Colombier 	ISect *is;
380368c31abSDavid du Colombier 	ZBlock *b;
381368c31abSDavid du Colombier 	int ok;
382368c31abSDavid du Colombier 
383368c31abSDavid du Colombier 	b = alloczblock(HeadSize, 0, 0);
384368c31abSDavid du Colombier 	if(b == nil || readpart(part, PartBlank, b->data, HeadSize) < 0){
385368c31abSDavid du Colombier 		seterr(EAdmin, "can't read index section header: %r");
386368c31abSDavid du Colombier 		return nil;
387368c31abSDavid du Colombier 	}
388368c31abSDavid du Colombier 
389368c31abSDavid du Colombier 	is = MKZ(ISect);
390368c31abSDavid du Colombier 	if(is == nil){
391368c31abSDavid du Colombier 		freezblock(b);
392368c31abSDavid du Colombier 		return nil;
393368c31abSDavid du Colombier 	}
394368c31abSDavid du Colombier 	is->part = part;
395368c31abSDavid du Colombier 	ok = unpackisect(is, b->data);
396368c31abSDavid du Colombier 	freezblock(b);
397368c31abSDavid du Colombier 	if(ok < 0){
398368c31abSDavid du Colombier 		seterr(ECorrupt, "corrupted index section header: %r");
399368c31abSDavid du Colombier 		freeisect(is);
400368c31abSDavid du Colombier 		return nil;
401368c31abSDavid du Colombier 	}
402368c31abSDavid du Colombier 
403368c31abSDavid du Colombier 	if(is->version != ISectVersion1 && is->version != ISectVersion2){
404368c31abSDavid du Colombier 		seterr(EAdmin, "unknown index section version %d", is->version);
405368c31abSDavid du Colombier 		freeisect(is);
406368c31abSDavid du Colombier 		return nil;
407368c31abSDavid du Colombier 	}
408368c31abSDavid du Colombier 
409368c31abSDavid du Colombier 	return initisect1(is);
410368c31abSDavid du Colombier }
411368c31abSDavid du Colombier 
412368c31abSDavid du Colombier ISect*
newisect(Part * part,u32int vers,char * name,u32int blocksize,u32int tabsize)413368c31abSDavid du Colombier newisect(Part *part, u32int vers, char *name, u32int blocksize, u32int tabsize)
414368c31abSDavid du Colombier {
415368c31abSDavid du Colombier 	ISect *is;
416368c31abSDavid du Colombier 	u32int tabbase;
417368c31abSDavid du Colombier 
418368c31abSDavid du Colombier 	is = MKZ(ISect);
419368c31abSDavid du Colombier 	if(is == nil)
420368c31abSDavid du Colombier 		return nil;
421368c31abSDavid du Colombier 
422368c31abSDavid du Colombier 	namecp(is->name, name);
423368c31abSDavid du Colombier 	is->version = vers;
424368c31abSDavid du Colombier 	is->part = part;
425368c31abSDavid du Colombier 	is->blocksize = blocksize;
426368c31abSDavid du Colombier 	is->start = 0;
427368c31abSDavid du Colombier 	is->stop = 0;
428368c31abSDavid du Colombier 	tabbase = (PartBlank + HeadSize + blocksize - 1) & ~(blocksize - 1);
429368c31abSDavid du Colombier 	is->blockbase = (tabbase + tabsize + blocksize - 1) & ~(blocksize - 1);
430368c31abSDavid du Colombier 	is->blocks = is->part->size / blocksize - is->blockbase / blocksize;
431368c31abSDavid du Colombier 	is->bucketmagic = 0;
432368c31abSDavid du Colombier 	if(is->version == ISectVersion2){
433368c31abSDavid du Colombier 		do{
434368c31abSDavid du Colombier 			is->bucketmagic = fastrand();
435368c31abSDavid du Colombier 		}while(is->bucketmagic==0);
436368c31abSDavid du Colombier 	}
437368c31abSDavid du Colombier 	is = initisect1(is);
438368c31abSDavid du Colombier 	if(is == nil)
439368c31abSDavid du Colombier 		return nil;
440368c31abSDavid du Colombier 
441368c31abSDavid du Colombier 	return is;
442368c31abSDavid du Colombier }
443368c31abSDavid du Colombier 
444368c31abSDavid du Colombier /*
445368c31abSDavid du Colombier  * initialize the computed parameters for an index
446368c31abSDavid du Colombier  */
447368c31abSDavid du Colombier static ISect*
initisect1(ISect * is)448368c31abSDavid du Colombier initisect1(ISect *is)
449368c31abSDavid du Colombier {
450368c31abSDavid du Colombier 	u64int v;
451368c31abSDavid du Colombier 
452368c31abSDavid du Colombier 	is->buckmax = (is->blocksize - IBucketSize) / IEntrySize;
453368c31abSDavid du Colombier 	is->blocklog = u64log2(is->blocksize);
454368c31abSDavid du Colombier 	if(is->blocksize != (1 << is->blocklog)){
455368c31abSDavid du Colombier 		seterr(ECorrupt, "illegal non-power-of-2 bucket size %d\n", is->blocksize);
456368c31abSDavid du Colombier 		freeisect(is);
457368c31abSDavid du Colombier 		return nil;
458368c31abSDavid du Colombier 	}
459368c31abSDavid du Colombier 	partblocksize(is->part, is->blocksize);
460368c31abSDavid du Colombier 	is->tabbase = (PartBlank + HeadSize + is->blocksize - 1) & ~(is->blocksize - 1);
461368c31abSDavid du Colombier 	if(is->tabbase >= is->blockbase){
462368c31abSDavid du Colombier 		seterr(ECorrupt, "index section config table overlaps bucket storage");
463368c31abSDavid du Colombier 		freeisect(is);
464368c31abSDavid du Colombier 		return nil;
465368c31abSDavid du Colombier 	}
466368c31abSDavid du Colombier 	is->tabsize = is->blockbase - is->tabbase;
467368c31abSDavid du Colombier 	v = is->part->size & ~(u64int)(is->blocksize - 1);
468368c31abSDavid du Colombier 	if(is->blockbase + (u64int)is->blocks * is->blocksize != v){
469368c31abSDavid du Colombier 		seterr(ECorrupt, "invalid blocks in index section %s", is->name);
470368c31abSDavid du Colombier 		/* ZZZ what to do?
471368c31abSDavid du Colombier 		freeisect(is);
472368c31abSDavid du Colombier 		return nil;
473368c31abSDavid du Colombier 		*/
474368c31abSDavid du Colombier 	}
475368c31abSDavid du Colombier 
476368c31abSDavid du Colombier 	if(is->stop - is->start > is->blocks){
477368c31abSDavid du Colombier 		seterr(ECorrupt, "index section overflows available space");
478368c31abSDavid du Colombier 		freeisect(is);
479368c31abSDavid du Colombier 		return nil;
480368c31abSDavid du Colombier 	}
481368c31abSDavid du Colombier 	if(is->start > is->stop){
482368c31abSDavid du Colombier 		seterr(ECorrupt, "invalid index section range");
483368c31abSDavid du Colombier 		freeisect(is);
484368c31abSDavid du Colombier 		return nil;
485368c31abSDavid du Colombier 	}
486368c31abSDavid du Colombier 
487368c31abSDavid du Colombier 	return is;
488368c31abSDavid du Colombier }
489368c31abSDavid du Colombier 
490368c31abSDavid du Colombier int
wbisect(ISect * is)491368c31abSDavid du Colombier wbisect(ISect *is)
492368c31abSDavid du Colombier {
493368c31abSDavid du Colombier 	ZBlock *b;
494368c31abSDavid du Colombier 
495368c31abSDavid du Colombier 	b = alloczblock(HeadSize, 1, 0);
496368c31abSDavid du Colombier 	if(b == nil){
497368c31abSDavid du Colombier 		/* ZZZ set error? */
498368c31abSDavid du Colombier 		return -1;
499368c31abSDavid du Colombier 	}
500368c31abSDavid du Colombier 
501368c31abSDavid du Colombier 	if(packisect(is, b->data) < 0){
502368c31abSDavid du Colombier 		seterr(ECorrupt, "can't make index section header: %r");
503368c31abSDavid du Colombier 		freezblock(b);
504368c31abSDavid du Colombier 		return -1;
505368c31abSDavid du Colombier 	}
506368c31abSDavid du Colombier 	if(writepart(is->part, PartBlank, b->data, HeadSize) < 0 || flushpart(is->part) < 0){
507368c31abSDavid du Colombier 		seterr(EAdmin, "can't write index section header: %r");
508368c31abSDavid du Colombier 		freezblock(b);
509368c31abSDavid du Colombier 		return -1;
510368c31abSDavid du Colombier 	}
511368c31abSDavid du Colombier 	freezblock(b);
512368c31abSDavid du Colombier 
513368c31abSDavid du Colombier 	return 0;
514368c31abSDavid du Colombier }
515368c31abSDavid du Colombier 
516368c31abSDavid du Colombier void
freeisect(ISect * is)517368c31abSDavid du Colombier freeisect(ISect *is)
518368c31abSDavid du Colombier {
519368c31abSDavid du Colombier 	if(is == nil)
520368c31abSDavid du Colombier 		return;
521368c31abSDavid du Colombier 	free(is);
522368c31abSDavid du Colombier }
523368c31abSDavid du Colombier 
524368c31abSDavid du Colombier void
freeindex(Index * ix)525368c31abSDavid du Colombier freeindex(Index *ix)
526368c31abSDavid du Colombier {
527368c31abSDavid du Colombier 	int i;
528368c31abSDavid du Colombier 
529368c31abSDavid du Colombier 	if(ix == nil)
530368c31abSDavid du Colombier 		return;
531368c31abSDavid du Colombier 	free(ix->amap);
532368c31abSDavid du Colombier 	free(ix->arenas);
533368c31abSDavid du Colombier 	if(ix->sects)
534368c31abSDavid du Colombier 		for(i = 0; i < ix->nsects; i++)
535368c31abSDavid du Colombier 			freeisect(ix->sects[i]);
536368c31abSDavid du Colombier 	free(ix->sects);
537368c31abSDavid du Colombier 	free(ix->smap);
538368c31abSDavid du Colombier 	free(ix);
539368c31abSDavid du Colombier }
540368c31abSDavid du Colombier 
541368c31abSDavid du Colombier /*
542368c31abSDavid du Colombier  * write a clump to an available arena in the index
543368c31abSDavid du Colombier  * and return the address of the clump within the index.
544368c31abSDavid du Colombier ZZZ question: should this distinguish between an arena
545368c31abSDavid du Colombier filling up and real errors writing the clump?
546368c31abSDavid du Colombier  */
547368c31abSDavid du Colombier u64int
writeiclump(Index * ix,Clump * c,u8int * clbuf)548f9e1cf08SDavid du Colombier writeiclump(Index *ix, Clump *c, u8int *clbuf)
549368c31abSDavid du Colombier {
550368c31abSDavid du Colombier 	u64int a;
551368c31abSDavid du Colombier 	int i;
552f9e1cf08SDavid du Colombier 	IAddr ia;
553f9e1cf08SDavid du Colombier 	AState as;
554368c31abSDavid du Colombier 
555368c31abSDavid du Colombier 	trace(TraceLump, "writeiclump enter");
556f9e1cf08SDavid du Colombier 	qlock(&ix->writing);
557368c31abSDavid du Colombier 	for(i = ix->mapalloc; i < ix->narenas; i++){
558f9e1cf08SDavid du Colombier 		a = writeaclump(ix->arenas[i], c, clbuf);
559368c31abSDavid du Colombier 		if(a != TWID64){
560f9e1cf08SDavid du Colombier 			ix->mapalloc = i;
561f9e1cf08SDavid du Colombier 			ia.addr = ix->amap[i].start + a;
562f9e1cf08SDavid du Colombier 			ia.type = c->info.type;
563f9e1cf08SDavid du Colombier 			ia.size = c->info.uncsize;
564f9e1cf08SDavid du Colombier 			ia.blocks = (c->info.size + ClumpSize + (1<<ABlockLog) - 1) >> ABlockLog;
565f9e1cf08SDavid du Colombier 			as.arena = ix->arenas[i];
566f9e1cf08SDavid du Colombier 			as.aa = ia.addr;
567f9e1cf08SDavid du Colombier 			as.stats = as.arena->memstats;
568f9e1cf08SDavid du Colombier 			insertscore(c->info.score, &ia, IEDirty, &as);
569f9e1cf08SDavid du Colombier 			qunlock(&ix->writing);
570368c31abSDavid du Colombier 			trace(TraceLump, "writeiclump exit");
571f9e1cf08SDavid du Colombier 			return ia.addr;
572368c31abSDavid du Colombier 		}
573368c31abSDavid du Colombier 	}
574f9e1cf08SDavid du Colombier 	qunlock(&ix->writing);
575368c31abSDavid du Colombier 
576368c31abSDavid du Colombier 	seterr(EAdmin, "no space left in arenas");
577368c31abSDavid du Colombier 	trace(TraceLump, "writeiclump failed");
578368c31abSDavid du Colombier 	return TWID64;
579368c31abSDavid du Colombier }
580368c31abSDavid du Colombier 
581368c31abSDavid du Colombier /*
582368c31abSDavid du Colombier  * convert an arena index to an relative arena address
583368c31abSDavid du Colombier  */
584368c31abSDavid du Colombier Arena*
amapitoa(Index * ix,u64int a,u64int * aa)585368c31abSDavid du Colombier amapitoa(Index *ix, u64int a, u64int *aa)
586368c31abSDavid du Colombier {
587368c31abSDavid du Colombier 	int i, r, l, m;
588368c31abSDavid du Colombier 
589368c31abSDavid du Colombier 	l = 1;
590368c31abSDavid du Colombier 	r = ix->narenas - 1;
591368c31abSDavid du Colombier 	while(l <= r){
592368c31abSDavid du Colombier 		m = (r + l) / 2;
593368c31abSDavid du Colombier 		if(ix->amap[m].start <= a)
594368c31abSDavid du Colombier 			l = m + 1;
595368c31abSDavid du Colombier 		else
596368c31abSDavid du Colombier 			r = m - 1;
597368c31abSDavid du Colombier 	}
598368c31abSDavid du Colombier 	l--;
599368c31abSDavid du Colombier 
600368c31abSDavid du Colombier 	if(a > ix->amap[l].stop){
601368c31abSDavid du Colombier for(i=0; i<ix->narenas; i++)
602368c31abSDavid du Colombier 	print("arena %d: %llux - %llux\n", i, ix->amap[i].start, ix->amap[i].stop);
603368c31abSDavid du Colombier print("want arena %d for %llux\n", l, a);
604368c31abSDavid du Colombier 		seterr(ECrash, "unmapped address passed to amapitoa");
605368c31abSDavid du Colombier 		return nil;
606368c31abSDavid du Colombier 	}
607368c31abSDavid du Colombier 
608368c31abSDavid du Colombier 	if(ix->arenas[l] == nil){
609368c31abSDavid du Colombier 		seterr(ECrash, "unmapped arena selected in amapitoa");
610368c31abSDavid du Colombier 		return nil;
611368c31abSDavid du Colombier 	}
612368c31abSDavid du Colombier 	*aa = a - ix->amap[l].start;
613368c31abSDavid du Colombier 	return ix->arenas[l];
614368c31abSDavid du Colombier }
615368c31abSDavid du Colombier 
616f9e1cf08SDavid du Colombier /*
617f9e1cf08SDavid du Colombier  * convert an arena index to the bounds of the containing arena group.
618f9e1cf08SDavid du Colombier  */
619f9e1cf08SDavid du Colombier Arena*
amapitoag(Index * ix,u64int a,u64int * gstart,u64int * glimit,int * g)620f9e1cf08SDavid du Colombier amapitoag(Index *ix, u64int a, u64int *gstart, u64int *glimit, int *g)
621f9e1cf08SDavid du Colombier {
622f9e1cf08SDavid du Colombier 	u64int aa;
623f9e1cf08SDavid du Colombier 	Arena *arena;
624f9e1cf08SDavid du Colombier 
625f9e1cf08SDavid du Colombier 	arena = amapitoa(ix, a, &aa);
626f9e1cf08SDavid du Colombier 	if(arena == nil)
627f9e1cf08SDavid du Colombier 		return nil;
628f9e1cf08SDavid du Colombier 	if(arenatog(arena, aa, gstart, glimit, g) < 0)
629f9e1cf08SDavid du Colombier 		return nil;
630f9e1cf08SDavid du Colombier 	*gstart += a - aa;
631f9e1cf08SDavid du Colombier 	*glimit += a - aa;
632f9e1cf08SDavid du Colombier 	return arena;
633f9e1cf08SDavid du Colombier }
634f9e1cf08SDavid du Colombier 
635368c31abSDavid du Colombier int
iaddrcmp(IAddr * ia1,IAddr * ia2)636368c31abSDavid du Colombier iaddrcmp(IAddr *ia1, IAddr *ia2)
637368c31abSDavid du Colombier {
638368c31abSDavid du Colombier 	return ia1->type != ia2->type
639368c31abSDavid du Colombier 		|| ia1->size != ia2->size
640368c31abSDavid du Colombier 		|| ia1->blocks != ia2->blocks
641368c31abSDavid du Colombier 		|| ia1->addr != ia2->addr;
642368c31abSDavid du Colombier }
643368c31abSDavid du Colombier 
644368c31abSDavid du Colombier /*
645368c31abSDavid du Colombier  * lookup the score in the partition
646368c31abSDavid du Colombier  *
647368c31abSDavid du Colombier  * nothing needs to be explicitly locked:
648368c31abSDavid du Colombier  * only static parts of ix are used, and
649368c31abSDavid du Colombier  * the bucket is locked by the DBlock lock.
650368c31abSDavid du Colombier  */
651368c31abSDavid du Colombier int
loadientry(Index * ix,u8int * score,int type,IEntry * ie)652368c31abSDavid du Colombier loadientry(Index *ix, u8int *score, int type, IEntry *ie)
653368c31abSDavid du Colombier {
654368c31abSDavid du Colombier 	ISect *is;
655368c31abSDavid du Colombier 	DBlock *b;
656368c31abSDavid du Colombier 	IBucket ib;
657368c31abSDavid du Colombier 	u32int buck;
658368c31abSDavid du Colombier 	int h, ok;
659368c31abSDavid du Colombier 
660368c31abSDavid du Colombier 	ok = -1;
661368c31abSDavid du Colombier 
662368c31abSDavid du Colombier 	trace(TraceLump, "loadientry enter");
663368c31abSDavid du Colombier 
664368c31abSDavid du Colombier 	/*
665368c31abSDavid du Colombier 	qlock(&stats.lock);
666368c31abSDavid du Colombier 	stats.indexreads++;
667368c31abSDavid du Colombier 	qunlock(&stats.lock);
668368c31abSDavid du Colombier 	*/
669368c31abSDavid du Colombier 
670368c31abSDavid du Colombier 	if(!inbloomfilter(mainindex->bloom, score)){
671368c31abSDavid du Colombier 		trace(TraceLump, "loadientry bloomhit");
672368c31abSDavid du Colombier 		return -1;
673368c31abSDavid du Colombier 	}
674368c31abSDavid du Colombier 
675368c31abSDavid du Colombier 	trace(TraceLump, "loadientry loadibucket");
676368c31abSDavid du Colombier 	b = loadibucket(ix, score, &is, &buck, &ib);
677368c31abSDavid du Colombier 	trace(TraceLump, "loadientry loadedibucket");
678368c31abSDavid du Colombier 	if(b == nil)
679368c31abSDavid du Colombier 		return -1;
680368c31abSDavid du Colombier 
681368c31abSDavid du Colombier 	if(okibucket(&ib, is) < 0){
682368c31abSDavid du Colombier 		trace(TraceLump, "loadientry badbucket");
683368c31abSDavid du Colombier 		goto out;
684368c31abSDavid du Colombier 	}
685368c31abSDavid du Colombier 
686368c31abSDavid du Colombier 	h = bucklook(score, type, ib.data, ib.n);
687368c31abSDavid du Colombier 	if(h & 1){
688368c31abSDavid du Colombier 		h ^= 1;
689368c31abSDavid du Colombier 		trace(TraceLump, "loadientry found");
690368c31abSDavid du Colombier 		unpackientry(ie, &ib.data[h]);
691368c31abSDavid du Colombier 		ok = 0;
692368c31abSDavid du Colombier 		goto out;
693368c31abSDavid du Colombier 	}
694368c31abSDavid du Colombier 	trace(TraceLump, "loadientry notfound");
695368c31abSDavid du Colombier 	addstat(StatBloomFalseMiss, 1);
696368c31abSDavid du Colombier out:
697368c31abSDavid du Colombier 	putdblock(b);
698368c31abSDavid du Colombier 	trace(TraceLump, "loadientry exit");
699368c31abSDavid du Colombier 	return ok;
700368c31abSDavid du Colombier }
701368c31abSDavid du Colombier 
702368c31abSDavid du Colombier int
okibucket(IBucket * ib,ISect * is)703368c31abSDavid du Colombier okibucket(IBucket *ib, ISect *is)
704368c31abSDavid du Colombier {
705368c31abSDavid du Colombier 	if(ib->n <= is->buckmax)
706368c31abSDavid du Colombier 		return 0;
707368c31abSDavid du Colombier 
708368c31abSDavid du Colombier 	seterr(EICorrupt, "corrupted disk index bucket: n=%ud max=%ud, range=[%lud,%lud)",
709368c31abSDavid du Colombier 		ib->n, is->buckmax, is->start, is->stop);
710368c31abSDavid du Colombier 	return -1;
711368c31abSDavid du Colombier }
712368c31abSDavid du Colombier 
713368c31abSDavid du Colombier /*
714368c31abSDavid du Colombier  * look for score within data;
715368c31abSDavid du Colombier  * return 1 | byte index of matching index,
716368c31abSDavid du Colombier  * or 0 | index of least element > score
717368c31abSDavid du Colombier  */
718368c31abSDavid du Colombier int
bucklook(u8int * score,int otype,u8int * data,int n)719368c31abSDavid du Colombier bucklook(u8int *score, int otype, u8int *data, int n)
720368c31abSDavid du Colombier {
721368c31abSDavid du Colombier 	int i, r, l, m, h, c, cc, type;
722368c31abSDavid du Colombier 
723368c31abSDavid du Colombier 	if(otype == -1)
724368c31abSDavid du Colombier 		type = -1;
725368c31abSDavid du Colombier 	else
726368c31abSDavid du Colombier 		type = vttodisktype(otype);
727368c31abSDavid du Colombier 	l = 0;
728368c31abSDavid du Colombier 	r = n - 1;
729368c31abSDavid du Colombier 	while(l <= r){
730368c31abSDavid du Colombier 		m = (r + l) >> 1;
731368c31abSDavid du Colombier 		h = m * IEntrySize;
732368c31abSDavid du Colombier 		for(i = 0; i < VtScoreSize; i++){
733368c31abSDavid du Colombier 			c = score[i];
734368c31abSDavid du Colombier 			cc = data[h + i];
735368c31abSDavid du Colombier 			if(c != cc){
736368c31abSDavid du Colombier 				if(c > cc)
737368c31abSDavid du Colombier 					l = m + 1;
738368c31abSDavid du Colombier 				else
739368c31abSDavid du Colombier 					r = m - 1;
740368c31abSDavid du Colombier 				goto cont;
741368c31abSDavid du Colombier 			}
742368c31abSDavid du Colombier 		}
743368c31abSDavid du Colombier 		cc = data[h + IEntryTypeOff];
744368c31abSDavid du Colombier 		if(type != cc && type != -1){
745368c31abSDavid du Colombier 			if(type > cc)
746368c31abSDavid du Colombier 				l = m + 1;
747368c31abSDavid du Colombier 			else
748368c31abSDavid du Colombier 				r = m - 1;
749368c31abSDavid du Colombier 			goto cont;
750368c31abSDavid du Colombier 		}
751368c31abSDavid du Colombier 		return h | 1;
752368c31abSDavid du Colombier 	cont:;
753368c31abSDavid du Colombier 	}
754368c31abSDavid du Colombier 
755368c31abSDavid du Colombier 	return l * IEntrySize;
756368c31abSDavid du Colombier }
757368c31abSDavid du Colombier 
758368c31abSDavid du Colombier /*
759368c31abSDavid du Colombier  * compare two IEntries; consistent with bucklook
760368c31abSDavid du Colombier  */
761368c31abSDavid du Colombier int
ientrycmp(const void * vie1,const void * vie2)762368c31abSDavid du Colombier ientrycmp(const void *vie1, const void *vie2)
763368c31abSDavid du Colombier {
764368c31abSDavid du Colombier 	u8int *ie1, *ie2;
765368c31abSDavid du Colombier 	int i, v1, v2;
766368c31abSDavid du Colombier 
767368c31abSDavid du Colombier 	ie1 = (u8int*)vie1;
768368c31abSDavid du Colombier 	ie2 = (u8int*)vie2;
769368c31abSDavid du Colombier 	for(i = 0; i < VtScoreSize; i++){
770368c31abSDavid du Colombier 		v1 = ie1[i];
771368c31abSDavid du Colombier 		v2 = ie2[i];
772368c31abSDavid du Colombier 		if(v1 != v2){
773368c31abSDavid du Colombier 			if(v1 < v2)
774368c31abSDavid du Colombier 				return -1;
775368c31abSDavid du Colombier 			return 1;
776368c31abSDavid du Colombier 		}
777368c31abSDavid du Colombier 	}
778368c31abSDavid du Colombier 	v1 = ie1[IEntryTypeOff];
779368c31abSDavid du Colombier 	v2 = ie2[IEntryTypeOff];
780368c31abSDavid du Colombier 	if(v1 != v2){
781368c31abSDavid du Colombier 		if(v1 < v2)
782368c31abSDavid du Colombier 			return -1;
783368c31abSDavid du Colombier 		return 1;
784368c31abSDavid du Colombier 	}
785368c31abSDavid du Colombier 	return 0;
786368c31abSDavid du Colombier }
787368c31abSDavid du Colombier 
788368c31abSDavid du Colombier /*
789368c31abSDavid du Colombier  * find the number of the index section holding bucket #buck
790368c31abSDavid du Colombier  */
791368c31abSDavid du Colombier int
indexsect0(Index * ix,u32int buck)792368c31abSDavid du Colombier indexsect0(Index *ix, u32int buck)
793368c31abSDavid du Colombier {
794368c31abSDavid du Colombier 	int r, l, m;
795368c31abSDavid du Colombier 
796368c31abSDavid du Colombier 	l = 1;
797368c31abSDavid du Colombier 	r = ix->nsects - 1;
798368c31abSDavid du Colombier 	while(l <= r){
799368c31abSDavid du Colombier 		m = (r + l) >> 1;
800368c31abSDavid du Colombier 		if(ix->sects[m]->start <= buck)
801368c31abSDavid du Colombier 			l = m + 1;
802368c31abSDavid du Colombier 		else
803368c31abSDavid du Colombier 			r = m - 1;
804368c31abSDavid du Colombier 	}
805368c31abSDavid du Colombier 	return l - 1;
806368c31abSDavid du Colombier }
807368c31abSDavid du Colombier 
808368c31abSDavid du Colombier /*
809368c31abSDavid du Colombier  * load the index block at bucket #buck
810368c31abSDavid du Colombier  */
811368c31abSDavid du Colombier static DBlock*
loadibucket0(Index * ix,u32int buck,ISect ** pis,u32int * pbuck,IBucket * ib,int mode)812368c31abSDavid du Colombier loadibucket0(Index *ix, u32int buck, ISect **pis, u32int *pbuck, IBucket *ib, int mode)
813368c31abSDavid du Colombier {
814368c31abSDavid du Colombier 	ISect *is;
815368c31abSDavid du Colombier 	DBlock *b;
816368c31abSDavid du Colombier 
817368c31abSDavid du Colombier 	is = ix->sects[indexsect0(ix, buck)];
818368c31abSDavid du Colombier 	if(buck < is->start || is->stop <= buck){
819368c31abSDavid du Colombier 		seterr(EAdmin, "index lookup out of range: %ud not found in index\n", buck);
820368c31abSDavid du Colombier 		return nil;
821368c31abSDavid du Colombier 	}
822368c31abSDavid du Colombier 
823368c31abSDavid du Colombier 	buck -= is->start;
824368c31abSDavid du Colombier 	if((b = getdblock(is->part, is->blockbase + ((u64int)buck << is->blocklog), mode)) == nil)
825368c31abSDavid du Colombier 		return nil;
826368c31abSDavid du Colombier 
827368c31abSDavid du Colombier 	if(pis)
828368c31abSDavid du Colombier 		*pis = is;
829368c31abSDavid du Colombier 	if(pbuck)
830368c31abSDavid du Colombier 		*pbuck = buck;
831368c31abSDavid du Colombier 	if(ib)
832368c31abSDavid du Colombier 		unpackibucket(ib, b->data, is->bucketmagic);
833368c31abSDavid du Colombier 	return b;
834368c31abSDavid du Colombier }
835368c31abSDavid du Colombier 
836368c31abSDavid du Colombier /*
837368c31abSDavid du Colombier  * find the number of the index section holding score
838368c31abSDavid du Colombier  */
839368c31abSDavid du Colombier int
indexsect1(Index * ix,u8int * score)840368c31abSDavid du Colombier indexsect1(Index *ix, u8int *score)
841368c31abSDavid du Colombier {
842368c31abSDavid du Colombier 	return indexsect0(ix, hashbits(score, 32) / ix->div);
843368c31abSDavid du Colombier }
844368c31abSDavid du Colombier 
845368c31abSDavid du Colombier /*
846368c31abSDavid du Colombier  * load the index block responsible for score.
847368c31abSDavid du Colombier  */
848368c31abSDavid du Colombier static DBlock*
loadibucket1(Index * ix,u8int * score,ISect ** pis,u32int * pbuck,IBucket * ib)849368c31abSDavid du Colombier loadibucket1(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
850368c31abSDavid du Colombier {
851368c31abSDavid du Colombier 	return loadibucket0(ix, hashbits(score, 32)/ix->div, pis, pbuck, ib, OREAD);
852368c31abSDavid du Colombier }
853368c31abSDavid du Colombier 
854368c31abSDavid du Colombier int
indexsect(Index * ix,u8int * score)855368c31abSDavid du Colombier indexsect(Index *ix, u8int *score)
856368c31abSDavid du Colombier {
857368c31abSDavid du Colombier 	return indexsect1(ix, score);
858368c31abSDavid du Colombier }
859368c31abSDavid du Colombier 
860368c31abSDavid du Colombier DBlock*
loadibucket(Index * ix,u8int * score,ISect ** pis,u32int * pbuck,IBucket * ib)861368c31abSDavid du Colombier loadibucket(Index *ix, u8int *score, ISect **pis, u32int *pbuck, IBucket *ib)
862368c31abSDavid du Colombier {
863368c31abSDavid du Colombier 	return loadibucket1(ix, score, pis, pbuck, ib);
864368c31abSDavid du Colombier }
865368c31abSDavid du Colombier 
866368c31abSDavid du Colombier 
867