/* from the TOS GCC library */ /* malloc, free, realloc: dynamic memory allocation */ #include /* for size_t */ #include #include #include extern long _stksize; extern void *_sbrk(); /* minimum chunk to ask OS for */ static size_t MINHUNK = 4096L; /* default */ static size_t MAXHUNK = 32*1024L; /* max. default */ /* CAUTION: use _mallocChunkSize() to tailor to your environment, do not make the default too large, as the compiler gets screwed on a 1M machine otherwise (stack/heap clash) */ struct mem_chunk { long valid; #define VAL_FREE 0xf4ee0abc #define VAL_ALLOC 0xa11c0abc struct mem_chunk *next; long size; }; /* linked list of free blocks */ static struct mem_chunk _mchunk_free_list = { VAL_FREE, NULL, 0L }; /* flag to control zero'ing of malloc'ed chunks */ static int _ZeroMallocs = 0; #if 0 asm(".text; .even; .globl _mlalloc; _mlalloc:"); /* dept of dirty tricks */ #endif void * malloc(n) size_t n; { struct mem_chunk *p, *q; long sz; extern void *_heapbase; /* add a mem_chunk to required size and round up */ n = n + sizeof(struct mem_chunk); n = (7 + n) & ~7; /* look for first block big enough in free list */ p = &_mchunk_free_list; q = _mchunk_free_list.next; while ((q != NULL) && (q->size < n)) { p = q; q = q->next; } /* if not enough memory, get more from the system */ if (q == NULL) { if ((_heapbase != NULL) || (n > MINHUNK)) sz = n; else { sz = MINHUNK; if (MINHUNK < MAXHUNK) MINHUNK *= 2; } q = (struct mem_chunk * )_sbrk(sz); if (((long)q) == -1) /* can't alloc any more? */ return(NULL); p->next = q; q->size = sz; q->next = NULL; q->valid = VAL_FREE; } if (q->size > n + sizeof(struct mem_chunk)) { /* split, leave part of free list */ q->size -= n; q = (struct mem_chunk * )(((long) q) + q->size); q->size = n; q->valid = VAL_ALLOC; } else { /* just unlink it */ p->next = q->next; q->valid = VAL_ALLOC; } q->next = NULL; q++; /* hand back ptr to after chunk desc */ if(_ZeroMallocs != 0) bzero((void *)q, (size_t)(n - sizeof(struct mem_chunk))); return((void * )q); } void free(param) void *param; { struct mem_chunk *o, *p, *q, *s; struct mem_chunk *r = (struct mem_chunk *) param; extern void *_heapbase; /* free(NULL) should do nothing */ if (r == 0) return; /* move back to uncover the mem_chunk */ r--; /* there it is! */ if (r->valid != VAL_ALLOC) return; r->valid = VAL_FREE; /* stick it into free list, preserving ascending address order */ o = NULL; p = &_mchunk_free_list; q = _mchunk_free_list.next; while (q != NULL && q < r) { o = p; p = q; q = q->next; } /* merge after if possible */ s = (struct mem_chunk * )(((long) r) + r->size); if (q != NULL && s >= q) { assert(s == q); r->size += q->size; q = q->next; s->size = 0; s->next = NULL; } r->next = q; /* merge before if possible, otherwise link it in */ s = (struct mem_chunk * )(((long) p) + p->size); if (s >= r && p != &_mchunk_free_list) /* remember: r may be below &_mchunk_free_list in memory */ { assert(s == r); p->size += r->size; p->next = r->next; r->size = 0; r->next = NULL; s = (struct mem_chunk * )(((long) p) + p->size); if (_heapbase != NULL && s >= (struct mem_chunk *) _heapbase) { assert(s == _heapbase); _heapbase = (char *) p; _stksize += p->size; o->next = NULL; /* o is always != NULL here */ } } else { s = (struct mem_chunk * )(((long) r) + r->size); if (_heapbase != NULL && s >= (struct mem_chunk *) _heapbase) { assert(s == _heapbase); _heapbase = (char *) r; _stksize += r->size; p->next = NULL; } else p->next = r; } } #if 0 asm(".text; .even; .globl _relalloc,_realloc; _relalloc: jra _realloc"); #ifdef NDEBUG static char *__dummy = ""; /* otherwise we get a bra with 0 offset above */ #endif #endif void * realloc(_r, n) void *_r; size_t n; { struct mem_chunk *p, *q, *r = _r; long sz; /* obscure features: realloc(NULL,n) is the same as malloc(n) * realloc(p, 0) is the same as free(p) */ if (!r) return malloc(n); if (n == 0) { free(_r); return NULL; } p = r - 1; sz = (n + sizeof(struct mem_chunk) + 7) & ~7; if (p->size > sz) { /* block too big, split in two */ q = (struct mem_chunk * )(((long) p) + sz); q->size = p->size - sz; q->valid = VAL_ALLOC; free(q + 1); p->size = sz; } else if (p->size < sz) { /* block too small, get new one */ struct mem_chunk *s, *t; q = &_mchunk_free_list; t = _mchunk_free_list.next; while (t != NULL && t < p) { q = t; t = t->next; } /* merge after if possible */ s = (struct mem_chunk * )(((long) p) + p->size); if (t != NULL && s >= t && p->size + t->size >= sz) { assert(s == t); p->size += t->size; q->next = t->next; t->size = 0; t->next = NULL; } else { q = (struct mem_chunk * )malloc(n); if (q != NULL) { n = p->size - sizeof(struct mem_chunk); bcopy(r, q, n); free(r); /* free r only if we got a new block */ } r = q; } } /* else current block will do just fine */ return((void * )r); } #if 0 asm(".text; .even; .globl _clalloc; _clalloc:"); #endif void * calloc(n, sz) size_t n, sz; { char *r; size_t total; total = n * sz; if ((r = malloc(total)) != NULL) { bzero(r, total); } return(r); } /* * Set zero block after malloc flag */ void _malloczero(yes) int yes; { _ZeroMallocs = yes; } /* * tune chunk size */ void _mallocChunkSize (siz) size_t siz; { MAXHUNK = MINHUNK = siz; }