diff --git a/Makefile b/Makefile index 5d7a25d..c6123e0 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ test: yatest %.o: %.c $(CC) -c $^ $(CPPFLAGS) $(CFLAGS) -yatest: yatest.o yamalloc.o +yatest: yatest.o yamalloc.o ya_block.o $(CC) -o $@ $^ $(CFLAGS) clean: diff --git a/ya_block.c b/ya_block.c new file mode 100644 index 0000000..13efdef --- /dev/null +++ b/ya_block.c @@ -0,0 +1,172 @@ +/* + * Yet Another Malloc + * ya_block.c + * Defines operations on blocks and boundary tags + */ + +/*----------*/ +/* Includes */ +/*----------*/ + +#include "ya_block.h" +#include "ya_debug.h" + +/*---------*/ +/* Globals */ +/*---------*/ + +intptr_t *heap_start = NULL; // with space for 2 words before +intptr_t *heap_end = NULL; // first block outside heap + +/*----------------------*/ +/* Function definitions */ +/*----------------------*/ + +#ifdef YA_DEBUG +void block_print_range(intptr_t *start, intptr_t *end) { + intptr_t *block; + intptr_t block_size; + for (block = start; block < end; block += block_size) { + block_size = YA_SZ_BLK(block); + printf("Block at %p size = %ld alloc = %d\n", + block, block_size, YA_IS_ALLOC_BLK(block)); + } +} +#endif + +/* Initializes the block's boundary tags. */ +void block_init(intptr_t *block, intptr_t size) { + block[-1] = size; + block[size - 2] = size; +} + +/* Sets the allocated bit in the block's boundary tags. */ +void block_alloc(intptr_t *block) { + intptr_t block_size = YA_SZ_BLK(block); + block[-1] |= 1; + block[block_size-2] |= 1; +} + +/* Erases the allocated bit in the block's boundary tags. */ +void block_free(intptr_t *block) { + intptr_t block_size = YA_SZ_BLK(block); + block[-1] &= -2; + block[block_size-2] &= -2; +} + +/* Returns the size in words of the smallest block that can + * store n_bytes bytes. Takes alignment and boundary tags into account */ +intptr_t block_fit(size_t n_bytes) { + intptr_t n_words = YA_ROUND_DIV(n_bytes, YA_SZ_WORD); // size in words + // round to dword and make space for tags + intptr_t size = 2 + YA_ROUND(n_words, 2); + ya_debug("block_fit: requested = %ld, allocating = %ld * %ld = %ld\n", + n_bytes, size, YA_SZ_WORD, size * YA_SZ_WORD); + return size; +} + +/* Tries to coalesce a block with its previous neighbor. + * Returns a pointer to the coalesced block. */ +intptr_t *block_join_prev(intptr_t *block) { + intptr_t prev_size = YA_SZ_TAG(block[-2]); + intptr_t *prev = block - prev_size; + if (prev <= heap_start || YA_IS_ALLOC_BLK(prev)) { + return block; + } + intptr_t block_size = YA_SZ_BLK(block); + block_init(prev, prev_size + block_size); + ya_debug("block_join_prev: joining %p:%ld and %p:%ld -> %p:%ld\n", + block, block_size, prev, prev_size, prev, prev_size + block_size); + return prev; +} + +/* Tries to colesce a block with its next neighbor. + * Returns the unchanged pointer to the block. */ +intptr_t *block_join_next(intptr_t *block) { + intptr_t block_size = YA_SZ_BLK(block); + intptr_t *next = block + block_size; + if (next >= heap_end || YA_IS_ALLOC_BLK(next)) { + return block; + } + intptr_t next_size = YA_SZ_BLK(next); + block_init(block, block_size + next_size); + ya_debug("block_join_next: joining %p:%ld and %p:%ld -> %p:%ld\n", + block, block_size, next, next_size, block, block_size + next_size); + return block; +} + +/* Tries to coalesce a block with its previous and next neighbors. + * Returns a pointer to the coalesced block. */ +intptr_t *block_join(intptr_t *block) { + if (block > heap_start) { + block = block_join_prev(block); + } + return block_join_next(block); +} + +/* Split the block [block_size] into [size, block_size - size] if possible + * Returns a pointer to the second block or NULL if no split occurred. */ +intptr_t *block_split(intptr_t *block, intptr_t size) { + intptr_t next_size = YA_SZ_BLK(block) - size; + if (next_size < YA_MIN_SZ_BLK) { + return NULL; // not enough space to warrant a split + } + block_init(block, size); + block_init(block + size, next_size); + return block + size; +} + +/* Try to find a free block at least size words big by walking the boundary + * tags. If no block is found the heap is grown adequately. + * Returns a pointer to the block or NULL in case of failure. */ +intptr_t *block_find(intptr_t size) { + intptr_t *block; + intptr_t block_size; + for (block = heap_start; block < heap_end; block += block_size) { + block_size = YA_SZ_BLK(block); + if (!YA_IS_ALLOC_BLK(block) && size <= block_size) { + return block; + } + } + // could not find block, extend heap + return heap_extend(size); +} + +/* Initializes the heap by calling sbrk to allocate some starter memory. + * Sets heap_start and heap_end to their appropriate values. + * Returns the pointer to the start of the heap or NULL in case of failure. */ +intptr_t *heap_init() { + intptr_t size_w = YA_SZ_CHUNK / YA_SZ_WORD; + void *ptr = sbrk(YA_SZ_WORD * (size_w + 2)); + if (ptr == (void*) -1) { + heap_start = NULL; + heap_end = NULL; + return NULL; + } + heap_start = ptr; // cast to intptr_t * + heap_start += 2; // space for the first block[-1] + dword alignment + heap_end = heap_start + size_w; + block_init(heap_start, size_w); + ya_debug("heap_init: start = %p, end = %p, size_w = %ld\n", + heap_start, heap_end, size_w); + return heap_start; +} + +/* Extends the heap by at least size_w words by calling sbrk. + * Returns a pointer to the last (free) block or NULL in case of failure. */ +intptr_t *heap_extend(intptr_t size_w) { + intptr_t size = YA_ROUND(size_w * YA_SZ_WORD, YA_SZ_CHUNK); + size_w = size / YA_SZ_WORD; + ya_debug("heap_extend: size_w = %ld\n", size_w); + void *ptr = sbrk(size); + if (ptr == (void *) - 1) { + return NULL; + } + intptr_t *block = ptr; // == old heap_end + heap_end = block + size_w; + block_init(block, size_w); + ya_debug("heap_extend: old end = %p, new end = %p, size_w = %ld\n", + block, heap_end, size_w); + ya_print_blocks(); + return block_join(ptr); +} diff --git a/ya_block.h b/ya_block.h new file mode 100644 index 0000000..1444650 --- /dev/null +++ b/ya_block.h @@ -0,0 +1,101 @@ +/* + * Yet Another Malloc + * ya_block.h + */ + +#ifndef YA_BLOCK_H +#define YA_BLOCK_H + +/*----------*/ +/* Includes */ +/*----------*/ + +#include // for size_t +#include // for intptr_t + +/*-----------*/ +/* Constants */ +/*-----------*/ + +#define YA_SZ_WORD (sizeof(intptr_t)) // big enough to hold a pointer +#define YA_SZ_DWORD (2 * YA_SZ_WORD) // storage is aligned to a dword +#define YA_SZ_CHUNK 8192 // request memory 8k by 8k from OS + +#define YA_MIN_SZ_BLK 4 // smallest block: dword-aligned with two boundary tags + +/*--------*/ +/* Macros */ +/*--------*/ + +#define YA_IS_ALLOC_TAG(tag) ((tag) & 1) +#define YA_SZ_TAG(tag) ((tag) & -2) + +#define YA_IS_ALLOC_BLK(block) YA_IS_ALLOC_TAG((block)[-1]) +#define YA_SZ_BLK(block) YA_SZ_TAG((block)[-1]) + +#define YA_IS_ALLOC_END(b_end) YA_IS_ALLOC_TAG((b_end)[0]) +#define YA_SZ_END(b_end) YA_SZ_TAG((b_end)[0]) + +#define YA_ROUND_DIV(n, m) (((n) + ((m)-1)) / (m)) +#define YA_ROUND(n, m) (YA_ROUND_DIV(n,m) * (m)) + +/*---------*/ +/* Globals */ +/*---------*/ + +extern intptr_t *heap_start; // with space for 2 words before +extern intptr_t *heap_end ; // first block outside heap + +/*--------------*/ +/* Declarations */ +/*--------------*/ + +/* Initializes the heap by calling sbrk to allocate some starter memory. + * Sets heap_start and heap_end to their appropriate values. + * Returns the pointer to the start of the heap or NULL in case of failure. */ +intptr_t *heap_init(); + +/* Extends the heap by at least size_w words by calling sbrk. + * Returns a pointer to the last (free) block or NULL in case of failure. */ +intptr_t *heap_extend(intptr_t size_w); + +#ifdef YA_DEBUG +/* Prints each block in the range from the block at start to the one at end */ +void block_print_range(intptr_t *start, intptr_t *end); +#endif + +/* Initializes the block's boundary tags. */ +void block_init(intptr_t *block, intptr_t size); + +/* Sets the allocated bit in the block's boundary tags. */ +void block_alloc(intptr_t *block); + +/* Erases the allocated bit in the block's boundary tags. */ +void block_free(intptr_t *block); + +/* Returns the size in words of the smallest block that can + * store n_bytes bytes. Takes alignment and boundary tags into account */ +intptr_t block_fit(size_t n_bytes); + +/* Tries to coalesce a block with its previous neighbor. + * Returns a pointer to the coalesced block. */ +intptr_t *block_join_prev(intptr_t *block); + +/* Tries to colesce a block with its next neighbor. + * Returns the unchanged pointer to the block. */ +intptr_t *block_join_next(intptr_t *block); + +/* Tries to coalesce a block with its previous and next neighbors. + * Returns a pointer to the coalesced block. */ +intptr_t *block_join(intptr_t *block); + +/* Split the block [block_size] into [size, block_size - size] if possible + * Returns a pointer to the second block or NULL if no split occurred. */ +intptr_t *block_split(intptr_t *block, intptr_t size); + +/* Try to find a free block at least size words big by walking the boundary + * tags. If no block is found the heap is grown adequately. + * Returns a pointer to the block or NULL in case of failure. */ +intptr_t *block_find(intptr_t size); + +#endif diff --git a/yamalloc.c b/yamalloc.c index c2fa82e..58733e7 100644 --- a/yamalloc.c +++ b/yamalloc.c @@ -3,11 +3,15 @@ * yamalloc.c */ +/*---------------------*/ /* Feature test macros */ +/*---------------------*/ #define _DEFAULT_SOURCE // for sbrk +/*----------*/ /* Includes */ +/*----------*/ #include #include @@ -15,193 +19,36 @@ #include "yamalloc.h" #include "ya_debug.h" +#include "ya_block.h" +/*-----------*/ /* Constants */ +/*-----------*/ -#define YA_SZ_WORD (sizeof(intptr_t)) // big enough to hold a pointer -#define YA_SZ_DWORD (2 * YA_SZ_WORD) // storage is aligned to a dword -#define YA_SZ_CHUNK 8192 // request memory 8k by 8k from OS - -#define YA_MIN_SZ_BLK 4 // smallest block: dword-aligned with two boundary tags +/*--------*/ /* Macros */ +/*--------*/ -#define YA_IS_ALLOC_TAG(tag) ((tag) & 1) -#define YA_SZ_TAG(tag) ((tag) & -2) - -#define YA_IS_ALLOC_BLK(block) YA_IS_ALLOC_TAG((block)[-1]) -#define YA_SZ_BLK(block) YA_SZ_TAG((block)[-1]) - -#define YA_IS_ALLOC_END(b_end) YA_IS_ALLOC_TAG((b_end)[0]) -#define YA_SZ_END(b_end) YA_SZ_TAG((b_end)[0]) - -#define YA_ROUND_DIV(n, m) (((n) + ((m)-1)) / (m)) -#define YA_ROUND(n, m) (YA_ROUND_DIV(n,m) * (m)) - -/* Globals */ - -intptr_t *heap_start = NULL; // space for 2 words before -intptr_t *heap_end = NULL; // first block outside heap +/*--------------------*/ /* Local declarations */ +/*--------------------*/ intptr_t *heap_init(); intptr_t *heap_extend(intptr_t size); -intptr_t *block_join(intptr_t *block); -intptr_t *block_split(intptr_t *block, intptr_t size); -intptr_t *block_find(intptr_t size); - +/*----------------------*/ /* Function definitions */ +/*----------------------*/ #ifdef YA_DEBUG +/* Print all blocks in the heap */ void ya_print_blocks() { - intptr_t *block; - intptr_t block_size; - for (block = heap_start; block < heap_end; block += block_size) { - block_size = YA_SZ_BLK(block); - printf("Block at %p size = %ld alloc = %d\n", - block, block_size, YA_IS_ALLOC_BLK(block)); - } + block_print_range(heap_start, heap_end); } #endif -/* Initializes the block's boundary tags. */ -void block_init(intptr_t *block, intptr_t size) { - block[-1] = size; - block[size - 2] = size; -} - -/* Sets the allocated bit in the block's boundary tags. */ -void block_alloc(intptr_t *block) { - intptr_t block_size = YA_SZ_BLK(block); - block[-1] |= 1; - block[block_size-2] |= 1; -} - -/* Erases the allocated bit in the block's boundary tags. */ -void block_free(intptr_t *block) { - intptr_t block_size = YA_SZ_BLK(block); - block[-1] &= -2; - block[block_size-2] &= -2; -} - -/* Returns the size in words of the smallest block that can - * store n_bytes bytes. Takes alignment and boundary tags into account */ -intptr_t block_fit(size_t n_bytes) { - intptr_t n_words = YA_ROUND_DIV(n_bytes, YA_SZ_WORD); // size in words - // round to dword and make space for tags - intptr_t size = 2 + YA_ROUND(n_words, 2); - ya_debug("block_fit: requested = %ld, allocating = %ld * %ld = %ld\n", - n_bytes, size, YA_SZ_WORD, size * YA_SZ_WORD); - return size; -} - -/* Tries to coalesce a block with its previous neighbor. - * Returns a pointer to the coalesced block. */ -intptr_t *block_join_prev(intptr_t *block) { - intptr_t prev_size = YA_SZ_TAG(block[-2]); - intptr_t *prev = block - prev_size; - if (prev <= heap_start || YA_IS_ALLOC_BLK(prev)) { - return block; - } - intptr_t block_size = YA_SZ_BLK(block); - block_init(prev, prev_size + block_size); - ya_debug("block_join_prev: joining %p:%ld and %p:%ld -> %p:%ld\n", - block, block_size, prev, prev_size, prev, prev_size + block_size); - return prev; -} - -/* Tries to colesce a block with its next neighbor. - * Returns the unchanged pointer to the block. */ -intptr_t *block_join_next(intptr_t *block) { - intptr_t block_size = YA_SZ_BLK(block); - intptr_t *next = block + block_size; - if (next >= heap_end || YA_IS_ALLOC_BLK(next)) { - return block; - } - intptr_t next_size = YA_SZ_BLK(next); - block_init(block, block_size + next_size); - ya_debug("block_join_next: joining %p:%ld and %p:%ld -> %p:%ld\n", - block, block_size, next, next_size, block, block_size + next_size); - return block; -} - -/* Tries to coalesce a block with its previous and next neighbors. - * Returns a pointer to the coalesced block. */ -intptr_t *block_join(intptr_t *block) { - if (block > heap_start) { - block = block_join_prev(block); - } - return block_join_next(block); -} - -/* Split the block [block_size] into [size, block_size - size] if possible - * Returns a pointer to the second block or NULL if no split occurred. */ -intptr_t *block_split(intptr_t *block, intptr_t size) { - intptr_t next_size = YA_SZ_BLK(block) - size; - if (next_size < YA_MIN_SZ_BLK) { - return NULL; // not enough space to warrant a split - } - block_init(block, size); - block_init(block + size, next_size); - return block + size; -} - -/* Try to find a free block at least size words big by walking the boundary - * tags. If no block is found the heap is grown adequately. - * Returns a pointer to the block or NULL in case of failure. */ -intptr_t *block_find(intptr_t size) { - intptr_t *block; - intptr_t block_size; - for (block = heap_start; block < heap_end; block += block_size) { - block_size = YA_SZ_BLK(block); - if (!YA_IS_ALLOC_BLK(block) && size <= block_size) { - return block; - } - } - // could not find block, extend heap - return heap_extend(size); -} - -/* Initializes the heap by calling sbrk to allocate some starter memory. - * Sets heap_start and heap_end to their appropriate values. - * Returns the pointer to the start of the heap or NULL in case of failure. */ -intptr_t *heap_init() { - intptr_t size_w = YA_SZ_CHUNK / YA_SZ_WORD; - void *ptr = sbrk(YA_SZ_WORD * (size_w + 2)); - if (ptr == (void*) -1) { - heap_start = NULL; - heap_end = NULL; - return NULL; - } - heap_start = ptr; // cast to intptr_t * - heap_start += 2; // space for the first block[-1] + dword alignment - heap_end = heap_start + size_w; - block_init(heap_start, size_w); - ya_debug("heap_init: start = %p, end = %p, size_w = %ld\n", - heap_start, heap_end, size_w); - return heap_start; -} - -/* Extends the heap by at least size_w words by calling sbrk. - * Returns a pointer to the last (free) block or NULL in case of failure. */ -intptr_t *heap_extend(intptr_t size_w) { - intptr_t size = YA_ROUND(size_w * YA_SZ_WORD, YA_SZ_CHUNK); - size_w = size / YA_SZ_WORD; - ya_debug("heap_extend: size_w = %ld\n", size_w); - void *ptr = sbrk(size); - if (ptr == (void *) - 1) { - return NULL; - } - intptr_t *block = ptr; // == old heap_end - heap_end = block + size_w; - block_init(block, size_w); - ya_debug("heap_extend: old end = %p, new end = %p, size_w = %ld\n", - block, heap_end, size_w); - ya_print_blocks(); - return block_join(ptr); -} /* Allocates enough memory to store at least size bytes. * Returns a dword-aligned pointer to the memory or NULL in case of failure. */ diff --git a/yamalloc.h b/yamalloc.h index a37bd15..fd00133 100644 --- a/yamalloc.h +++ b/yamalloc.h @@ -6,7 +6,7 @@ #ifndef YAMALLOC_H #define YAMALLOC_H -#include +#include // for size_t void *malloc(size_t size);