diff options
Diffstat (limited to 'qemu/roms/SLOF/lib/libc/stdlib/malloc.c')
-rw-r--r-- | qemu/roms/SLOF/lib/libc/stdlib/malloc.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/lib/libc/stdlib/malloc.c b/qemu/roms/SLOF/lib/libc/stdlib/malloc.c new file mode 100644 index 000000000..b2a3138eb --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/malloc.c @@ -0,0 +1,157 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stddef.h" +#include "stdlib.h" +#include "unistd.h" +#include "malloc_defs.h" + + +static int clean(void); + + +/* act points to the end of the initialized heap and the start of uninitialized heap */ +static char *act; + +/* Pointers to start and end of heap: */ +static char *heap_start, *heap_end; + + +/* + * Standard malloc function + */ +void * +malloc(size_t size) +{ + char *header; + void *data; + size_t blksize; /* size of memory block including the chunk */ + + blksize = size + sizeof(struct chunk); + + /* has malloc been called for the first time? */ + if (act == 0) { + size_t initsize; + /* add some space so we have a good initial playground */ + initsize = (blksize + 0x1000) & ~0x0fff; + /* get initial memory region with sbrk() */ + heap_start = sbrk(initsize); + if (heap_start == (void*)-1) + return NULL; + heap_end = heap_start + initsize; + act = heap_start; + } + + header = act; + data = act + sizeof(struct chunk); + + /* Check if there is space left in the uninitialized part of the heap */ + if (act + blksize > heap_end) { + //search at begin of heap + header = heap_start; + + while ((((struct chunk *) header)->inuse != 0 + || ((struct chunk *) header)->length < size) + && header < act) { + header = header + sizeof(struct chunk) + + ((struct chunk *) header)->length; + } + + // check if heap is full + if (header >= act) { + if (clean()) { + // merging of free blocks succeeded, so try again + return malloc(size); + } else if (sbrk(blksize) == heap_end) { + // succeeded to get more memory, so try again + heap_end += blksize; + return malloc(size); + } else { + // No more memory available + return 0; + } + } + + // Check if we need to split this memory block into two + if (((struct chunk *) header)->length > blksize) { + //available memory is too big + int alt; + + alt = ((struct chunk *) header)->length; + ((struct chunk *) header)->inuse = 1; + ((struct chunk *) header)->length = size; + data = header + sizeof(struct chunk); + + //mark the rest of the heap + header = data + size; + ((struct chunk *) header)->inuse = 0; + ((struct chunk *) header)->length = + alt - blksize; + } else { + //new memory matched exactly in available memory + ((struct chunk *) header)->inuse = 1; + data = header + sizeof(struct chunk); + } + + } else { + + ((struct chunk *) header)->inuse = 1; + ((struct chunk *) header)->length = size; + + act += blksize; + } + + return data; +} + + +/* + * Merge free memory blocks in initialized heap if possible + */ +static int +clean(void) +{ + char *header; + char *firstfree = 0; + char check = 0; + + header = heap_start; + //if (act == 0) // This should never happen + // act = heap_end; + + while (header < act) { + + if (((struct chunk *) header)->inuse == 0) { + if (firstfree == 0) { + /* First free block in a row, only save address */ + firstfree = header; + + } else { + /* more than one free block in a row, merge them! */ + ((struct chunk *) firstfree)->length += + ((struct chunk *) header)->length + + sizeof(struct chunk); + check = 1; + } + } else { + firstfree = 0; + + } + + header = header + sizeof(struct chunk) + + ((struct chunk *) header)->length; + + } + + return check; +} |