ack/lang/cem/libcc.ansi/malloc/malloc.c
2018-06-21 22:33:47 +02:00

151 lines
2.9 KiB
C

#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include "malloc.h"
block_t __mem_root = { &__mem_root, 0 };
block_t* __mem_freelist = &__mem_root;
/* Pulls more memory from the system. */
static block_t* brkmore(size_t nb)
{
uintptr_t bytes;
block_t* p;
if (nb < BRKSIZE)
nb = BRKSIZE;
bytes = nb * sizeof(block_t);
/* Danger, will robinson! sbrk's parameter is *signed*... but malloc() takes a
* size_t. */
if (bytes > INTPTR_MAX)
return NULL;
p = sbrk(bytes);
if (p == (block_t*)-1)
return NULL;
/* Add it to the free list by pretending it's a used block and freeing it. */
p->size = nb;
free(p + 1);
return __mem_freelist;
}
void* malloc(size_t size)
{
block_t* p;
block_t* prev;
size_t nblocks;
/* Add on space for the header; make sure we allocate a round number
* of blocks; avoid overflow. */
nblocks = BLOCKCOUNT(size);
if (nblocks < size)
return NULL;
nblocks /= sizeof(block_t);
prev = __mem_freelist;
p = prev->next;
for (;;)
{
if (p->size == nblocks)
{
/* We found a hole of exactly the right size. Unlink and return it.
* The size field is already set. */
prev->next = p->next;
__mem_freelist = prev;
return (void*)(p + 1);
}
else if (p->size > nblocks)
{
/* We found a hole bigger than we need. We shrink the hole and return
* what's left. */
p->size -= nblocks;
p += p->size; /* p now points at our new block */
p->size = nblocks;
__mem_freelist = prev;
return (void*)(p + 1);
}
if (p == __mem_freelist)
{
/* Uh-oh --- we've gone right round the ring and haven't found
* anything. Get more memory from the system and keep going. */
p = brkmore(nblocks);
if (!p)
return NULL;
}
prev = p;
p = p->next;
}
}
void free(void* ptr)
{
block_t* h = BLOCKOF(ptr);
block_t* p;
if (!ptr)
return;
/* __mem_freelist points into an ordered ring of free blocks. First,
* we run around the ring until we find the last block before this one.
*/
p = __mem_freelist;
for (;;)
{
/* Is h between p and the block after p? If so, h needs to be inserted
* after p, so stop here. */
if ((p < h) && (h < p->next))
break;
/* Is p the last block before the end of the address space? */
if (p >= p->next)
{
/* Is h after p? (That is, will it become the new last block?) */
if (p < h)
break;
/* Is h going to become the new *first* block? */
if (h < p->next)
break;
}
p = p->next;
}
/* If we can, merge the next block onto the end of h. */
if ((h + h->size) == p->next)
{
h->size += p->next->size;
h->next = p->next->next;
}
else
{
/* Otherwise, insert h before p->next. */
h->next = p->next;
}
/* Now try to merge h onto the end of p. */
if ((p + p->size) == h)
{
p->size += h->size;
p->next = h->next;
}
else
{
/* Okay, we couldn't do the merge. Fix up the linked list. */
p->next = h;
}
/* ...and update the ring pointer. */
__mem_freelist = p;
}