xref: /plan9/sys/src/ape/cmd/pdksh/alloc.c (revision 6b6b9ac8b0b103b1e30e4d019522a78c950fce74)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * area-based allocation built on malloc/free
37dd7cddfSDavid du Colombier  */
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier #include "sh.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier # if DEBUG_ALLOC
107dd7cddfSDavid du Colombier void acheck ARGS((Area *ap));
117dd7cddfSDavid du Colombier #  define ACHECK(ap)	acheck(ap)
127dd7cddfSDavid du Colombier # else /* DEBUG_ALLOC */
137dd7cddfSDavid du Colombier #  define ACHECK(ap)
147dd7cddfSDavid du Colombier # endif /* DEBUG_ALLOC */
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier #define	ICELLS	200		/* number of Cells in small Block */
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier typedef union Cell Cell;
197dd7cddfSDavid du Colombier typedef struct Block Block;
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier /*
227dd7cddfSDavid du Colombier  * The Cells in a Block are organized as a set of objects.
237dd7cddfSDavid du Colombier  * Each object (pointed to by dp) begins with the block it is in
247dd7cddfSDavid du Colombier  * (dp-2)->block, then has a size in (dp-1)->size, which is
257dd7cddfSDavid du Colombier  * followed with "size" data Cells.  Free objects are
267dd7cddfSDavid du Colombier  * linked together via dp->next.
277dd7cddfSDavid du Colombier  */
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier #define NOBJECT_FIELDS	2	/* the block and size `fields' */
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier union Cell {
327dd7cddfSDavid du Colombier 	size_t	size;
337dd7cddfSDavid du Colombier 	Cell   *next;
347dd7cddfSDavid du Colombier 	Block  *block;
357dd7cddfSDavid du Colombier 	struct {int _;} junk;	/* alignment */
367dd7cddfSDavid du Colombier 	double djunk;		/* alignment */
377dd7cddfSDavid du Colombier };
387dd7cddfSDavid du Colombier 
397dd7cddfSDavid du Colombier struct Block {
407dd7cddfSDavid du Colombier 	Block  *next;		/* list of Blocks in Area */
417dd7cddfSDavid du Colombier 	Block  *prev;		/* previous block in list */
427dd7cddfSDavid du Colombier 	Cell   *freelist;	/* object free list */
437dd7cddfSDavid du Colombier 	Cell   *last;		/* &b.cell[size] */
447dd7cddfSDavid du Colombier 	Cell	cell [1];	/* [size] Cells for allocation */
457dd7cddfSDavid du Colombier };
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier static Block aempty = {&aempty, &aempty, aempty.cell, aempty.cell};
487dd7cddfSDavid du Colombier 
497dd7cddfSDavid du Colombier static void ablockfree ARGS((Block *bp, Area *ap));
507dd7cddfSDavid du Colombier static void *asplit ARGS((Area *ap, Block *bp, Cell *fp, Cell *fpp, int cells));
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier /* create empty Area */
537dd7cddfSDavid du Colombier Area *
ainit(ap)547dd7cddfSDavid du Colombier ainit(ap)
557dd7cddfSDavid du Colombier 	register Area *ap;
567dd7cddfSDavid du Colombier {
577dd7cddfSDavid du Colombier 	ap->freelist = &aempty;
587dd7cddfSDavid du Colombier 	ACHECK(ap);
597dd7cddfSDavid du Colombier 	return ap;
607dd7cddfSDavid du Colombier }
617dd7cddfSDavid du Colombier 
627dd7cddfSDavid du Colombier /* free all object in Area */
637dd7cddfSDavid du Colombier void
afreeall(ap)647dd7cddfSDavid du Colombier afreeall(ap)
657dd7cddfSDavid du Colombier 	register Area *ap;
667dd7cddfSDavid du Colombier {
677dd7cddfSDavid du Colombier 	register Block *bp;
687dd7cddfSDavid du Colombier 	register Block *tmp;
697dd7cddfSDavid du Colombier 
707dd7cddfSDavid du Colombier 	ACHECK(ap);
717dd7cddfSDavid du Colombier 	bp = ap->freelist;
727dd7cddfSDavid du Colombier 	if (bp != NULL && bp != &aempty) {
737dd7cddfSDavid du Colombier 		do {
747dd7cddfSDavid du Colombier 			tmp = bp;
757dd7cddfSDavid du Colombier 			bp = bp->next;
767dd7cddfSDavid du Colombier 			free((void*)tmp);
777dd7cddfSDavid du Colombier 		} while (bp != ap->freelist);
787dd7cddfSDavid du Colombier 		ap->freelist = &aempty;
797dd7cddfSDavid du Colombier 	}
807dd7cddfSDavid du Colombier 	ACHECK(ap);
817dd7cddfSDavid du Colombier }
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier /* allocate object from Area */
847dd7cddfSDavid du Colombier void *
alloc(size,ap)857dd7cddfSDavid du Colombier alloc(size, ap)
867dd7cddfSDavid du Colombier 	size_t size;
877dd7cddfSDavid du Colombier 	register Area *ap;
887dd7cddfSDavid du Colombier {
897dd7cddfSDavid du Colombier 	int cells, acells;
907dd7cddfSDavid du Colombier 	Block *bp = 0;
917dd7cddfSDavid du Colombier 	Cell *fp = 0, *fpp = 0;
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	ACHECK(ap);
947dd7cddfSDavid du Colombier 	if (size <= 0)
957dd7cddfSDavid du Colombier 		aerror(ap, "allocate bad size");
967dd7cddfSDavid du Colombier 	cells = (unsigned)(size + sizeof(Cell) - 1) / sizeof(Cell);
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier 	/* allocate at least this many cells */
997dd7cddfSDavid du Colombier 	acells = cells + NOBJECT_FIELDS;
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier 	/*
1027dd7cddfSDavid du Colombier 	 * Only attempt to track small objects - let malloc deal
1037dd7cddfSDavid du Colombier 	 * with larger objects. (this way we don't have to deal with
1047dd7cddfSDavid du Colombier 	 * coalescing memory, or with releasing it to the system)
1057dd7cddfSDavid du Colombier 	 */
1067dd7cddfSDavid du Colombier 	if (cells <= ICELLS) {
1077dd7cddfSDavid du Colombier 		/* find free Cell large enough */
1087dd7cddfSDavid du Colombier 		for (bp = ap->freelist; ; bp = bp->next) {
1097dd7cddfSDavid du Colombier 			for (fpp = NULL, fp = bp->freelist;
1107dd7cddfSDavid du Colombier 			     fp != bp->last; fpp = fp, fp = fp->next)
1117dd7cddfSDavid du Colombier 			{
1127dd7cddfSDavid du Colombier 				if ((fp-1)->size >= cells)
1137dd7cddfSDavid du Colombier 					goto Found;
1147dd7cddfSDavid du Colombier 			}
1157dd7cddfSDavid du Colombier 			/* wrapped around Block list, create new Block */
1167dd7cddfSDavid du Colombier 			if (bp->next == ap->freelist) {
1177dd7cddfSDavid du Colombier 				bp = 0;
1187dd7cddfSDavid du Colombier 				break;
1197dd7cddfSDavid du Colombier 			}
1207dd7cddfSDavid du Colombier 		}
1217dd7cddfSDavid du Colombier 		/* Not much free space left?  Allocate a big object this time */
1227dd7cddfSDavid du Colombier 		acells += ICELLS;
1237dd7cddfSDavid du Colombier 	}
1247dd7cddfSDavid du Colombier 	if (bp == 0) {
1257dd7cddfSDavid du Colombier 		bp = (Block*) malloc(offsetof(Block, cell[acells]));
1267dd7cddfSDavid du Colombier 		if (bp == NULL)
1277dd7cddfSDavid du Colombier 			aerror(ap, "cannot allocate");
1287dd7cddfSDavid du Colombier 		if (ap->freelist == &aempty) {
1297dd7cddfSDavid du Colombier 			ap->freelist = bp->next = bp->prev = bp;
1307dd7cddfSDavid du Colombier 		} else {
1317dd7cddfSDavid du Colombier 			bp->next = ap->freelist->next;
1327dd7cddfSDavid du Colombier 			ap->freelist->next->prev = bp;
1337dd7cddfSDavid du Colombier 			ap->freelist->next = bp;
1347dd7cddfSDavid du Colombier 			bp->prev = ap->freelist;
1357dd7cddfSDavid du Colombier 		}
1367dd7cddfSDavid du Colombier 		bp->last = bp->cell + acells;
1377dd7cddfSDavid du Colombier 		/* initial free list */
1387dd7cddfSDavid du Colombier 		fp = bp->freelist = bp->cell + NOBJECT_FIELDS;
1397dd7cddfSDavid du Colombier 		(fp-1)->size = acells - NOBJECT_FIELDS;
1407dd7cddfSDavid du Colombier 		(fp-2)->block = bp;
1417dd7cddfSDavid du Colombier 		fp->next = bp->last;
1427dd7cddfSDavid du Colombier 		fpp = NULL;
1437dd7cddfSDavid du Colombier 	}
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier   Found:
1467dd7cddfSDavid du Colombier 	return asplit(ap, bp, fp, fpp, cells);
1477dd7cddfSDavid du Colombier }
1487dd7cddfSDavid du Colombier 
1497dd7cddfSDavid du Colombier /* Do the work of splitting an object into allocated and (possibly) unallocated
1507dd7cddfSDavid du Colombier  * objects.  Returns the `allocated' object.
1517dd7cddfSDavid du Colombier  */
1527dd7cddfSDavid du Colombier static void *
asplit(ap,bp,fp,fpp,cells)1537dd7cddfSDavid du Colombier asplit(ap, bp, fp, fpp, cells)
1547dd7cddfSDavid du Colombier 	Area *ap;
1557dd7cddfSDavid du Colombier 	Block *bp;
1567dd7cddfSDavid du Colombier 	Cell *fp;
1577dd7cddfSDavid du Colombier 	Cell *fpp;
1587dd7cddfSDavid du Colombier 	int cells;
1597dd7cddfSDavid du Colombier {
1607dd7cddfSDavid du Colombier 	Cell *dp = fp;	/* allocated object */
1617dd7cddfSDavid du Colombier 	int split = (fp-1)->size - cells;
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	ACHECK(ap);
1647dd7cddfSDavid du Colombier 	if (split < 0)
1657dd7cddfSDavid du Colombier 		aerror(ap, "allocated object too small");
1667dd7cddfSDavid du Colombier 	if (split <= NOBJECT_FIELDS) {	/* allocate all */
1677dd7cddfSDavid du Colombier 		fp = fp->next;
1687dd7cddfSDavid du Colombier 	} else {		/* allocate head, free tail */
1697dd7cddfSDavid du Colombier 		Cell *next = fp->next; /* needed, as cells may be 0 */
1707dd7cddfSDavid du Colombier 		ap->freelist = bp; /* next time, start looking for space here */
1717dd7cddfSDavid du Colombier 		(fp-1)->size = cells;
1727dd7cddfSDavid du Colombier 		fp += cells + NOBJECT_FIELDS;
1737dd7cddfSDavid du Colombier 		(fp-1)->size = split - NOBJECT_FIELDS;
1747dd7cddfSDavid du Colombier 		(fp-2)->block = bp;
1757dd7cddfSDavid du Colombier 		fp->next = next;
1767dd7cddfSDavid du Colombier 	}
1777dd7cddfSDavid du Colombier 	if (fpp == NULL)
1787dd7cddfSDavid du Colombier 		bp->freelist = fp;
1797dd7cddfSDavid du Colombier 	else
1807dd7cddfSDavid du Colombier 		fpp->next = fp;
1817dd7cddfSDavid du Colombier 	ACHECK(ap);
1827dd7cddfSDavid du Colombier 	return (void*) dp;
1837dd7cddfSDavid du Colombier }
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier /* change size of object -- like realloc */
1867dd7cddfSDavid du Colombier void *
aresize(ptr,size,ap)1877dd7cddfSDavid du Colombier aresize(ptr, size, ap)
1887dd7cddfSDavid du Colombier 	register void *ptr;
1897dd7cddfSDavid du Colombier 	size_t size;
1907dd7cddfSDavid du Colombier 	Area *ap;
1917dd7cddfSDavid du Colombier {
1927dd7cddfSDavid du Colombier 	int cells;
1937dd7cddfSDavid du Colombier 	Cell *dp = (Cell*) ptr;
1947dd7cddfSDavid du Colombier 	int oldcells = dp ? (dp-1)->size : 0;
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	ACHECK(ap);
1977dd7cddfSDavid du Colombier 	if (size <= 0)
1987dd7cddfSDavid du Colombier 		aerror(ap, "allocate bad size");
1997dd7cddfSDavid du Colombier 	/* New size (in cells) */
2007dd7cddfSDavid du Colombier 	cells = (unsigned)(size - 1) / sizeof(Cell) + 1;
2017dd7cddfSDavid du Colombier 
2027dd7cddfSDavid du Colombier 	/* Is this a large object?  If so, let malloc deal with it
2037dd7cddfSDavid du Colombier 	 * directly (unless we are crossing the ICELLS border, in
2047dd7cddfSDavid du Colombier 	 * which case the alloc/free below handles it - this should
2057dd7cddfSDavid du Colombier 	 * cut down on fragmentation, and will also keep the code
2067dd7cddfSDavid du Colombier 	 * working (as it assumes size < ICELLS means it is not
2077dd7cddfSDavid du Colombier 	 * a `large object').
2087dd7cddfSDavid du Colombier 	 */
2097dd7cddfSDavid du Colombier 	if (oldcells > ICELLS && cells > ICELLS) {
2107dd7cddfSDavid du Colombier 		Block *bp = (dp-2)->block;
2117dd7cddfSDavid du Colombier 		Block *nbp;
2127dd7cddfSDavid du Colombier 		/* Saved in case realloc fails.. */
2137dd7cddfSDavid du Colombier 		Block *next = bp->next, *prev = bp->prev;
2147dd7cddfSDavid du Colombier 
2157dd7cddfSDavid du Colombier 		if (bp->freelist != bp->last)
2167dd7cddfSDavid du Colombier 			aerror(ap, "allocation resizing free pointer");
2177dd7cddfSDavid du Colombier 		nbp = realloc((void *) bp,
2187dd7cddfSDavid du Colombier 			      offsetof(Block, cell[cells + NOBJECT_FIELDS]));
2197dd7cddfSDavid du Colombier 		if (!nbp) {
2207dd7cddfSDavid du Colombier 			/* Have to clean up... */
2217dd7cddfSDavid du Colombier 			/* NOTE: If this code changes, similar changes may be
2227dd7cddfSDavid du Colombier 			 * needed in ablockfree().
2237dd7cddfSDavid du Colombier 			 */
2247dd7cddfSDavid du Colombier 			if (next == bp) /* only block */
2257dd7cddfSDavid du Colombier 				ap->freelist = &aempty;
2267dd7cddfSDavid du Colombier 			else {
2277dd7cddfSDavid du Colombier 				next->prev = prev;
2287dd7cddfSDavid du Colombier 				prev->next = next;
2297dd7cddfSDavid du Colombier 				if (ap->freelist == bp)
2307dd7cddfSDavid du Colombier 					ap->freelist = next;
2317dd7cddfSDavid du Colombier 			}
2327dd7cddfSDavid du Colombier 			aerror(ap, "cannot re-allocate");
2337dd7cddfSDavid du Colombier 		}
2347dd7cddfSDavid du Colombier 		/* If location changed, keep pointers straight... */
2357dd7cddfSDavid du Colombier 		if (nbp != bp) {
2367dd7cddfSDavid du Colombier 			if (next == bp) /* only one block */
2377dd7cddfSDavid du Colombier 				nbp->next = nbp->prev = nbp;
2387dd7cddfSDavid du Colombier 			else {
2397dd7cddfSDavid du Colombier 				next->prev = nbp;
2407dd7cddfSDavid du Colombier 				prev->next = nbp;
2417dd7cddfSDavid du Colombier 			}
2427dd7cddfSDavid du Colombier 			if (ap->freelist == bp)
2437dd7cddfSDavid du Colombier 				ap->freelist = nbp;
2447dd7cddfSDavid du Colombier 			dp = nbp->cell + NOBJECT_FIELDS;
2457dd7cddfSDavid du Colombier 			(dp-2)->block = nbp;
2467dd7cddfSDavid du Colombier 		}
2477dd7cddfSDavid du Colombier 		(dp-1)->size = cells;
2487dd7cddfSDavid du Colombier 		nbp->last = nbp->cell + cells + NOBJECT_FIELDS;
2497dd7cddfSDavid du Colombier 		nbp->freelist = nbp->last;
2507dd7cddfSDavid du Colombier 
2517dd7cddfSDavid du Colombier 		ACHECK(ap);
2527dd7cddfSDavid du Colombier 		return (void*) dp;
2537dd7cddfSDavid du Colombier 	}
2547dd7cddfSDavid du Colombier 
2557dd7cddfSDavid du Colombier 	/* Check if we can just grow this cell
2567dd7cddfSDavid du Colombier 	 * (need to check that cells < ICELLS so we don't make an
2577dd7cddfSDavid du Colombier 	 * object a `large' - that would mess everything up).
2587dd7cddfSDavid du Colombier 	 */
2597dd7cddfSDavid du Colombier 	if (dp && cells > oldcells && cells <= ICELLS) {
2607dd7cddfSDavid du Colombier 		Cell *fp, *fpp;
2617dd7cddfSDavid du Colombier 		Block *bp = (dp-2)->block;
2627dd7cddfSDavid du Colombier 		int need = cells - oldcells - NOBJECT_FIELDS;
2637dd7cddfSDavid du Colombier 
2647dd7cddfSDavid du Colombier 		/* XXX if we had a flag in an object indicating
2657dd7cddfSDavid du Colombier 		 * if the object was free/allocated, we could
2667dd7cddfSDavid du Colombier 		 * avoid this loop (perhaps)
2677dd7cddfSDavid du Colombier 		 */
2687dd7cddfSDavid du Colombier 		for (fpp = NULL, fp = bp->freelist;
2697dd7cddfSDavid du Colombier 		     fp != bp->last
2707dd7cddfSDavid du Colombier 		     && dp + oldcells + NOBJECT_FIELDS <= fp
2717dd7cddfSDavid du Colombier 		     ; fpp = fp, fp = fp->next)
2727dd7cddfSDavid du Colombier 		{
2737dd7cddfSDavid du Colombier 			if (dp + oldcells + NOBJECT_FIELDS == fp
2747dd7cddfSDavid du Colombier 			    && (fp-1)->size >= need)
2757dd7cddfSDavid du Colombier 			{
2767dd7cddfSDavid du Colombier 				Cell *np = asplit(ap, bp, fp, fpp, need);
2777dd7cddfSDavid du Colombier 				/* May get more than we need here */
2787dd7cddfSDavid du Colombier 				(dp-1)->size += (np-1)->size + NOBJECT_FIELDS;
2797dd7cddfSDavid du Colombier 				ACHECK(ap);
2807dd7cddfSDavid du Colombier 				return ptr;
2817dd7cddfSDavid du Colombier 			}
2827dd7cddfSDavid du Colombier 		}
2837dd7cddfSDavid du Colombier 	}
2847dd7cddfSDavid du Colombier 
2857dd7cddfSDavid du Colombier 	/* Check if we can just shrink this cell
2867dd7cddfSDavid du Colombier 	 * (if oldcells > ICELLS, this is a large object and we leave
2877dd7cddfSDavid du Colombier 	 * it to malloc...)
2887dd7cddfSDavid du Colombier 	 * Note: this also handles cells == oldcells (a no-op).
2897dd7cddfSDavid du Colombier 	 */
2907dd7cddfSDavid du Colombier 	if (dp && cells <= oldcells && oldcells <= ICELLS) {
2917dd7cddfSDavid du Colombier 		int split;
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 		split = oldcells - cells;
2947dd7cddfSDavid du Colombier 		if (split <= NOBJECT_FIELDS) /* cannot split */
2957dd7cddfSDavid du Colombier 			;
2967dd7cddfSDavid du Colombier 		else {		/* shrink head, free tail */
2977dd7cddfSDavid du Colombier 			Block *bp = (dp-2)->block;
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 			(dp-1)->size = cells;
3007dd7cddfSDavid du Colombier 			dp += cells + NOBJECT_FIELDS;
3017dd7cddfSDavid du Colombier 			(dp-1)->size = split - NOBJECT_FIELDS;
3027dd7cddfSDavid du Colombier 			(dp-2)->block = bp;
3037dd7cddfSDavid du Colombier 			afree((void*)dp, ap);
3047dd7cddfSDavid du Colombier 		}
3057dd7cddfSDavid du Colombier 		/* ACHECK() done in afree() */
3067dd7cddfSDavid du Colombier 		return ptr;
3077dd7cddfSDavid du Colombier 	}
3087dd7cddfSDavid du Colombier 
3097dd7cddfSDavid du Colombier 	/* Have to do it the hard way... */
3107dd7cddfSDavid du Colombier 	ptr = alloc(size, ap);
3117dd7cddfSDavid du Colombier 	if (dp != NULL) {
3127dd7cddfSDavid du Colombier 		size_t s = (dp-1)->size * sizeof(Cell);
3137dd7cddfSDavid du Colombier 		if (s > size)
3147dd7cddfSDavid du Colombier 			s = size;
3157dd7cddfSDavid du Colombier 		memcpy(ptr, dp, s);
3167dd7cddfSDavid du Colombier 		afree((void *) dp, ap);
3177dd7cddfSDavid du Colombier 	}
3187dd7cddfSDavid du Colombier 	/* ACHECK() done in alloc()/afree() */
3197dd7cddfSDavid du Colombier 	return ptr;
3207dd7cddfSDavid du Colombier }
3217dd7cddfSDavid du Colombier 
3227dd7cddfSDavid du Colombier void
afree(ptr,ap)3237dd7cddfSDavid du Colombier afree(ptr, ap)
3247dd7cddfSDavid du Colombier 	void *ptr;
3257dd7cddfSDavid du Colombier 	register Area *ap;
3267dd7cddfSDavid du Colombier {
3277dd7cddfSDavid du Colombier 	register Block *bp;
3287dd7cddfSDavid du Colombier 	register Cell *fp, *fpp;
3297dd7cddfSDavid du Colombier 	register Cell *dp = (Cell*)ptr;
3307dd7cddfSDavid du Colombier 
3317dd7cddfSDavid du Colombier 	ACHECK(ap);
3327dd7cddfSDavid du Colombier 	if (ptr == 0)
3337dd7cddfSDavid du Colombier 		aerror(ap, "freeing null pointer");
3347dd7cddfSDavid du Colombier 	bp = (dp-2)->block;
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier 	/* If this is a large object, just free it up... */
3377dd7cddfSDavid du Colombier 	/* Release object... */
3387dd7cddfSDavid du Colombier 	if ((dp-1)->size > ICELLS) {
3397dd7cddfSDavid du Colombier 		ablockfree(bp, ap);
3407dd7cddfSDavid du Colombier 		ACHECK(ap);
3417dd7cddfSDavid du Colombier 		return;
3427dd7cddfSDavid du Colombier 	}
3437dd7cddfSDavid du Colombier 
3447dd7cddfSDavid du Colombier 	if (dp < &bp->cell[NOBJECT_FIELDS] || dp >= bp->last)
3457dd7cddfSDavid du Colombier 		aerror(ap, "freeing memory outside of block (corrupted?)");
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier 	/* find position in free list */
3487dd7cddfSDavid du Colombier 	/* XXX if we had prev/next pointers for objects, this loop could go */
3497dd7cddfSDavid du Colombier 	for (fpp = NULL, fp = bp->freelist; fp < dp; fpp = fp, fp = fp->next)
3507dd7cddfSDavid du Colombier 		;
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 	if (fp == dp)
3537dd7cddfSDavid du Colombier 		aerror(ap, "freeing free object");
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier 	/* join object with next */
3567dd7cddfSDavid du Colombier 	if (dp + (dp-1)->size == fp-NOBJECT_FIELDS) { /* adjacent */
3577dd7cddfSDavid du Colombier 		(dp-1)->size += (fp-1)->size + NOBJECT_FIELDS;
3587dd7cddfSDavid du Colombier 		dp->next = fp->next;
3597dd7cddfSDavid du Colombier 	} else			/* non-adjacent */
3607dd7cddfSDavid du Colombier 		dp->next = fp;
3617dd7cddfSDavid du Colombier 
3627dd7cddfSDavid du Colombier 	/* join previous with object */
3637dd7cddfSDavid du Colombier 	if (fpp == NULL)
3647dd7cddfSDavid du Colombier 		bp->freelist = dp;
3657dd7cddfSDavid du Colombier 	else if (fpp + (fpp-1)->size == dp-NOBJECT_FIELDS) { /* adjacent */
3667dd7cddfSDavid du Colombier 		(fpp-1)->size += (dp-1)->size + NOBJECT_FIELDS;
3677dd7cddfSDavid du Colombier 		fpp->next = dp->next;
3687dd7cddfSDavid du Colombier 	} else			/* non-adjacent */
3697dd7cddfSDavid du Colombier 		fpp->next = dp;
3707dd7cddfSDavid du Colombier 
3717dd7cddfSDavid du Colombier 	/* If whole block is free (and we have some other blocks
3727dd7cddfSDavid du Colombier 	 * around), release this block back to the system...
3737dd7cddfSDavid du Colombier 	 */
3747dd7cddfSDavid du Colombier 	if (bp->next != bp && bp->freelist == bp->cell + NOBJECT_FIELDS
3757dd7cddfSDavid du Colombier 	    && bp->freelist + (bp->freelist-1)->size == bp->last
3767dd7cddfSDavid du Colombier 	    /* XXX and the other block has some free memory? */
3777dd7cddfSDavid du Colombier 	    )
3787dd7cddfSDavid du Colombier 		ablockfree(bp, ap);
3797dd7cddfSDavid du Colombier 	ACHECK(ap);
3807dd7cddfSDavid du Colombier }
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier static void
ablockfree(bp,ap)3837dd7cddfSDavid du Colombier ablockfree(bp, ap)
3847dd7cddfSDavid du Colombier 	Block *bp;
3857dd7cddfSDavid du Colombier 	Area *ap;
3867dd7cddfSDavid du Colombier {
3877dd7cddfSDavid du Colombier 	/* NOTE: If this code changes, similar changes may be
3887dd7cddfSDavid du Colombier 	 * needed in alloc() (where realloc fails).
3897dd7cddfSDavid du Colombier 	 */
3907dd7cddfSDavid du Colombier 
3917dd7cddfSDavid du Colombier 	if (bp->next == bp) /* only block */
3927dd7cddfSDavid du Colombier 		ap->freelist = &aempty;
3937dd7cddfSDavid du Colombier 	else {
3947dd7cddfSDavid du Colombier 		bp->next->prev = bp->prev;
3957dd7cddfSDavid du Colombier 		bp->prev->next = bp->next;
3967dd7cddfSDavid du Colombier 		if (ap->freelist == bp)
3977dd7cddfSDavid du Colombier 			ap->freelist = bp->next;
3987dd7cddfSDavid du Colombier 	}
3997dd7cddfSDavid du Colombier 	free((void*) bp);
4007dd7cddfSDavid du Colombier }
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier # if DEBUG_ALLOC
4037dd7cddfSDavid du Colombier void
acheck(ap)4047dd7cddfSDavid du Colombier acheck(ap)
4057dd7cddfSDavid du Colombier 	Area *ap;
4067dd7cddfSDavid du Colombier {
4077dd7cddfSDavid du Colombier 	Block *bp, *bpp;
4087dd7cddfSDavid du Colombier 	Cell *dp, *dptmp, *fp;
4097dd7cddfSDavid du Colombier 	int ok = 1;
4107dd7cddfSDavid du Colombier 	int isfree;
4117dd7cddfSDavid du Colombier 	static int disabled;
4127dd7cddfSDavid du Colombier 
4137dd7cddfSDavid du Colombier 	if (disabled)
4147dd7cddfSDavid du Colombier 		return;
4157dd7cddfSDavid du Colombier 
4167dd7cddfSDavid du Colombier 	if (!ap) {
4177dd7cddfSDavid du Colombier 		disabled = 1;
4187dd7cddfSDavid du Colombier 		aerror(ap, "acheck: null area pointer");
4197dd7cddfSDavid du Colombier 	}
4207dd7cddfSDavid du Colombier 
4217dd7cddfSDavid du Colombier 	bp = ap->freelist;
4227dd7cddfSDavid du Colombier 	if (!bp) {
4237dd7cddfSDavid du Colombier 		disabled = 1;
4247dd7cddfSDavid du Colombier 		aerror(ap, "acheck: null area freelist");
4257dd7cddfSDavid du Colombier 	}
4267dd7cddfSDavid du Colombier 
4277dd7cddfSDavid du Colombier 	/* Nothing to check... */
4287dd7cddfSDavid du Colombier 	if (bp == &aempty)
4297dd7cddfSDavid du Colombier 		return;
4307dd7cddfSDavid du Colombier 
4317dd7cddfSDavid du Colombier 	bpp = ap->freelist->prev;
4327dd7cddfSDavid du Colombier 	while (1) {
4337dd7cddfSDavid du Colombier 		if (bp->prev != bpp) {
4347dd7cddfSDavid du Colombier 			shellf("acheck: bp->prev != previous\n");
4357dd7cddfSDavid du Colombier 			ok = 0;
4367dd7cddfSDavid du Colombier 		}
4377dd7cddfSDavid du Colombier 		fp = bp->freelist;
4387dd7cddfSDavid du Colombier 		for (dp = &bp->cell[NOBJECT_FIELDS]; dp != bp->last; ) {
4397dd7cddfSDavid du Colombier 			if ((dp-2)->block != bp) {
4407dd7cddfSDavid du Colombier 				shellf("acheck: fragment's block is wrong\n");
4417dd7cddfSDavid du Colombier 				ok = 0;
4427dd7cddfSDavid du Colombier 			}
4437dd7cddfSDavid du Colombier 			isfree = dp == fp;
4447dd7cddfSDavid du Colombier 			if ((dp-1)->size == 0 && isfree) {
4457dd7cddfSDavid du Colombier 				shellf("acheck: 0 size frag\n");
4467dd7cddfSDavid du Colombier 				ok = 0;
4477dd7cddfSDavid du Colombier 			}
4487dd7cddfSDavid du Colombier 			if ((dp-1)->size > ICELLS
4497dd7cddfSDavid du Colombier 			    && !isfree
4507dd7cddfSDavid du Colombier 			    && (dp != &bp->cell[NOBJECT_FIELDS]
4517dd7cddfSDavid du Colombier 				|| dp + (dp-1)->size != bp->last))
4527dd7cddfSDavid du Colombier 			{
4537dd7cddfSDavid du Colombier 				shellf("acheck: big cell doesn't make up whole block\n");
4547dd7cddfSDavid du Colombier 				ok = 0;
4557dd7cddfSDavid du Colombier 			}
4567dd7cddfSDavid du Colombier 			if (isfree) {
4577dd7cddfSDavid du Colombier 				if (dp->next <= dp) {
4587dd7cddfSDavid du Colombier 					shellf("acheck: free fragment's next <= self\n");
4597dd7cddfSDavid du Colombier 					ok = 0;
4607dd7cddfSDavid du Colombier 				}
4617dd7cddfSDavid du Colombier 				if (dp->next > bp->last) {
4627dd7cddfSDavid du Colombier 					shellf("acheck: free fragment's next > last\n");
4637dd7cddfSDavid du Colombier 					ok = 0;
4647dd7cddfSDavid du Colombier 				}
4657dd7cddfSDavid du Colombier 				fp = dp->next;
4667dd7cddfSDavid du Colombier 			}
4677dd7cddfSDavid du Colombier 			dptmp = dp + (dp-1)->size;
4687dd7cddfSDavid du Colombier 			if (dptmp > bp->last) {
4697dd7cddfSDavid du Colombier 				shellf("acheck: next frag out of range\n");
4707dd7cddfSDavid du Colombier 				ok = 0;
4717dd7cddfSDavid du Colombier 				break;
4727dd7cddfSDavid du Colombier 			} else if (dptmp != bp->last) {
4737dd7cddfSDavid du Colombier 				dptmp += NOBJECT_FIELDS;
4747dd7cddfSDavid du Colombier 				if (dptmp > bp->last) {
4757dd7cddfSDavid du Colombier 					shellf("acheck: next frag just out of range\n");
4767dd7cddfSDavid du Colombier 					ok = 0;
4777dd7cddfSDavid du Colombier 					break;
4787dd7cddfSDavid du Colombier 				}
4797dd7cddfSDavid du Colombier 			}
4807dd7cddfSDavid du Colombier 			if (isfree && dptmp == fp && dptmp != bp->last) {
4817dd7cddfSDavid du Colombier 				shellf("acheck: adjacent free frags\n");
4827dd7cddfSDavid du Colombier 				ok = 0;
4837dd7cddfSDavid du Colombier 			} else if (dptmp > fp) {
4847dd7cddfSDavid du Colombier 				shellf("acheck: free frag list messed up\n");
4857dd7cddfSDavid du Colombier 				ok = 0;
4867dd7cddfSDavid du Colombier 			}
4877dd7cddfSDavid du Colombier 			dp = dptmp;
4887dd7cddfSDavid du Colombier 		}
4897dd7cddfSDavid du Colombier 		bpp = bp;
4907dd7cddfSDavid du Colombier 		bp = bp->next;
4917dd7cddfSDavid du Colombier 		if (bp == ap->freelist)
4927dd7cddfSDavid du Colombier 			break;
4937dd7cddfSDavid du Colombier 	}
4947dd7cddfSDavid du Colombier 	if (!ok) {
4957dd7cddfSDavid du Colombier 		disabled = 1;
4967dd7cddfSDavid du Colombier 		aerror(ap, "acheck failed");
4977dd7cddfSDavid du Colombier 	}
4987dd7cddfSDavid du Colombier }
4997dd7cddfSDavid du Colombier 
5007dd7cddfSDavid du Colombier void
aprint(ap,ptr,size)5017dd7cddfSDavid du Colombier aprint(ap, ptr, size)
5027dd7cddfSDavid du Colombier 	register Area *ap;
5037dd7cddfSDavid du Colombier 	void *ptr;
5047dd7cddfSDavid du Colombier 	size_t size;
5057dd7cddfSDavid du Colombier {
5067dd7cddfSDavid du Colombier 	Block *bp;
5077dd7cddfSDavid du Colombier 
5087dd7cddfSDavid du Colombier 	if (!ap)
5097dd7cddfSDavid du Colombier 		shellf("aprint: null area pointer\n");
5107dd7cddfSDavid du Colombier 	else if (!(bp = ap->freelist))
5117dd7cddfSDavid du Colombier 		shellf("aprint: null area freelist\n");
5127dd7cddfSDavid du Colombier 	else if (bp == &aempty)
5137dd7cddfSDavid du Colombier 		shellf("aprint: area is empty\n");
5147dd7cddfSDavid du Colombier 	else {
5157dd7cddfSDavid du Colombier 		int i;
5167dd7cddfSDavid du Colombier 		Cell *dp, *fp;
5177dd7cddfSDavid du Colombier 		Block *bpp;
5187dd7cddfSDavid du Colombier 
5197dd7cddfSDavid du Colombier 		bpp = ap->freelist->prev;
5207dd7cddfSDavid du Colombier 		for (i = 0; ; i++) {
5217dd7cddfSDavid du Colombier 			if (ptr) {
5227dd7cddfSDavid du Colombier 				void *eptr = (void *) (((char *) ptr) + size);
5237dd7cddfSDavid du Colombier 				/* print block only if it overlaps ptr/size */
5247dd7cddfSDavid du Colombier 				if (!((ptr >= (void *) bp
5257dd7cddfSDavid du Colombier 				       && ptr <= (void *) bp->last)
5267dd7cddfSDavid du Colombier 				      || (eptr >= (void *) bp
5277dd7cddfSDavid du Colombier 				         && eptr <= (void *) bp->last)))
5287dd7cddfSDavid du Colombier 					continue;
5297dd7cddfSDavid du Colombier 				shellf("aprint: overlap of 0x%p .. 0x%p\n",
5307dd7cddfSDavid du Colombier 					ptr, eptr);
5317dd7cddfSDavid du Colombier 			}
5327dd7cddfSDavid du Colombier 			if (bp->prev != bpp || bp->next->prev != bp)
5337dd7cddfSDavid du Colombier 				shellf(
5347dd7cddfSDavid du Colombier 	"aprint: BAD prev pointer: bp %p, bp->prev %p, bp->next %p, bpp=%p\n",
5357dd7cddfSDavid du Colombier 					bp, bp->prev, bp->next, bpp);
5367dd7cddfSDavid du Colombier 			shellf("aprint: block %2d (p=%p,%p,n=%p): 0x%p .. 0x%p (%ld)\n", i,
5377dd7cddfSDavid du Colombier 				bp->prev, bp, bp->next,
5387dd7cddfSDavid du Colombier 				bp->cell, bp->last,
5397dd7cddfSDavid du Colombier 				(long) ((char *) bp->last - (char *) bp->cell));
5407dd7cddfSDavid du Colombier 			fp = bp->freelist;
5417dd7cddfSDavid du Colombier 			if (bp->last <= bp->cell + NOBJECT_FIELDS)
5427dd7cddfSDavid du Colombier 				shellf(
5437dd7cddfSDavid du Colombier 			"aprint: BAD bp->last too small: %p <= %p\n",
5447dd7cddfSDavid du Colombier 					bp->last, bp->cell + NOBJECT_FIELDS);
5457dd7cddfSDavid du Colombier 			if (bp->freelist < bp->cell + NOBJECT_FIELDS
5467dd7cddfSDavid du Colombier 			    || bp->freelist > bp->last)
5477dd7cddfSDavid du Colombier 				shellf(
5487dd7cddfSDavid du Colombier 			"aprint: BAD bp->freelist %p out of range: %p .. %p\n",
5497dd7cddfSDavid du Colombier 					bp->freelist,
5507dd7cddfSDavid du Colombier 					bp->cell + NOBJECT_FIELDS, bp->last);
5517dd7cddfSDavid du Colombier 			for (dp = bp->cell; dp != bp->last ; ) {
5527dd7cddfSDavid du Colombier 				dp += NOBJECT_FIELDS;
5537dd7cddfSDavid du Colombier 				shellf(
5547dd7cddfSDavid du Colombier 				    "aprint:   0x%p .. 0x%p (%ld) %s\n",
5557dd7cddfSDavid du Colombier 					(dp-NOBJECT_FIELDS),
5567dd7cddfSDavid du Colombier 					(dp-NOBJECT_FIELDS) + (dp-1)->size
5577dd7cddfSDavid du Colombier 						+ NOBJECT_FIELDS,
5587dd7cddfSDavid du Colombier 					(long) ((dp-1)->size + NOBJECT_FIELDS)
5597dd7cddfSDavid du Colombier 						* sizeof(Cell),
5607dd7cddfSDavid du Colombier 					dp == fp ? "free" : "allocated");
5617dd7cddfSDavid du Colombier 				if ((dp-2)->block != bp)
5627dd7cddfSDavid du Colombier 					shellf(
5637dd7cddfSDavid du Colombier 					"aprint: BAD dp->block %p != bp %p\n",
5647dd7cddfSDavid du Colombier 						(dp-2)->block, bp);
5657dd7cddfSDavid du Colombier 				if (dp > bp->last)
5667dd7cddfSDavid du Colombier 					shellf(
5677dd7cddfSDavid du Colombier 				"aprint: BAD dp gone past block: %p > %p\n",
5687dd7cddfSDavid du Colombier 						dp, bp->last);
5697dd7cddfSDavid du Colombier 				if (dp > fp)
5707dd7cddfSDavid du Colombier 					shellf(
5717dd7cddfSDavid du Colombier 				"aprint: BAD dp gone past free: %p > %p\n",
5727dd7cddfSDavid du Colombier 						dp, fp);
5737dd7cddfSDavid du Colombier 				if (dp == fp) {
5747dd7cddfSDavid du Colombier 					fp = fp->next;
5757dd7cddfSDavid du Colombier 					if (fp < dp || fp > bp->last)
5767dd7cddfSDavid du Colombier 						shellf(
5777dd7cddfSDavid du Colombier 			"aprint: BAD free object %p out of range: %p .. %p\n",
5787dd7cddfSDavid du Colombier 							fp,
5797dd7cddfSDavid du Colombier 							dp, bp->last);
5807dd7cddfSDavid du Colombier 				}
5817dd7cddfSDavid du Colombier 				dp += (dp-1)->size;
5827dd7cddfSDavid du Colombier 			}
5837dd7cddfSDavid du Colombier 			bpp = bp;
5847dd7cddfSDavid du Colombier 			bp = bp->next;
5857dd7cddfSDavid du Colombier 			if (bp == ap->freelist)
5867dd7cddfSDavid du Colombier 				break;
5877dd7cddfSDavid du Colombier 		}
5887dd7cddfSDavid du Colombier 	}
5897dd7cddfSDavid du Colombier }
5907dd7cddfSDavid du Colombier 
591*6b6b9ac8SDavid du Colombier #endif
592