xref: /netbsd-src/bin/ksh/alloc.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: alloc.c,v 1.3 1997/07/20 17:41:59 christos Exp $	*/
2 
3 /*
4  * area-based allocation built on malloc/free
5  */
6 
7 #include "sh.h"
8 #ifdef MEM_DEBUG
9 # undef alloc
10 # undef aresize
11 # undef afree
12 #endif /* MEM_DEBUG */
13 
14 #define	ICELLS	100		/* number of Cells in small Block */
15 
16 typedef union Cell Cell;
17 typedef struct Block Block;
18 
19 /*
20  * The Cells in a Block are organized as a set of objects.
21  * Each object (pointed to by dp) begins with a size in (dp-1)->size,
22  * followed with "size" data Cells.  Free objects are
23  * linked together via dp->next.
24  */
25 
26 union Cell {
27 	size_t	size;
28 	Cell   *next;
29 	struct {int _;} junk;	/* alignment */
30 };
31 
32 struct Block {
33 	Block  *next;		/* list of Blocks in Area */
34 	Cell   *freelist;	/* object free list */
35 	Cell   *last;		/* &b.cell[size] */
36 	Cell	cell [1];	/* [size] Cells for allocation */
37 };
38 
39 static Block aempty = {&aempty, aempty.cell, aempty.cell};
40 
41 /* create empty Area */
42 Area *
43 ainit(ap)
44 	register Area *ap;
45 {
46 	ap->freelist = &aempty;
47 	return ap;
48 }
49 
50 /* free all object in Area */
51 void
52 afreeall(ap)
53 	register Area *ap;
54 {
55 	register Block *bp;
56 	register Block *tmp;
57 
58 	bp = ap->freelist;
59 	if (bp != NULL && bp != &aempty) {
60 		do {
61 			tmp = bp->next;
62 			free((void*)bp);
63 			bp = tmp;
64 		} while (bp != ap->freelist);
65 		ap->freelist = &aempty;
66 	}
67 }
68 
69 /* allocate object from Area */
70 void *
71 alloc(size, ap)
72 	size_t size;
73 	register Area *ap;
74 {
75 	int cells, split;
76 	register Block *bp;
77 	register Cell *dp, *fp, *fpp;
78 
79 	if (size <= 0) {
80 		aerror(ap, "allocate bad size");
81 		return NULL;
82 	}
83 	cells = (unsigned)(size - 1) / sizeof(Cell) + 1;
84 
85 	/* find Cell large enough */
86 	for (bp = ap->freelist; ; bp = bp->next) {
87 		for (fpp = NULL, fp = bp->freelist;
88 		     fp != bp->last; fpp = fp, fp = fpp->next)
89 			if ((fp-1)->size >= cells)
90 				goto Found;
91 
92 		/* wrapped around Block list, create new Block */
93 		if (bp->next == ap->freelist) {
94 			bp = (Block*) malloc(offsetof(Block, cell[ICELLS])
95 					     + sizeof(bp->cell[0]) * cells);
96 			if (bp == NULL) {
97 				aerror(ap, "cannot allocate");
98 				return NULL;
99 			}
100 			if (ap->freelist == &aempty)
101 				bp->next = bp;
102 			else {
103 				bp->next = ap->freelist->next;
104 				ap->freelist->next = bp;
105 			}
106 			bp->last = bp->cell + ICELLS + cells;
107 			fp = bp->freelist = bp->cell + 1; /* initial free list */
108 			(fp-1)->size = ICELLS + cells - 1;
109 			fp->next = bp->last;
110 			fpp = NULL;
111 			break;
112 		}
113 	}
114   Found:
115 	ap->freelist = bp;
116 	dp = fp;		/* allocated object */
117 	split = (dp-1)->size - cells;
118 	if (split < 0)
119 		aerror(ap, "allocated object too small");
120 	if (--split <= 0) {	/* allocate all */
121 		fp = fp->next;
122 	} else {		/* allocate head, free tail */
123 		(fp-1)->size = cells;
124 		fp += cells + 1;
125 		(fp-1)->size = split;
126 		fp->next = dp->next;
127 	}
128 	if (fpp == NULL)
129 		bp->freelist = fp;
130 	else
131 		fpp->next = fp;
132 	return (void*) dp;
133 }
134 
135 /* change size of object -- like realloc */
136 void *
137 aresize(ptr, size, ap)
138 	register void *ptr;
139 	size_t size;
140 	Area *ap;
141 {
142 	int cells;
143 	register Cell *dp = (Cell*) ptr;
144 
145 	if (size <= 0) {
146 		aerror(ap, "allocate bad size");
147 		return NULL;
148 	}
149 	cells = (unsigned)(size - 1) / sizeof(Cell) + 1;
150 
151 	if (dp == NULL || (dp-1)->size < cells) { /* enlarge object */
152 		/* XXX check for available adjacent free block */
153 		ptr = alloc(size, ap);
154 		if (dp != NULL) {
155 			memcpy(ptr, dp, (dp-1)->size * sizeof(Cell));
156 			afree((void *) dp, ap);
157 		}
158 	} else {		/* shrink object */
159 		int split;
160 
161 		split = (dp-1)->size - cells;
162 		if (--split <= 0) /* cannot split */
163 			;
164 		else {		/* shrink head, free tail */
165 			(dp-1)->size = cells;
166 			dp += cells + 1;
167 			(dp-1)->size = split;
168 			afree((void*)dp, ap);
169 		}
170 	}
171 	return (void*) ptr;
172 }
173 
174 void
175 afree(ptr, ap)
176 	void *ptr;
177 	register Area *ap;
178 {
179 	register Block *bp;
180 	register Cell *fp, *fpp;
181 	register Cell *dp = (Cell*)ptr;
182 
183 	/* find Block containing Cell */
184 	for (bp = ap->freelist; ; bp = bp->next) {
185 		if (bp->cell <= dp && dp < bp->last)
186 			break;
187 		if (bp->next == ap->freelist) {
188 			aerror(ap, "freeing with invalid area");
189 			return;
190 		}
191 	}
192 
193 	/* find position in free list */
194 	for (fpp = NULL, fp = bp->freelist; fp < dp; fpp = fp, fp = fpp->next)
195 		;
196 
197 	if (fp == dp) {
198 		aerror(ap, "freeing free object");
199 		return;
200 	}
201 
202 	/* join object with next */
203 	if (dp + (dp-1)->size == fp-1) { /* adjacent */
204 		(dp-1)->size += (fp-1)->size + 1;
205 		dp->next = fp->next;
206 	} else			/* non-adjacent */
207 		dp->next = fp;
208 
209 	/* join previous with object */
210 	if (fpp == NULL)
211 		bp->freelist = dp;
212 	else if (fpp + (fpp-1)->size == dp-1) { /* adjacent */
213 		(fpp-1)->size += (dp-1)->size + 1;
214 		fpp->next = dp->next;
215 	} else			/* non-adjacent */
216 		fpp->next = dp;
217 }
218 
219 #if DEBUG_ALLOC
220 void
221 aprint(ap, ptr, size)
222 	register Area *ap;
223 	void *ptr;
224 	size_t size;
225 {
226 	Block *bp;
227 
228 	if (!ap)
229 		shellf("aprint: null area pointer\n");
230 	else if (!(bp = ap->freelist))
231 		shellf("aprint: null area freelist\n");
232 	else if (bp == &aempty)
233 		shellf("aprint: area is empty\n");
234 	else {
235 		int i;
236 		Cell *fp;
237 
238 		for (i = 0; !i || bp != ap->freelist; bp = bp->next, i++) {
239 			if (ptr) {
240 				void *eptr = (void *) (((char *) ptr) + size);
241 				/* print block only if it overlaps ptr/size */
242 				if (!((ptr >= (void *) bp
243 				       && ptr <= (void *) bp->last)
244 				      || (eptr >= (void *) bp
245 				         && eptr <= (void *) bp->last)))
246 					continue;
247 				shellf("aprint: overlap of 0x%p .. 0x%p\n",
248 					ptr, eptr);
249 			}
250 			shellf("aprint: block %2d: 0x%p .. 0x%p (%d)\n", i,
251 				bp->cell, bp->last,
252 				(char *) bp->last - (char *) bp->cell);
253 			for (fp = bp->freelist; fp != bp->last; fp = fp->next)
254 				shellf(
255 				    "aprint:   0x%p .. 0x%p (%d) free\n",
256 					(fp-1), (fp-1) + (fp-1)->size,
257 					(fp-1)->size * sizeof(Cell));
258 		}
259 	}
260 }
261 #endif /* DEBUG_ALLOC */
262 
263 
264 #ifdef TEST_ALLOC
265 
266 Area a;
267 
268 main(int argc, char **argv) {
269 	int i;
270 	char *p [9];
271 
272 	ainit(&a);
273 	for (i = 0; i < 9; i++) {
274 		p[i] = alloc(124, &a);
275 		printf("alloc: %x\n", p[i]);
276 	}
277 	for (i = 1; i < argc; i++)
278 		afree(p[atoi(argv[i])], &a);
279 	afreeall(&a);
280 	return 0;
281 }
282 
283 void aerror(Area *ap, const char *msg) {
284 	abort();
285 }
286 
287 #endif
288 
289