libbpg-0.9.3
This commit is contained in:
commit
b21307932d
266 changed files with 108670 additions and 0 deletions
314
tmalloc.c
Normal file
314
tmalloc.c
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue