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