| #include "stdinc.h" | 
 | #include "dat.h" | 
 | #include "fns.h" | 
 |  | 
 | typedef struct AHash	AHash; | 
 |  | 
 | /* | 
 |  * hash table for finding arena's based on their names. | 
 |  */ | 
 | struct AHash | 
 | { | 
 | 	AHash	*next; | 
 | 	Arena	*arena; | 
 | }; | 
 |  | 
 | enum | 
 | { | 
 | 	AHashSize	= 512 | 
 | }; | 
 |  | 
 | static AHash	*ahash[AHashSize]; | 
 |  | 
 | static u32int | 
 | hashstr(char *s) | 
 | { | 
 | 	u32int h; | 
 | 	int c; | 
 |  | 
 | 	h = 0; | 
 | 	for(; c = *s; s++){ | 
 | 		c ^= c << 6; | 
 | 		h += (c << 11) ^ (c >> 1); | 
 | 		c = *s; | 
 | 		h ^= (c << 14) + (c << 7) + (c << 4) + c; | 
 | 	} | 
 | 	return h; | 
 | } | 
 |  | 
 | int | 
 | addarena(Arena *arena) | 
 | { | 
 | 	AHash *a; | 
 | 	u32int h; | 
 |  | 
 | 	h = hashstr(arena->name) & (AHashSize - 1); | 
 | 	a = MK(AHash); | 
 | 	if(a == nil) | 
 | 		return -1; | 
 | 	a->arena = arena; | 
 | 	a->next = ahash[h]; | 
 | 	ahash[h] = a; | 
 | 	return 0; | 
 | } | 
 |  | 
 | Arena* | 
 | findarena(char *name) | 
 | { | 
 | 	AHash *a; | 
 | 	u32int h; | 
 |  | 
 | 	h = hashstr(name) & (AHashSize - 1); | 
 | 	for(a = ahash[h]; a != nil; a = a->next) | 
 | 		if(strcmp(a->arena->name, name) == 0) | 
 | 			return a->arena; | 
 | 	return nil; | 
 | } | 
 |  | 
 | int | 
 | delarena(Arena *arena) | 
 | { | 
 | 	AHash *a, *last; | 
 | 	u32int h; | 
 |  | 
 | 	h = hashstr(arena->name) & (AHashSize - 1); | 
 | 	last = nil; | 
 | 	for(a = ahash[h]; a != nil; a = a->next){ | 
 | 		if(a->arena == arena){ | 
 | 			if(last != nil) | 
 | 				last->next = a->next; | 
 | 			else | 
 | 				ahash[h] = a->next; | 
 | 			free(a); | 
 | 			return 0; | 
 | 		} | 
 | 		last = a; | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | ArenaPart* | 
 | initarenapart(Part *part) | 
 | { | 
 | 	AMapN amn; | 
 | 	ArenaPart *ap; | 
 | 	ZBlock *b; | 
 | 	u32int i; | 
 | 	int ok; | 
 |  | 
 | 	b = alloczblock(HeadSize, 0, 0); | 
 | 	if(b == nil || readpart(part, PartBlank, b->data, HeadSize) < 0){ | 
 | 		seterr(EAdmin, "can't read arena partition header: %r"); | 
 | 		return nil; | 
 | 	} | 
 |  | 
 | 	ap = MKZ(ArenaPart); | 
 | 	if(ap == nil){ | 
 | 		freezblock(b); | 
 | 		return nil; | 
 | 	} | 
 | 	ap->part = part; | 
 | 	ok = unpackarenapart(ap, b->data); | 
 | 	freezblock(b); | 
 | 	if(ok < 0){ | 
 | 		freearenapart(ap, 0); | 
 | 		return nil; | 
 | 	} | 
 |  | 
 | 	ap->tabbase = (PartBlank + HeadSize + ap->blocksize - 1) & ~(ap->blocksize - 1); | 
 | 	if(ap->version != ArenaPartVersion){ | 
 | 		seterr(ECorrupt, "unknown arena partition version %d", ap->version); | 
 | 		freearenapart(ap, 0); | 
 | 		return nil; | 
 | 	} | 
 | 	if(ap->blocksize & (ap->blocksize - 1)){ | 
 | 		seterr(ECorrupt, "illegal non-power-of-2 block size %d\n", ap->blocksize); | 
 | 		freearenapart(ap, 0); | 
 | 		return nil; | 
 | 	} | 
 | 	if(ap->tabbase >= ap->arenabase){ | 
 | 		seterr(ECorrupt, "arena partition table overlaps with arena storage"); | 
 | 		freearenapart(ap, 0); | 
 | 		return nil; | 
 | 	} | 
 | 	ap->tabsize = ap->arenabase - ap->tabbase; | 
 | 	partblocksize(part, ap->blocksize); | 
 | 	ap->size = ap->part->size & ~(u64int)(ap->blocksize - 1); | 
 |  | 
 | 	if(readarenamap(&amn, part, ap->tabbase, ap->tabsize) < 0){ | 
 | 		freearenapart(ap, 0); | 
 | 		return nil; | 
 | 	} | 
 | 	ap->narenas = amn.n; | 
 | 	ap->map = amn.map; | 
 | 	if(okamap(ap->map, ap->narenas, ap->arenabase, ap->size, "arena table") < 0){ | 
 | 		freearenapart(ap, 0); | 
 | 		return nil; | 
 | 	} | 
 |  | 
 | 	ap->arenas = MKNZ(Arena*, ap->narenas); | 
 | 	for(i = 0; i < ap->narenas; i++){ | 
 | 		debugarena = i; | 
 | 		ap->arenas[i] = initarena(part, ap->map[i].start, ap->map[i].stop - ap->map[i].start, ap->blocksize); | 
 | 		if(ap->arenas[i] == nil){ | 
 | 			seterr(ECorrupt, "%s: %r", ap->map[i].name); | 
 | 			freearenapart(ap, 1); | 
 | 			return nil; | 
 | 		} | 
 | 		if(namecmp(ap->map[i].name, ap->arenas[i]->name) != 0){ | 
 | 			seterr(ECorrupt, "arena name mismatches with expected name: %s vs. %s", | 
 | 				ap->map[i].name, ap->arenas[i]->name); | 
 | 			freearenapart(ap, 1); | 
 | 			return nil; | 
 | 		} | 
 | 		if(findarena(ap->arenas[i]->name)){ | 
 | 			seterr(ECorrupt, "duplicate arena name %s in %s", | 
 | 				ap->map[i].name, ap->part->name); | 
 | 			freearenapart(ap, 1); | 
 | 			return nil; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for(i = 0; i < ap->narenas; i++) { | 
 | 		debugarena = i; | 
 | 		addarena(ap->arenas[i]); | 
 | 	} | 
 | 	debugarena = -1; | 
 |  | 
 | 	return ap; | 
 | } | 
 |  | 
 | ArenaPart* | 
 | newarenapart(Part *part, u32int blocksize, u32int tabsize) | 
 | { | 
 | 	ArenaPart *ap; | 
 |  | 
 | 	if(blocksize & (blocksize - 1)){ | 
 | 		seterr(ECorrupt, "illegal non-power-of-2 block size %d\n", blocksize); | 
 | 		return nil; | 
 | 	} | 
 | 	ap = MKZ(ArenaPart); | 
 | 	if(ap == nil) | 
 | 		return nil; | 
 |  | 
 | 	ap->version = ArenaPartVersion; | 
 | 	ap->part = part; | 
 | 	ap->blocksize = blocksize; | 
 | 	partblocksize(part, blocksize); | 
 | 	ap->size = part->size & ~(u64int)(blocksize - 1); | 
 | 	ap->tabbase = (PartBlank + HeadSize + blocksize - 1) & ~(blocksize - 1); | 
 | 	ap->arenabase = (ap->tabbase + tabsize + blocksize - 1) & ~(blocksize - 1); | 
 | 	ap->tabsize = ap->arenabase - ap->tabbase; | 
 | 	ap->narenas = 0; | 
 |  | 
 | 	if(wbarenapart(ap) < 0){ | 
 | 		freearenapart(ap, 0); | 
 | 		return nil; | 
 | 	} | 
 |  | 
 | 	return ap; | 
 | } | 
 |  | 
 | int | 
 | wbarenapart(ArenaPart *ap) | 
 | { | 
 | 	ZBlock *b; | 
 |  | 
 | 	if(okamap(ap->map, ap->narenas, ap->arenabase, ap->size, "arena table") < 0) | 
 | 		return -1; | 
 | 	b = alloczblock(HeadSize, 1, 0); | 
 | 	if(b == nil) | 
 | /* ZZZ set error message? */ | 
 | 		return -1; | 
 |  | 
 | 	if(packarenapart(ap, b->data) < 0){ | 
 | 		seterr(ECorrupt, "can't make arena partition header: %r"); | 
 | 		freezblock(b); | 
 | 		return -1; | 
 | 	} | 
 | 	if(writepart(ap->part, PartBlank, b->data, HeadSize) < 0 || | 
 | 	   flushpart(ap->part) < 0){ | 
 | 		seterr(EAdmin, "can't write arena partition header: %r"); | 
 | 		freezblock(b); | 
 | 		return -1; | 
 | 	} | 
 | 	freezblock(b); | 
 |  | 
 | 	return wbarenamap(ap->map, ap->narenas, ap->part, ap->tabbase, ap->tabsize); | 
 | } | 
 |  | 
 | void | 
 | freearenapart(ArenaPart *ap, int freearenas) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if(ap == nil) | 
 | 		return; | 
 | 	if(freearenas){ | 
 | 		for(i = 0; i < ap->narenas; i++){ | 
 | 			if(ap->arenas[i] == nil) | 
 | 				continue; | 
 | 			delarena(ap->arenas[i]); | 
 | 			freearena(ap->arenas[i]); | 
 | 		} | 
 | 	} | 
 | 	free(ap->map); | 
 | 	free(ap->arenas); | 
 | 	free(ap); | 
 | } | 
 |  | 
 | int | 
 | okamap(AMap *am, int n, u64int start, u64int stop, char *what) | 
 | { | 
 | 	u64int last; | 
 | 	u32int i; | 
 |  | 
 | 	last = start; | 
 | 	for(i = 0; i < n; i++){ | 
 | 		if(am[i].start < last){ | 
 | 			if(i == 0) | 
 | 				seterr(ECorrupt, "invalid start address in %s", what); | 
 | 			else | 
 | 				seterr(ECorrupt, "overlapping ranges in %s", what); | 
 | 			return -1; | 
 | 		} | 
 | 		if(am[i].stop < am[i].start){ | 
 | 			seterr(ECorrupt, "invalid range in %s", what); | 
 | 			return -1; | 
 | 		} | 
 | 		last = am[i].stop; | 
 | 	} | 
 | 	if(last > stop){ | 
 | 		seterr(ECorrupt, "invalid ending address in %s", what); | 
 | 		return -1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int | 
 | maparenas(AMap *am, Arena **arenas, int n, char *what) | 
 | { | 
 | 	u32int i; | 
 |  | 
 | 	for(i = 0; i < n; i++){ | 
 | 		arenas[i] = findarena(am[i].name); | 
 | 		if(arenas[i] == nil){ | 
 | 			seterr(EAdmin, "can't find arena '%s' for '%s'\n", am[i].name, what); | 
 | 			return -1; | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int | 
 | readarenamap(AMapN *amn, Part *part, u64int base, u32int size) | 
 | { | 
 | 	IFile f; | 
 | 	u32int ok; | 
 |  | 
 | 	if(partifile(&f, part, base, size) < 0) | 
 | 		return -1; | 
 | 	ok = parseamap(&f, amn); | 
 | 	freeifile(&f); | 
 | 	return ok; | 
 | } | 
 |  | 
 | int | 
 | wbarenamap(AMap *am, int n, Part *part, u64int base, u64int size) | 
 | { | 
 | 	Fmt f; | 
 | 	ZBlock *b; | 
 |  | 
 | 	b = alloczblock(size, 1, part->blocksize); | 
 | 	if(b == nil) | 
 | 		return -1; | 
 |  | 
 | 	fmtzbinit(&f, b); | 
 |  | 
 | 	if(outputamap(&f, am, n) < 0){ | 
 | 		seterr(ECorrupt, "arena set size too small"); | 
 | 		freezblock(b); | 
 | 		return -1; | 
 | 	} | 
 | 	if(writepart(part, base, b->data, size) < 0 || flushpart(part) < 0){ | 
 | 		seterr(EAdmin, "can't write arena set: %r"); | 
 | 		freezblock(b); | 
 | 		return -1; | 
 | 	} | 
 | 	freezblock(b); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * amap: n '\n' amapelem * n | 
 |  * n: u32int | 
 |  * amapelem: name '\t' astart '\t' astop '\n' | 
 |  * astart, astop: u64int | 
 |  */ | 
 | int | 
 | parseamap(IFile *f, AMapN *amn) | 
 | { | 
 | 	AMap *am; | 
 | 	u64int v64; | 
 | 	u32int v; | 
 | 	char *s, *t, *flds[4]; | 
 | 	int i, n; | 
 |  | 
 | 	/* | 
 | 	 * arenas | 
 | 	 */ | 
 | 	if(ifileu32int(f, &v) < 0){ | 
 | 		seterr(ECorrupt, "syntax error: bad number of elements in %s", f->name); | 
 | 		return -1; | 
 | 	} | 
 | 	n = v; | 
 | 	if(n > MaxAMap){ | 
 | 		seterr(ECorrupt, "illegal number of elements %d in %s", | 
 | 			n, f->name); | 
 | 		return -1; | 
 | 	} | 
 | 	am = MKNZ(AMap, n); | 
 | 	if(am == nil){ | 
 | 		fprint(2, "out of memory\n"); | 
 | 		return -1; | 
 | 	} | 
 | 	for(i = 0; i < n; i++){ | 
 | 		s = ifileline(f); | 
 | 		if(s) | 
 | 			t = estrdup(s); | 
 | 		else | 
 | 			t = nil; | 
 | 		if(s == nil || getfields(s, flds, 4, 0, "\t") != 3){ | 
 | 			fprint(2, "early eof after %d of %d, %s:#%d: %s\n", i, n, f->name, f->pos, t); | 
 | 			free(t); | 
 | 			return -1; | 
 | 		} | 
 | 		free(t); | 
 | 		if(nameok(flds[0]) < 0) | 
 | 			return -1; | 
 | 		namecp(am[i].name, flds[0]); | 
 | 		if(stru64int(flds[1], &v64) < 0){ | 
 | 			seterr(ECorrupt, "syntax error: bad arena base address in %s", f->name); | 
 | 			free(am); | 
 | 			return -1; | 
 | 		} | 
 | 		am[i].start = v64; | 
 | 		if(stru64int(flds[2], &v64) < 0){ | 
 | 			seterr(ECorrupt, "syntax error: bad arena size in %s", f->name); | 
 | 			free(am); | 
 | 			return -1; | 
 | 		} | 
 | 		am[i].stop = v64; | 
 | 	} | 
 |  | 
 | 	amn->map = am; | 
 | 	amn->n = n; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int | 
 | outputamap(Fmt *f, AMap *am, int n) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if(fmtprint(f, "%ud\n", n) < 0) | 
 | 		return -1; | 
 | 	for(i = 0; i < n; i++) | 
 | 		if(fmtprint(f, "%s\t%llud\t%llud\n", am[i].name, am[i].start, am[i].stop) < 0) | 
 | 			return -1; | 
 | 	return 0; | 
 | } |