Mercurial > hg > forks > libbpg
diff tmalloc.c @ 0:772086c29cc7
Initial import.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 16 Nov 2016 11:16:33 +0200 |
parents | |
children | 33594243ce31 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tmalloc.c Wed Nov 16 11:16:33 2016 +0200 @@ -0,0 +1,314 @@ +/* + * Tiny malloc + * + * Copyright (c) 2014 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <limits.h> +#ifndef MALLOC_TEST +#define NDEBUG +#endif +#include <assert.h> + +/* + * Note: only works for 32 bit pointers + */ +#define MALLOC_ALIGN 8 +#define MALLOC_BLOCK_SIZE 32 + +#define STATE_FREE 0xaa +#define STATE_ALLOCATED 0x55 + +struct list_head { + struct list_head *prev, *next; +}; + +#define list_entry(el, type, member) \ + ((type *)((uint8_t *)(el) - offsetof(type, member))) + +/* Note: the 'state' byte is stored just before the MemBlock header, + so at most 23 bytes can be allocated in a single block. */ +typedef struct MemBlock { + struct list_head link; + union { + uint8_t data[0] __attribute((aligned(MALLOC_ALIGN))); + struct list_head free_link; + } u; +} MemBlock; + +void *sbrk(intptr_t increment); + +/* Invariants: the last block is always a free block. The last free + block is always the last block. */ +static struct list_head free_list; +static struct list_head block_list; +static uint8_t *mem_top; + +/* insert 'el' after prev */ +static void list_add(struct list_head *el, struct list_head *prev) +{ + struct list_head *next = prev->next; + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +static void list_del(struct list_head *el) +{ + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; +} + +static size_t get_alloc_size(size_t size) +{ + size = offsetof(MemBlock, u.data) + size; + /* one more byte for the state byte from the next block */ + size = (size + MALLOC_BLOCK_SIZE) & ~(MALLOC_BLOCK_SIZE - 1); + return size; +} + +/* Note: this size includes the 'state' byte from the next block */ +static size_t get_block_size(MemBlock *p) +{ + uint8_t *end; + struct list_head *el; + el = p->link.next; + if (el == &block_list) + end = mem_top; + else + end = (uint8_t *)list_entry(el, MemBlock, link); + return end - (uint8_t *)p; +} + +static inline void set_block_state(MemBlock *p, int state) +{ + ((uint8_t *)p)[-1] = state; +} + +static inline int get_block_state(const MemBlock *p) +{ + return ((const uint8_t *)p)[-1]; +} + +void *malloc(size_t size) +{ + MemBlock *p, *p1; + struct list_head *el; + size_t block_size; + + if (size == 0 || size > (INT_MAX - 2 * MALLOC_BLOCK_SIZE)) + return NULL; + if (free_list.next == NULL) { + /* init */ + p = sbrk(MALLOC_BLOCK_SIZE * 2); + if (p == (void *)-1) + return NULL; + + mem_top = sbrk(0); + free_list.prev = free_list.next = &free_list; + block_list.prev = block_list.next = &block_list; + p++; + set_block_state(p, STATE_FREE); + list_add(&p->link, &block_list); + list_add(&p->u.free_link, &free_list); + } + + size = get_alloc_size(size); + el = free_list.next; + for(;;) { + p = list_entry(el, MemBlock, u.free_link); + assert(get_block_state(p) == STATE_FREE); + block_size = get_block_size(p); + if (size < block_size) { + goto done1; + } else if (el == free_list.prev) { + /* last free block: increase its size */ + if (sbrk(size + MALLOC_BLOCK_SIZE - block_size) == (void *)-1) + return NULL; + mem_top = sbrk(0); + done1: + p1 = (MemBlock *)((uint8_t *)p + size); + list_add(&p1->link, &p->link); + list_add(&p1->u.free_link, &p->u.free_link); + set_block_state(p1, STATE_FREE); + list_del(&p->u.free_link); + done: + set_block_state(p, STATE_ALLOCATED); + return p->u.data; + } else if (size == block_size) { + list_del(&p->u.free_link); + goto done; + } + el = el->next; + } +} + +void free(void *ptr) +{ + MemBlock *p, *p1; + struct list_head *el; + + if (!ptr) + return; + p = (MemBlock *)((uint8_t *)ptr - offsetof(MemBlock, u.data)); + assert(get_block_state(p) == STATE_ALLOCATED); + + /* mark as free */ + list_add(&p->u.free_link, &free_list); + set_block_state(p, STATE_FREE); + + /* merge with previous free block if possible */ + el = p->link.prev; + if (el != &block_list) { + p1 = list_entry(el, MemBlock, link); + if (get_block_state(p1) == STATE_FREE) { + list_del(&p->link); + list_del(&p->u.free_link); + p = p1; + } + } + /* merge with next block if possible */ + el = p->link.next; + if (el != &block_list) { + p1 = list_entry(el, MemBlock, link); + if (get_block_state(p1) == STATE_FREE) { + list_del(&p1->link); + /* keep p in the same position in free_list as p1 */ + list_del(&p->u.free_link); + list_add(&p->u.free_link, &p1->u.free_link); + list_del(&p1->u.free_link); + } + } +} + +void *realloc(void *ptr, size_t size) +{ + MemBlock *p; + void *ptr1; + size_t size1; + + if (ptr == NULL) { + return malloc(size); + } else if (size == 0) { + free(ptr); + return NULL; + } else { + p = (MemBlock *)((uint8_t *)ptr - offsetof(MemBlock, u.data)); + assert(get_block_state(p) == STATE_ALLOCATED); + ptr1 = malloc(size); + if (!ptr1) + return NULL; + /* Note: never the last block so it is valid */ + size1 = (uint8_t *)list_entry(p->link.next, MemBlock, link) - + p->u.data - 1; + if (size < size1) + size1 = size; + memcpy(ptr1, ptr, size1); + free(ptr); + return ptr1; + } +} + +#ifdef MALLOC_TEST +static void malloc_check(void) +{ + MemBlock *p; + struct list_head *el; + int state; + + for(el = block_list.next; el != &block_list; el = el->next) { + p = list_entry(el, MemBlock, link); + state = get_block_state(p); + assert(state == STATE_FREE || state == STATE_ALLOCATED); + if (el->next != &block_list) + assert(el->next > el); + } + for(el = free_list.next; el != &free_list; el = el->next) { + p = list_entry(el, MemBlock, u.free_link); + assert(get_block_state(p) == STATE_FREE); + } + + /* check invariant */ + el = free_list.prev; + if (el != &free_list) { + p = list_entry(el, MemBlock, u.free_link); + assert(&p->link == block_list.prev); + } +} + +static void malloc_dump(void) +{ + MemBlock *p; + struct list_head *el; + + printf("blocks:\n"); + for(el = block_list.next; el != &block_list; el = el->next) { + p = list_entry(el, MemBlock, link); + printf("block: %p next=%p free=%d size=%u\n", p, p->link.next, + get_block_state(p) == STATE_FREE, + (unsigned int)get_block_size(p)); + } + printf("free list:\n"); + for(el = free_list.next; el != &free_list; el = el->next) { + p = list_entry(el, MemBlock, u.free_link); + printf("block: %p size=%u\n", p, (unsigned int)get_block_size(p)); + } +} + +int main(int argc, char **argv) +{ + int i, n, j, size; + void **tab; + + n = 100; + tab = malloc(sizeof(void *) * n); + memset(tab, 0, n * sizeof(void *)); + + for(i = 0; i < n * 1000; i++) { + j = random() % n; + + free(tab[j]); + + malloc_check(); + + size = random() % 500; + tab[j] = malloc(size); + memset(tab[j], 0x11, size); + + malloc_check(); + } + + malloc_dump(); + + for(i = 0; i < n; i++) { + free(tab[i]); + } + return 0; +} +#endif