alloc.c revision 1.1.1.2 1 /*
2 * area-based allocation built on malloc/free
3 */
4
5 #include "sh.h"
6
7 #ifdef TEST_ALLOC
8 # define shellf printf
9 # ifndef DEBUG_ALLOC
10 # define DEBUG_ALLOC
11 # endif /* DEBUG_ALLOC */
12 #endif /* TEST_ALLOC */
13
14 #ifdef MEM_DEBUG
15
16 /*
17 * Special versions of alloc routines if doing mem_debug
18 */
19 Area *
20 _chmem_ainit(ap, file, line)
21 Area *ap;
22 const char *file;
23 int line;
24 {
25 ap->freelist = (struct Block *) _chmem_newpool("ainit", (char *) 0, -1,
26 file, line);
27 if (!ap->freelist)
28 aerror(ap, "ainit failed (ie, newpool)");
29 return ap;
30 }
31
32 /* free all object in Area */
33 void
34 _chmem_afreeall(ap, file, line)
35 Area *ap;
36 const char *file;
37 int line;
38 {
39 _chmem_delpool((Chmem_poolp) ap->freelist, 0, file, line);
40 /* Kind of ugly, but it works */
41 _chmem_ainit(ap, file, line);
42 }
43
44 /* allocate object from Area */
45 void *
46 _chmem_alloc(size, ap, file, line)
47 size_t size;
48 Area *ap;
49 const char *file;
50 int line;
51 {
52 return _chmem_mallocp((Chmem_poolp) ap->freelist, size, file, line);
53 }
54
55 /* change size of object -- like realloc */
56 void *
57 _chmem_aresize(ptr, size, ap, file, line)
58 void *ptr;
59 size_t size;
60 Area *ap;
61 const char *file;
62 int line;
63 {
64 if (!ptr)
65 /* Done as realloc(0, size) is not portable */
66 return _chmem_mallocp((Chmem_poolp) ap->freelist, size,
67 file, line);
68 else
69 return _chmem_reallocp((Chmem_poolp) ap->freelist, ptr, size,
70 file, line);
71 }
72
73 void
74 _chmem_afree(ptr, ap, file, line)
75 void *ptr;
76 Area *ap;
77 const char *file;
78 int line;
79 {
80 return _chmem_freep((Chmem_poolp) ap->freelist, ptr, file, line);
81 }
82
83 #else /* MEM_DEBUG */
84
85 # if DEBUG_ALLOC
86 void acheck ARGS((Area *ap));
87 # define ACHECK(ap) acheck(ap)
88 # else /* DEBUG_ALLOC */
89 # define ACHECK(ap)
90 # endif /* DEBUG_ALLOC */
91
92 #define ICELLS 200 /* number of Cells in small Block */
93
94 typedef union Cell Cell;
95 typedef struct Block Block;
96
97 /*
98 * The Cells in a Block are organized as a set of objects.
99 * Each object (pointed to by dp) begins with the block it is in
100 * (dp-2)->block, then has a size in (dp-1)->size, which is
101 * followed with "size" data Cells. Free objects are
102 * linked together via dp->next.
103 */
104
105 #define NOBJECT_FIELDS 2 /* the block and size `fields' */
106
107 union Cell {
108 size_t size;
109 Cell *next;
110 Block *block;
111 struct {int _;} junk; /* alignment */
112 double djunk; /* alignment */
113 };
114
115 struct Block {
116 Block *next; /* list of Blocks in Area */
117 Block *prev; /* previous block in list */
118 Cell *freelist; /* object free list */
119 Cell *last; /* &b.cell[size] */
120 Cell cell [1]; /* [size] Cells for allocation */
121 };
122
123 static Block aempty = {&aempty, &aempty, aempty.cell, aempty.cell};
124
125 static void ablockfree ARGS((Block *bp, Area *ap));
126 static void *asplit ARGS((Area *ap, Block *bp, Cell *fp, Cell *fpp, int cells));
127
128 /* create empty Area */
129 Area *
130 ainit(ap)
131 register Area *ap;
132 {
133 ap->freelist = &aempty;
134 ACHECK(ap);
135 return ap;
136 }
137
138 /* free all object in Area */
139 void
140 afreeall(ap)
141 register Area *ap;
142 {
143 register Block *bp;
144 register Block *tmp;
145
146 ACHECK(ap);
147 bp = ap->freelist;
148 if (bp != NULL && bp != &aempty) {
149 do {
150 tmp = bp;
151 bp = bp->next;
152 free((void*)tmp);
153 } while (bp != ap->freelist);
154 ap->freelist = &aempty;
155 }
156 ACHECK(ap);
157 }
158
159 /* allocate object from Area */
160 void *
161 alloc(size, ap)
162 size_t size;
163 register Area *ap;
164 {
165 int cells, acells;
166 Block *bp = 0;
167 Cell *fp = 0, *fpp = 0;
168
169 ACHECK(ap);
170 if (size <= 0)
171 aerror(ap, "allocate bad size");
172 cells = (unsigned)(size + sizeof(Cell) - 1) / sizeof(Cell);
173
174 /* allocate at least this many cells */
175 acells = cells + NOBJECT_FIELDS;
176
177 /*
178 * Only attempt to track small objects - let malloc deal
179 * with larger objects. (this way we don't have to deal with
180 * coalescing memory, or with releasing it to the system)
181 */
182 if (cells <= ICELLS) {
183 /* find free Cell large enough */
184 for (bp = ap->freelist; ; bp = bp->next) {
185 for (fpp = NULL, fp = bp->freelist;
186 fp != bp->last; fpp = fp, fp = fp->next)
187 {
188 if ((fp-1)->size >= cells)
189 goto Found;
190 }
191 /* wrapped around Block list, create new Block */
192 if (bp->next == ap->freelist) {
193 bp = 0;
194 break;
195 }
196 }
197 /* Not much free space left? Allocate a big object this time */
198 acells += ICELLS;
199 }
200 if (bp == 0) {
201 bp = (Block*) malloc(offsetof(Block, cell[acells]));
202 if (bp == NULL)
203 aerror(ap, "cannot allocate");
204 if (ap->freelist == &aempty) {
205 ap->freelist = bp->next = bp->prev = bp;
206 } else {
207 bp->next = ap->freelist->next;
208 ap->freelist->next->prev = bp;
209 ap->freelist->next = bp;
210 bp->prev = ap->freelist;
211 }
212 bp->last = bp->cell + acells;
213 /* initial free list */
214 fp = bp->freelist = bp->cell + NOBJECT_FIELDS;
215 (fp-1)->size = acells - NOBJECT_FIELDS;
216 (fp-2)->block = bp;
217 fp->next = bp->last;
218 fpp = NULL;
219 }
220
221 Found:
222 return asplit(ap, bp, fp, fpp, cells);
223 }
224
225 /* Do the work of splitting an object into allocated and (possibly) unallocated
226 * objects. Returns the `allocated' object.
227 */
228 static void *
229 asplit(ap, bp, fp, fpp, cells)
230 Area *ap;
231 Block *bp;
232 Cell *fp;
233 Cell *fpp;
234 int cells;
235 {
236 Cell *dp = fp; /* allocated object */
237 int split = (fp-1)->size - cells;
238
239 ACHECK(ap);
240 if (split < 0)
241 aerror(ap, "allocated object too small");
242 if (split <= NOBJECT_FIELDS) { /* allocate all */
243 fp = fp->next;
244 } else { /* allocate head, free tail */
245 Cell *next = fp->next; /* needed, as cells may be 0 */
246 ap->freelist = bp; /* next time, start looking for space here */
247 (fp-1)->size = cells;
248 fp += cells + NOBJECT_FIELDS;
249 (fp-1)->size = split - NOBJECT_FIELDS;
250 (fp-2)->block = bp;
251 fp->next = next;
252 }
253 if (fpp == NULL)
254 bp->freelist = fp;
255 else
256 fpp->next = fp;
257 ACHECK(ap);
258 return (void*) dp;
259 }
260
261 /* change size of object -- like realloc */
262 void *
263 aresize(ptr, size, ap)
264 register void *ptr;
265 size_t size;
266 Area *ap;
267 {
268 int cells;
269 Cell *dp = (Cell*) ptr;
270 int oldcells = dp ? (dp-1)->size : 0;
271
272 ACHECK(ap);
273 if (size <= 0)
274 aerror(ap, "allocate bad size");
275 /* New size (in cells) */
276 cells = (unsigned)(size - 1) / sizeof(Cell) + 1;
277
278 /* Is this a large object? If so, let malloc deal with it
279 * directly (unless we are crossing the ICELLS border, in
280 * which case the alloc/free below handles it - this should
281 * cut down on fragmentation, and will also keep the code
282 * working (as it assumes size < ICELLS means it is not
283 * a `large object').
284 */
285 if (oldcells > ICELLS && cells > ICELLS) {
286 Block *bp = (dp-2)->block;
287 Block *nbp;
288 /* Saved in case realloc fails.. */
289 Block *next = bp->next, *prev = bp->prev;
290
291 if (bp->freelist != bp->last)
292 aerror(ap, "allocation resizing free pointer");
293 nbp = realloc((void *) bp,
294 offsetof(Block, cell[cells + NOBJECT_FIELDS]));
295 if (!nbp) {
296 /* Have to clean up... */
297 /* NOTE: If this code changes, similar changes may be
298 * needed in ablockfree().
299 */
300 if (next == bp) /* only block */
301 ap->freelist = &aempty;
302 else {
303 next->prev = prev;
304 prev->next = next;
305 if (ap->freelist == bp)
306 ap->freelist = next;
307 }
308 aerror(ap, "cannot re-allocate");
309 }
310 /* If location changed, keep pointers straight... */
311 if (nbp != bp) {
312 if (next == bp) /* only one block */
313 nbp->next = nbp->prev = nbp;
314 else {
315 next->prev = nbp;
316 prev->next = nbp;
317 }
318 if (ap->freelist == bp)
319 ap->freelist = nbp;
320 dp = nbp->cell + NOBJECT_FIELDS;
321 (dp-2)->block = nbp;
322 }
323 (dp-1)->size = cells;
324 nbp->last = nbp->cell + cells + NOBJECT_FIELDS;
325 nbp->freelist = nbp->last;
326
327 ACHECK(ap);
328 return (void*) dp;
329 }
330
331 /* Check if we can just grow this cell
332 * (need to check that cells < ICELLS so we don't make an
333 * object a `large' - that would mess everything up).
334 */
335 if (dp && cells > oldcells && cells <= ICELLS) {
336 Cell *fp, *fpp;
337 Block *bp = (dp-2)->block;
338 int need = cells - oldcells - NOBJECT_FIELDS;
339
340 /* XXX if we had a flag in an object indicating
341 * if the object was free/allocated, we could
342 * avoid this loop (perhaps)
343 */
344 for (fpp = NULL, fp = bp->freelist;
345 fp != bp->last
346 && dp + oldcells + NOBJECT_FIELDS <= fp
347 ; fpp = fp, fp = fp->next)
348 {
349 if (dp + oldcells + NOBJECT_FIELDS == fp
350 && (fp-1)->size >= need)
351 {
352 Cell *np = asplit(ap, bp, fp, fpp, need);
353 /* May get more than we need here */
354 (dp-1)->size += (np-1)->size + NOBJECT_FIELDS;
355 ACHECK(ap);
356 return ptr;
357 }
358 }
359 }
360
361 /* Check if we can just shrink this cell
362 * (if oldcells > ICELLS, this is a large object and we leave
363 * it to malloc...)
364 * Note: this also handles cells == oldcells (a no-op).
365 */
366 if (dp && cells <= oldcells && oldcells <= ICELLS) {
367 int split;
368
369 split = oldcells - cells;
370 if (split <= NOBJECT_FIELDS) /* cannot split */
371 ;
372 else { /* shrink head, free tail */
373 Block *bp = (dp-2)->block;
374
375 (dp-1)->size = cells;
376 dp += cells + NOBJECT_FIELDS;
377 (dp-1)->size = split - NOBJECT_FIELDS;
378 (dp-2)->block = bp;
379 afree((void*)dp, ap);
380 }
381 /* ACHECK() done in afree() */
382 return ptr;
383 }
384
385 /* Have to do it the hard way... */
386 ptr = alloc(size, ap);
387 if (dp != NULL) {
388 size_t s = (dp-1)->size * sizeof(Cell);
389 if (s > size)
390 s = size;
391 memcpy(ptr, dp, s);
392 afree((void *) dp, ap);
393 }
394 /* ACHECK() done in alloc()/afree() */
395 return ptr;
396 }
397
398 void
399 afree(ptr, ap)
400 void *ptr;
401 register Area *ap;
402 {
403 register Block *bp;
404 register Cell *fp, *fpp;
405 register Cell *dp = (Cell*)ptr;
406
407 ACHECK(ap);
408 if (ptr == 0)
409 aerror(ap, "freeing null pointer");
410 bp = (dp-2)->block;
411
412 /* If this is a large object, just free it up... */
413 /* Release object... */
414 if ((dp-1)->size > ICELLS) {
415 ablockfree(bp, ap);
416 ACHECK(ap);
417 return;
418 }
419
420 if (dp < &bp->cell[NOBJECT_FIELDS] || dp >= bp->last)
421 aerror(ap, "freeing memory outside of block (corrupted?)");
422
423 /* find position in free list */
424 /* XXX if we had prev/next pointers for objects, this loop could go */
425 for (fpp = NULL, fp = bp->freelist; fp < dp; fpp = fp, fp = fp->next)
426 ;
427
428 if (fp == dp)
429 aerror(ap, "freeing free object");
430
431 /* join object with next */
432 if (dp + (dp-1)->size == fp-NOBJECT_FIELDS) { /* adjacent */
433 (dp-1)->size += (fp-1)->size + NOBJECT_FIELDS;
434 dp->next = fp->next;
435 } else /* non-adjacent */
436 dp->next = fp;
437
438 /* join previous with object */
439 if (fpp == NULL)
440 bp->freelist = dp;
441 else if (fpp + (fpp-1)->size == dp-NOBJECT_FIELDS) { /* adjacent */
442 (fpp-1)->size += (dp-1)->size + NOBJECT_FIELDS;
443 fpp->next = dp->next;
444 } else /* non-adjacent */
445 fpp->next = dp;
446
447 /* If whole block is free (and we have some other blocks
448 * around), release this block back to the system...
449 */
450 if (bp->next != bp && bp->freelist == bp->cell + NOBJECT_FIELDS
451 && bp->freelist + (bp->freelist-1)->size == bp->last
452 /* XXX and the other block has some free memory? */
453 )
454 ablockfree(bp, ap);
455 ACHECK(ap);
456 }
457
458 static void
459 ablockfree(bp, ap)
460 Block *bp;
461 Area *ap;
462 {
463 /* NOTE: If this code changes, similar changes may be
464 * needed in alloc() (where realloc fails).
465 */
466
467 if (bp->next == bp) /* only block */
468 ap->freelist = &aempty;
469 else {
470 bp->next->prev = bp->prev;
471 bp->prev->next = bp->next;
472 if (ap->freelist == bp)
473 ap->freelist = bp->next;
474 }
475 free((void*) bp);
476 }
477
478 # if DEBUG_ALLOC
479 void
480 acheck(ap)
481 Area *ap;
482 {
483 Block *bp, *bpp;
484 Cell *dp, *dptmp, *fp;
485 int ok = 1;
486 int isfree;
487 static int disabled;
488
489 if (disabled)
490 return;
491
492 if (!ap) {
493 disabled = 1;
494 aerror(ap, "acheck: null area pointer");
495 }
496
497 bp = ap->freelist;
498 if (!bp) {
499 disabled = 1;
500 aerror(ap, "acheck: null area freelist");
501 }
502
503 /* Nothing to check... */
504 if (bp == &aempty)
505 return;
506
507 bpp = ap->freelist->prev;
508 while (1) {
509 if (bp->prev != bpp) {
510 shellf("acheck: bp->prev != previous\n");
511 ok = 0;
512 }
513 fp = bp->freelist;
514 for (dp = &bp->cell[NOBJECT_FIELDS]; dp != bp->last; ) {
515 if ((dp-2)->block != bp) {
516 shellf("acheck: fragment's block is wrong\n");
517 ok = 0;
518 }
519 isfree = dp == fp;
520 if ((dp-1)->size == 0 && isfree) {
521 shellf("acheck: 0 size frag\n");
522 ok = 0;
523 }
524 if ((dp-1)->size > ICELLS
525 && !isfree
526 && (dp != &bp->cell[NOBJECT_FIELDS]
527 || dp + (dp-1)->size != bp->last))
528 {
529 shellf("acheck: big cell doesn't make up whole block\n");
530 ok = 0;
531 }
532 if (isfree) {
533 if (dp->next <= dp) {
534 shellf("acheck: free fragment's next <= self\n");
535 ok = 0;
536 }
537 if (dp->next > bp->last) {
538 shellf("acheck: free fragment's next > last\n");
539 ok = 0;
540 }
541 fp = dp->next;
542 }
543 dptmp = dp + (dp-1)->size;
544 if (dptmp > bp->last) {
545 shellf("acheck: next frag out of range\n");
546 ok = 0;
547 break;
548 } else if (dptmp != bp->last) {
549 dptmp += NOBJECT_FIELDS;
550 if (dptmp > bp->last) {
551 shellf("acheck: next frag just out of range\n");
552 ok = 0;
553 break;
554 }
555 }
556 if (isfree && dptmp == fp && dptmp != bp->last) {
557 shellf("acheck: adjacent free frags\n");
558 ok = 0;
559 } else if (dptmp > fp) {
560 shellf("acheck: free frag list messed up\n");
561 ok = 0;
562 }
563 dp = dptmp;
564 }
565 bpp = bp;
566 bp = bp->next;
567 if (bp == ap->freelist)
568 break;
569 }
570 if (!ok) {
571 disabled = 1;
572 aerror(ap, "acheck failed");
573 }
574 }
575
576 void
577 aprint(ap, ptr, size)
578 register Area *ap;
579 void *ptr;
580 size_t size;
581 {
582 Block *bp;
583
584 if (!ap)
585 shellf("aprint: null area pointer\n");
586 else if (!(bp = ap->freelist))
587 shellf("aprint: null area freelist\n");
588 else if (bp == &aempty)
589 shellf("aprint: area is empty\n");
590 else {
591 int i;
592 Cell *dp, *fp;
593 Block *bpp;
594
595 bpp = ap->freelist->prev;
596 for (i = 0; ; i++) {
597 if (ptr) {
598 void *eptr = (void *) (((char *) ptr) + size);
599 /* print block only if it overlaps ptr/size */
600 if (!((ptr >= (void *) bp
601 && ptr <= (void *) bp->last)
602 || (eptr >= (void *) bp
603 && eptr <= (void *) bp->last)))
604 continue;
605 shellf("aprint: overlap of 0x%p .. 0x%p\n",
606 ptr, eptr);
607 }
608 if (bp->prev != bpp || bp->next->prev != bp)
609 shellf(
610 "aprint: BAD prev pointer: bp %p, bp->prev %p, bp->next %p, bpp=%p\n",
611 bp, bp->prev, bp->next, bpp);
612 shellf("aprint: block %2d (p=%p,%p,n=%p): 0x%p .. 0x%p (%ld)\n", i,
613 bp->prev, bp, bp->next,
614 bp->cell, bp->last,
615 (long) ((char *) bp->last - (char *) bp->cell));
616 fp = bp->freelist;
617 if (bp->last <= bp->cell + NOBJECT_FIELDS)
618 shellf(
619 "aprint: BAD bp->last too small: %p <= %p\n",
620 bp->last, bp->cell + NOBJECT_FIELDS);
621 if (bp->freelist < bp->cell + NOBJECT_FIELDS
622 || bp->freelist > bp->last)
623 shellf(
624 "aprint: BAD bp->freelist %p out of range: %p .. %p\n",
625 bp->freelist,
626 bp->cell + NOBJECT_FIELDS, bp->last);
627 for (dp = bp->cell; dp != bp->last ; ) {
628 dp += NOBJECT_FIELDS;
629 shellf(
630 "aprint: 0x%p .. 0x%p (%ld) %s\n",
631 (dp-NOBJECT_FIELDS),
632 (dp-NOBJECT_FIELDS) + (dp-1)->size
633 + NOBJECT_FIELDS,
634 (long) ((dp-1)->size + NOBJECT_FIELDS)
635 * sizeof(Cell),
636 dp == fp ? "free" : "allocated");
637 if ((dp-2)->block != bp)
638 shellf(
639 "aprint: BAD dp->block %p != bp %p\n",
640 (dp-2)->block, bp);
641 if (dp > bp->last)
642 shellf(
643 "aprint: BAD dp gone past block: %p > %p\n",
644 dp, bp->last);
645 if (dp > fp)
646 shellf(
647 "aprint: BAD dp gone past free: %p > %p\n",
648 dp, fp);
649 if (dp == fp) {
650 fp = fp->next;
651 if (fp < dp || fp > bp->last)
652 shellf(
653 "aprint: BAD free object %p out of range: %p .. %p\n",
654 fp,
655 dp, bp->last);
656 }
657 dp += (dp-1)->size;
658 }
659 bpp = bp;
660 bp = bp->next;
661 if (bp == ap->freelist)
662 break;
663 }
664 }
665 }
666 # endif /* DEBUG_ALLOC */
667
668 # ifdef TEST_ALLOC
669
670 Area a;
671 FILE *myout;
672
673 int
674 main(int argc, char **argv)
675 {
676 char buf[1024];
677 struct info {
678 int size;
679 void *value;
680 };
681 struct info info[1024 * 2];
682 int size, ident;
683 int lineno = 0;
684
685 myout = stdout;
686 ainit(&a);
687 while (fgets(buf, sizeof(buf), stdin)) {
688 lineno++;
689 if (buf[0] == '\n' || buf[0] == '#')
690 continue;
691 if (sscanf(buf, " alloc %d = i%d", &size, &ident) == 2) {
692 if (ident < 0 || ident > NELEM(info)) {
693 fprintf(stderr, "bad ident (%d) on line %d\n",
694 ident, lineno);
695 exit(1);
696 }
697 info[ident].value = alloc(info[ident].size = size, &a);
698 printf("%p = alloc(%d) [%d,i%d]\n",
699 info[ident].value, info[ident].size,
700 lineno, ident);
701 memset(info[ident].value, 1, size);
702 continue;
703 }
704 if (sscanf(buf, " afree i%d", &ident) == 1) {
705 if (ident < 0 || ident > NELEM(info)) {
706 fprintf(stderr, "bad ident (%d) on line %d\n",
707 ident, lineno);
708 exit(1);
709 }
710 afree(info[ident].value, &a);
711 printf("afree(%p) [%d,i%d]\n", info[ident].value,
712 lineno, ident);
713 continue;
714 }
715 if (sscanf(buf, " aresize i%d , %d", &ident, &size) == 2) {
716 void *value;
717 if (ident < 0 || ident > NELEM(info)) {
718 fprintf(stderr, "bad ident (%d) on line %d\n",
719 ident, lineno);
720 exit(1);
721 }
722 value = info[ident].value;
723 info[ident].value = aresize(value,
724 info[ident].size = size,
725 &a);
726 printf("%p = aresize(%p, %d) [%d,i%d]\n",
727 info[ident].value, value, info[ident].size,
728 lineno, ident);
729 memset(info[ident].value, 1, size);
730 continue;
731 }
732 if (sscanf(buf, " aprint i%d , %d", &ident, &size) == 2) {
733 if (ident < 0 || ident > NELEM(info)) {
734 fprintf(stderr, "bad ident (%d) on line %d\n",
735 ident, lineno);
736 exit(1);
737 }
738 printf("aprint(%p, %d) [%d,i%d]\n",
739 info[ident].value, size, lineno, ident);
740 aprint(&a, info[ident].value, size);
741 continue;
742 }
743 if (sscanf(buf, " aprint %d", &ident) == 1) {
744 if (ident < 0 || ident > NELEM(info)) {
745 fprintf(stderr, "bad ident (%d) on line %d\n",
746 ident, lineno);
747 exit(1);
748 }
749 printf("aprint(0, 0) [%d]\n", lineno);
750 aprint(&a, 0, 0);
751 continue;
752 }
753 if (sscanf(buf, " afreeall %d", &ident) == 1) {
754 printf("afreeall() [%d]\n", lineno);
755 afreeall(&a);
756 memset(info, 0, sizeof(info));
757 continue;
758 }
759 fprintf(stderr, "unrecognized line (line %d)\n",
760 lineno);
761 exit(1);
762 }
763 return 0;
764 }
765
766 void
767 aerror(Area *ap, const char *msg)
768 {
769 printf("aerror: %s\n", msg);
770 fflush(stdout);
771 abort();
772 }
773
774 # endif /* TEST_ALLOC */
775
776 #endif /* MEM_DEBUG */
777