| #include "stdinc.h" | 
 | #include "dat.h" | 
 | #include "fns.h" | 
 |  | 
 | /* | 
 |  * An IEStream is a sorted list of index entries. | 
 |  */ | 
 | struct IEStream | 
 | { | 
 | 	Part	*part; | 
 | 	u64int	off;		/* read position within part */ | 
 | 	u64int	n;		/* number of valid ientries left to read */ | 
 | 	u32int	size;		/* allocated space in buffer */ | 
 | 	u8int	*buf; | 
 | 	u8int	*pos;		/* current place in buffer */ | 
 | 	u8int	*epos;		/* end of valid buffer contents */ | 
 | }; | 
 |  | 
 | IEStream* | 
 | initiestream(Part *part, u64int off, u64int clumps, u32int size) | 
 | { | 
 | 	IEStream *ies; | 
 |  | 
 | /* out of memory? */ | 
 | 	ies = MKZ(IEStream); | 
 | 	ies->buf = MKN(u8int, size); | 
 | 	ies->epos = ies->buf; | 
 | 	ies->pos = ies->epos; | 
 | 	ies->off = off; | 
 | 	ies->n = clumps; | 
 | 	ies->size = size; | 
 | 	ies->part = part; | 
 | 	return ies; | 
 | } | 
 |  | 
 | void | 
 | freeiestream(IEStream *ies) | 
 | { | 
 | 	if(ies == nil) | 
 | 		return; | 
 | 	free(ies->buf); | 
 | 	free(ies); | 
 | } | 
 |  | 
 | /* | 
 |  * Return the next IEntry (still packed) in the stream. | 
 |  */ | 
 | static u8int* | 
 | peekientry(IEStream *ies) | 
 | { | 
 | 	u32int n, nn; | 
 |  | 
 | 	n = ies->epos - ies->pos; | 
 | 	if(n < IEntrySize){ | 
 | 		memmove(ies->buf, ies->pos, n); | 
 | 		ies->epos = &ies->buf[n]; | 
 | 		ies->pos = ies->buf; | 
 | 		nn = ies->size; | 
 | 		if(nn > ies->n * IEntrySize) | 
 | 			nn = ies->n * IEntrySize; | 
 | 		nn -= n; | 
 | 		if(nn == 0) | 
 | 			return nil; | 
 | //fprint(2, "peek %d from %llud into %p\n", nn, ies->off, ies->epos); | 
 | 		if(readpart(ies->part, ies->off, ies->epos, nn) < 0){ | 
 | 			seterr(EOk, "can't read sorted index entries: %r"); | 
 | 			return nil; | 
 | 		} | 
 | 		ies->epos += nn; | 
 | 		ies->off += nn; | 
 | 	} | 
 | 	return ies->pos; | 
 | } | 
 |  | 
 | /* | 
 |  * Compute the bucket number for the given IEntry. | 
 |  * Knows that the score is the first thing in the packed | 
 |  * representation. | 
 |  */ | 
 | static u32int | 
 | iebuck(Index *ix, u8int *b, IBucket *ib, IEStream *ies) | 
 | { | 
 | 	USED(ies); | 
 | 	USED(ib); | 
 | 	return hashbits(b, 32) / ix->div; | 
 | } | 
 |  | 
 | /* | 
 |  * Fill ib with the next bucket in the stream. | 
 |  */ | 
 | u32int | 
 | buildbucket(Index *ix, IEStream *ies, IBucket *ib, uint maxdata) | 
 | { | 
 | 	IEntry ie1, ie2; | 
 | 	u8int *b; | 
 | 	u32int buck; | 
 |  | 
 | 	buck = TWID32; | 
 | 	ib->n = 0; | 
 | 	while(ies->n){ | 
 | 		b = peekientry(ies); | 
 | 		if(b == nil) | 
 | 			return TWID32; | 
 | /* fprint(2, "b=%p ies->n=%lld ib.n=%d buck=%d score=%V\n", b, ies->n, ib->n, iebuck(ix, b, ib, ies), b); */ | 
 | 		if(ib->n == 0) | 
 | 			buck = iebuck(ix, b, ib, ies); | 
 | 		else{ | 
 | 			if(buck != iebuck(ix, b, ib, ies)) | 
 | 				break; | 
 | 			if(ientrycmp(&ib->data[(ib->n - 1)* IEntrySize], b) == 0){ | 
 | 				/* | 
 | 				 * guess that the larger address is the correct one to use | 
 | 				 */ | 
 | 				unpackientry(&ie1, &ib->data[(ib->n - 1)* IEntrySize]); | 
 | 				unpackientry(&ie2, b); | 
 | 				seterr(EOk, "duplicate index entry for score=%V type=%d", ie1.score, ie1.ia.type); | 
 | 				ib->n--; | 
 | 				if(ie1.ia.addr > ie2.ia.addr) | 
 | 					memmove(b, &ib->data[ib->n * IEntrySize], IEntrySize); | 
 | 			} | 
 | 		} | 
 | 		if((ib->n+1)*IEntrySize > maxdata){ | 
 | 			seterr(EOk, "bucket overflow"); | 
 | 			return TWID32; | 
 | 		} | 
 | 		memmove(&ib->data[ib->n * IEntrySize], b, IEntrySize); | 
 | 		ib->n++; | 
 | 		ies->n--; | 
 | 		ies->pos += IEntrySize; | 
 | 	} | 
 | 	return buck; | 
 | } |