151 lines
2.9 KiB
C
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;
|
|
}
|