123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
- * This file is part of the Linux-8086 C library and is distributed
- * under the GNU Library General Public License.
- */
- /*
- * This is a combined alloca/malloc package. It uses a classic algorithm
- * and so may be seen to be quite slow compared to more modern routines
- * with 'nasty' distributions.
- */
- #include <malloc.h>
- #include <errno.h>
- #define MCHUNK 2048 /* Allocation unit in 'mem' elements */
- #define XLAZY_FREE /* If set frees can be infinitly defered */
- #define XMINALLOC 32 /* Smallest chunk to alloc in 'mem's */
- #define XVERBOSE /* Lots of noise, debuging ? */
- #undef malloc
- #define MAX_INT ((int)(((unsigned)-1)>>1))
- #ifdef VERBOSE
- #define noise __noise
- #else
- #define noise(y,x)
- #endif
- typedef union mem_cell
- {
- union mem_cell *next; /* A pointer to the next mem */
- unsigned int size; /* An int >= sizeof pointer */
- char *depth; /* For the alloca hack */
- }
- mem;
- #define m_size(p) ((p) [0].size) /* For malloc */
- #define m_next(p) ((p) [1].next) /* For malloc and alloca */
- #define m_deep(p) ((p) [0].depth) /* For alloca */
- extern void *__mini_malloc __P ((size_t));
- extern void *(*__alloca_alloc) __P ((size_t));
- extern mem *__freed_list;
- #ifdef L_free
- /* Start the alloca with just the dumb version of malloc */
- void *(*__alloca_alloc) __P ((size_t)) = __mini_malloc;
- mem *__freed_list = 0;
- #ifdef VERBOSE
- /* NB: Careful here, stdio may use malloc - so we can't */
- static
- phex(val)
- {
- static char hex[] = "0123456789ABCDEF";
- int i;
- for (i = sizeof(int)*8-4; i >= 0; i -= 4)
- write(2, hex + ((val >> i) & 0xF), 1);
- }
- noise(y, x)
- char *y;
- mem *x;
- {
- write(2, "Malloc ", 7);
- phex(x);
- write(2, " sz ", 4);
- if(x) phex(m_size(x)); else phex(0);
- write(2, " nxt ", 5);
- if(x) phex(m_next(x)); else phex(0);
- write(2, " is ", 4);
- write(2, y, strlen(y));
- write(2, "\n", 1);
- }
- #endif
- #endif
- #ifdef L_alloca
- /* This alloca is based on the same concept as the EMACS fallback alloca.
- * It should probably be considered Copyright the FSF under the GPL.
- */
- static mem *alloca_stack = 0;
- void *
- alloca(size)
- size_t size;
- {
- auto char probe; /* Probes stack depth: */
- register mem *hp;
- /*
- * Reclaim garbage, defined as all alloca'd storage that was allocated
- * from deeper in the stack than currently.
- */
- for (hp = alloca_stack; hp != 0;)
- if (m_deep(hp) < &probe)
- {
- register mem *np = m_next(hp);
- free((void *) hp); /* Collect garbage. */
- hp = np; /* -> next header. */
- }
- else
- break; /* Rest are not deeper. */
- alloca_stack = hp; /* -> last valid storage. */
- if (size == 0)
- return 0; /* No allocation required. */
- hp = (mem *) (*__alloca_alloc) (sizeof(mem)*2 + size);
- if (hp == 0)
- return hp;
- m_next(hp) = alloca_stack;
- m_deep(hp) = &probe;
- alloca_stack = hp;
- /* User storage begins just after header. */
- return (void *) (hp + 2);
- }
- #endif /* L_alloca */
- #ifdef L_free
- void
- free(ptr)
- void *ptr;
- {
- register mem *top;
- register mem *chk = (mem *) ptr;
- if (chk == 0)
- return; /* free(NULL) - be nice */
- chk--;
- try_this:;
- top = (mem *) sbrk(0);
- if (chk + m_size(chk) == top)
- {
- noise("FREE brk", chk);
- brk(top-m_size(chk));
- /*
- * Adding this code allow free to release blocks in any order; they
- * can still only be allocated from the top of the heap tho.
- */
- #ifdef __MINI_MALLOC__
- if (__alloca_alloc == __mini_malloc && __freed_list)
- {
- mem *prev, *curr;
- chk = __freed_list;
- __freed_list = m_next(__freed_list);
- goto try_this;
- }
- #endif
- }
- else
- { /* Nope, not sure where this goes, leave
- * it for malloc to deal with */
- #ifdef __MINI_MALLOC__
- if( __freed_list || chk > __freed_list )
- { m_next(chk) = __freed_list; __freed_list = chk; }
- else
- {
- register mem *prev;
- prev=__freed_list;
- for(top=__freed_list; top && top > chk; prev=top, top=m_next(top))
- ;
- m_next(chk) = top;
- m_next(prev) = chk;
- }
- #else
- m_next(chk) = __freed_list;
- __freed_list = chk;
- #endif
- noise("ADD LIST", chk);
- }
- }
- void *
- __mini_malloc(size)
- size_t size;
- {
- register mem *ptr;
- register unsigned int sz;
- /* First time round this _might_ be odd, But we won't do that! */
- sz = (unsigned int) sbrk(0);
- if (sz & (sizeof(mem) - 1))
- sbrk(4 - (sz & (sizeof(mem) - 1)));
- if (size <= 0)
- return 0;
- /* Minor oops here, sbrk has a signed argument */
- if( size > (((unsigned)-1)>>1)-sizeof(mem)*3 )
- {
- errno = ENOMEM;
- return 0;
- }
- size += sizeof(mem) * 2 - 1; /* Round up and leave space for size field */
- size /= sizeof(mem);
- ptr = (mem *) sbrk(size * sizeof(mem));
- if ((int) ptr == -1)
- return 0;
- m_size(ptr) = size;
- noise("CREATE", ptr);
- return ptr + 1;
- }
- #endif /* L_free */
- #ifdef L_malloc
- /*
- * The chunk_list pointer is either NULL or points to a chunk in a
- * circular list of all the free blocks in memory
- */
- #define Static static
- static mem *chunk_list = 0;
- Static void __insert_chunk();
- Static mem *__search_chunk();
- void *
- malloc(size)
- size_t size;
- {
- register mem *ptr = 0;
- register unsigned int sz;
- if (size == 0)
- return 0; /* ANSI STD */
- sz = size + sizeof(mem) * 2 - 1;
- sz /= sizeof(mem);
- #ifdef MINALLOC
- if (sz < MINALLOC)
- sz = MINALLOC;
- #endif
- #ifdef VERBOSE
- {
- static mem arr[2];
- m_size(arr) = sz;
- noise("WANTED", arr);
- }
- #endif
- __alloca_alloc = malloc; /* We'll be messing with the heap now TVM */
- #ifdef LAZY_FREE
- ptr = __search_chunk(sz);
- if (ptr == 0)
- {
- #endif
- /* First deal with the freed list */
- if (__freed_list)
- {
- while (__freed_list)
- {
- ptr = __freed_list;
- __freed_list = m_next(__freed_list);
- if (m_size(ptr) == sz) /* Oh! Well that's lucky ain't it
- * :-) */
- {
- noise("LUCKY MALLOC", ptr);
- return ptr + 1;
- }
- __insert_chunk(ptr);
- }
- ptr = m_next(chunk_list);
- if (ptr + m_size(ptr) == (void *) sbrk(0))
- {
- /* Time to free for real */
- m_next(chunk_list) = m_next(ptr);
- if (ptr == m_next(ptr))
- chunk_list = 0;
- free(ptr + 1);
- }
- #ifdef LAZY_FREE
- ptr = __search_chunk(sz);
- #endif
- }
- #ifndef LAZY_FREE
- ptr = __search_chunk(sz);
- #endif
- if (ptr == 0)
- {
- #ifdef MCHUNK
- unsigned int alloc;
- alloc = sizeof(mem) * (MCHUNK * ((sz + MCHUNK - 1) / MCHUNK) - 1);
- ptr = __mini_malloc(alloc);
- if (ptr)
- __insert_chunk(ptr - 1);
- else /* Oooo, near end of RAM */
- {
- unsigned int needed = alloc;
- for(alloc/=2; alloc>256 && needed; )
- {
- ptr = __mini_malloc(alloc);
- if (ptr)
- {
- if( alloc > needed ) needed = 0; else needed -= alloc;
- __insert_chunk(ptr - 1);
- }
- else alloc/=2;
- }
- }
- ptr = __search_chunk(sz);
- if (ptr == 0)
- #endif
- {
- #ifndef MCHUNK
- ptr = __mini_malloc(size);
- #endif
- #ifdef VERBOSE
- if( ptr == 0 )
- noise("MALLOC FAIL", 0);
- else
- noise("MALLOC NOW", ptr - 1);
- #endif
- return ptr;
- }
- }
- #ifdef LAZY_FREE
- }
- #endif
- #ifdef VERBOSE
- ptr[1].size = 0x55555555;
- #endif
- noise("MALLOC RET", ptr);
- return ptr + 1;
- }
- /*
- * This function takes a pointer to a block of memory and inserts it into
- * the chain of memory chunks
- */
- Static void
- __insert_chunk(mem_chunk)
- mem *mem_chunk;
- {
- register mem *p1, *p2;
- if (chunk_list == 0) /* Simple case first */
- {
- m_next(mem_chunk) = chunk_list = mem_chunk;
- noise("FIRST CHUNK", mem_chunk);
- return;
- }
- p1 = mem_chunk;
- p2 = chunk_list;
- do
- {
- if (p1 > p2)
- {
- if (m_next(p2) <= p2)
- { /* We're at the top of the chain, p1 is
- * higher */
- if (p2 + m_size(p2) == p1)
- { /* Good, stick 'em together */
- noise("INSERT CHUNK", mem_chunk);
- m_size(p2) += m_size(p1);
- noise("JOIN 1", p2);
- }
- else
- {
- m_next(p1) = m_next(p2);
- m_next(p2) = p1;
- noise("INSERT CHUNK", mem_chunk);
- noise("FROM", p2);
- }
- return;
- }
- if (m_next(p2) > p1)
- {
- /* In chain, p1 between p2 and next */
- m_next(p1) = m_next(p2);
- m_next(p2) = p1;
- noise("INSERT CHUNK", mem_chunk);
- noise("FROM", p2);
- /* Try to join above */
- if (p1 + m_size(p1) == m_next(p1))
- {
- m_size(p1) += m_size(m_next(p1));
- m_next(p1) = m_next(m_next(p1));
- noise("JOIN 2", p1);
- }
- /* Try to join below */
- if (p2 + m_size(p2) == p1)
- {
- m_size(p2) += m_size(p1);
- m_next(p2) = m_next(p1);
- noise("JOIN 3", p2);
- }
- chunk_list = p2; /* Make sure it's valid */
- return;
- }
- }
- else if (p1 < p2)
- {
- if (m_next(p2) <= p2 && p1 < m_next(p2))
- {
- /* At top of chain, next is bottom of chain, p1 is below next */
- m_next(p1) = m_next(p2);
- m_next(p2) = p1;
- noise("INSERT CHUNK", mem_chunk);
- noise("FROM", p2);
- chunk_list = p2;
- if (p1 + m_size(p1) == m_next(p1))
- {
- if (p2 == m_next(p1))
- chunk_list = p1;
- m_size(p1) += m_size(m_next(p1));
- m_next(p1) = m_next(m_next(p1));
- noise("JOIN 4", p1);
- }
- return;
- }
- }
- chunk_list = p2; /* Save for search */
- p2 = m_next(p2);
- }
- while (p2 != chunk_list);
- /* If we get here we have a problem, ignore it, maybe it'll go away */
- noise("DROPPED CHUNK", mem_chunk);
- }
- /*
- * This function will search for a chunk in memory of at least 'mem_size'
- * when found, if the chunk is too big it'll be split, and pointer to the
- * chunk returned. If none is found NULL is returned.
- */
- Static mem *
- __search_chunk(mem_size)
- unsigned int mem_size;
- {
- register mem *p1, *p2;
- if (chunk_list == 0) /* Simple case first */
- return 0;
- /* Search for a block >= the size we want */
- p1 = m_next(chunk_list);
- p2 = chunk_list;
- do
- {
- noise("CHECKED", p1);
- if (m_size(p1) >= mem_size)
- break;
- p2 = p1;
- p1 = m_next(p1);
- }
- while (p2 != chunk_list);
- /* None found, exit */
- if (m_size(p1) < mem_size)
- return 0;
- /* If it's exactly right remove it */
- if (m_size(p1) < mem_size + 2)
- {
- noise("FOUND RIGHT", p1);
- chunk_list = m_next(p2) = m_next(p1);
- if (chunk_list == p1)
- chunk_list = 0;
- return p1;
- }
- noise("SPLIT", p1);
- /* Otherwise split it */
- m_next(p2) = p1 + mem_size;
- chunk_list = p2;
- p2 = m_next(p2);
- m_size(p2) = m_size(p1) - mem_size;
- m_next(p2) = m_next(p1);
- m_size(p1) = mem_size;
- if (chunk_list == p1)
- chunk_list = p2;
- #ifdef VERBOSE
- p1[1].size = 0xAAAAAAAA;
- #endif
- noise("INSERT CHUNK", p2);
- noise("FOUND CHUNK", p1);
- noise("LIST IS", chunk_list);
- return p1;
- }
- #endif /* L_malloc */
- #ifdef L_calloc
- void *
- calloc(elm, sz)
- unsigned int elm, sz;
- {
- register unsigned int v;
- register void *ptr;
- ptr = malloc(v = elm * sz);
- if (ptr)
- memset(ptr, 0, v);
- return ptr;
- }
- #endif /* L_calloc */
- #ifdef L_realloc
- void *
- realloc(ptr, size)
- void *ptr;
- size_t size;
- {
- void *nptr;
- unsigned int osize;
- if (ptr == 0)
- return malloc(size);
- osize = (m_size(((mem *) ptr) - 1) - 1) * sizeof(mem);
- if (size <= osize)
- {
- return ptr;
- }
- nptr = malloc(size);
- if (nptr == 0)
- return 0;
- memcpy(nptr, ptr, osize);
- free(ptr);
- return nptr;
- }
- #endif /* L_realloc */
|