From c9a51a85ea5b869ef399c73015c397304d0ea585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Mon, 11 May 2026 22:37:01 +0200 Subject: [PATCH] Initial --- .clang-format | 16 +++ .gitignore | 3 + .vscode/c_cpp_properties.json | 13 +++ .vscode/settings.json | 14 +++ .vscode/tasks.json | 15 +++ Makefile | 55 +++++++++++ include/lalloc.h | 53 ++++++++++ include/larena.h | 180 ++++++++++++++++++++++++++++++++++ include/lpool.h | 66 +++++++++++++ src/lalloc.c | 170 ++++++++++++++++++++++++++++++++ src/lpool.c | 109 ++++++++++++++++++++ test/lalloc_test.c | 50 ++++++++++ test/larena_test.c | 33 +++++++ test/lpool_test.c | 32 ++++++ 14 files changed, 809 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 Makefile create mode 100644 include/lalloc.h create mode 100644 include/larena.h create mode 100644 include/lpool.h create mode 100644 src/lalloc.c create mode 100644 src/lpool.c create mode 100644 test/lalloc_test.c create mode 100644 test/larena_test.c create mode 100644 test/lpool_test.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6a60e2e --- /dev/null +++ b/.clang-format @@ -0,0 +1,16 @@ +--- +BasedOnStyle: Google +AccessModifierOffset: "-4" +AllowShortFunctionsOnASingleLine: Empty +BreakBeforeBraces: WebKit +IndentWidth: "4" +ColumnLimit: 160 +BreakConstructorInitializers: AfterColon +ConstructorInitializerAllOnOneLineOrOnePerLine: true +SortIncludes: false +PointerAlignment: Right +# clang-format 9: AlignConsecutiveMacros: true +# clang-format 11: AlignConsecutiveBitFields: true +# clang-format 14: PackConstructorInitializers: Never + +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f8ba24 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +.*swp +.gdb_history diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..501a37e --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,13 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${default}", + "${workspaceFolder}/include" + ], + "defines": [] + } + ], + "version": 4 +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5f90913 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "editor.formatOnSave": true, + "editor.insertSpaces": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.associations": { + "*.h": "c", + }, + "C_Cpp.default.includePath": [ + "/opt/homebrew/include/", + "$(workspaceFolder)/include/", + ], + "C_Cpp.default.cStandard": "gnu17" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..e005b6a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "make: build", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + + } + ] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8d39262 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +CC = gcc +LD = $(CC) +CFLAGS = -Wall -Wextra -Wno-unused-parameter -ggdb +CPPFLAGS += -Iinclude +LDFLAGS += +ifndef DEBUG +CFLAGS += -O2 +CPPFLAGS += -DNDEBUG +else +CPPFLAGS += -DDEBUG +endif +BUILDDIR = build + +SOURCES = $(wildcard src/*.c) +OBJECTS = $(addprefix $(BUILDDIR)/,$(SOURCES:.c=.o)) +DEPS = $(addprefix $(BUILDDIR)/,$(SOURCES:.c=.d)) +LIB = liblilalloc +TEST_SOURCES = $(wildcard test/*.c) +TEST_BINS = $(patsubst test/%.c,$(BUILDDIR)/test/%,$(TEST_SOURCES)) + +.PHONY: all +all: $(BUILDDIR)/$(LIB).a + + +$(BUILDDIR)/%.o: %.c + mkdir -p $(@D) + $(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/$(LIB).a: $(OBJECTS) + ar crs $@ $^ + + +$(BUILDDIR)/test/%: test/%.c $(BUILDDIR)/$(LIB).a + mkdir -p $(@D) + $(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -o $@ $< $(BUILDDIR)/$(LIB).a + + +.PHONY: test +test: $(TEST_BINS) + @set -e; \ + for test_bin in $(TEST_BINS); do \ + echo "Running $$test_bin:" ; \ + ./$$test_bin; \ + echo ; \ + done + + + +.PHONY: clean +clean: + rm -rf $(BUILDDIR) + +ifeq (,$(findstring clean, $(MAKECMDGOALS))) +-include $(DEPS) +endif diff --git a/include/lalloc.h b/include/lalloc.h new file mode 100644 index 0000000..c68ac41 --- /dev/null +++ b/include/lalloc.h @@ -0,0 +1,53 @@ +#ifndef LALLOC_H_ +#define LALLOC_H_ + +#include +#include + +#ifdef __ccplusplus +extern "C" { +#endif + +// Internal header added in front of each allocation +typedef struct BlockHeader BlockHeader; +struct BlockHeader { + uint32_t next; // offset from base + uint32_t prev; // offset from base + uint32_t size_and_flags; // size | free_bit + // Padding to ensure we always return 16 byte aligned pointers. + uint8_t pad[4]; +}; +_Static_assert(sizeof(BlockHeader) == 16, "sizeof(BlockHeader) is not 16"); + +/** + * General purpose allocator. + * - Allocated pointers are aligned to 16 bytes. + * - Backing buffer must be aligned to 16 bytes start + * - 4Gb - 16 is max for the allocator backing buffer. + * - Allocation of > 0 bytes returns a size aligned up to 16 bytes + * - Allocation of 0 bytes returns a valid pointer with 0 usable space + * The allocator maintains a doubly linked list of blocks, + * when blocks are free'd, adjacent blocks are merged to keep + * fragmentation to a minimum + */ +typedef struct LAlloc LAlloc; +struct LAlloc { + uint8_t *base; // backing store + uint32_t size; // total size + BlockHeader *heap_start; // first block + BlockHeader *next_alloc; // start of free block search +}; + +void lalloc_init(LAlloc *allocator, void *backing_store, uint32_t size); + +void *lalloc(LAlloc *allocator, uint32_t size); +void lfree(LAlloc *allocator, void *ptr); + +BlockHeader *block_from_offset(const LAlloc *allocator, uint32_t offset); +uint32_t block_size(const BlockHeader *block); +int block_is_free(const BlockHeader *block); + +#ifdef __ccplusplus +} // extern "C" +#endif +#endif diff --git a/include/larena.h b/include/larena.h new file mode 100644 index 0000000..16953a7 --- /dev/null +++ b/include/larena.h @@ -0,0 +1,180 @@ +#ifndef LILARENA__H_ +#define LILARENA__H_ + +#include +#include +#include + +/** @file + * Arena allocator. + * + * An LArena allocates memory from a provided buffer. + * + * The backing buffer should be suitable aligned to the types + * that will be allocated from it. Use .e.g + * @code + * _Alignas(16) uint8_t mem[1024]; + * @endcode + * as the backing buffer. + * + * Normally the arena is used to allocate memory from, reset and reuse the arena. + * + * Temporary allocations can be obtained and released back to he arena via + * larena_tmp_begin()/larena_tmp_end() pair of calls, releassing memory from all + * lalloc() calls between larena_tmp_begin() and larena_tmp_end()the back to the arena. + * + */ +typedef struct LArena LArena; +struct LArena { + uint8_t *start; // start of user buffer + uint8_t *curr; // next alloc point + uint8_t *end; // one past end of user buffer + int tmp_allocs; // level of larena_tmp_begin stacks +}; + +typedef struct LArenaTemp LArenaTemp; +struct LArenaTemp { + uint8_t *alloc_point; + LArena *arena; +}; +/** Initialize a LArena with predefined backing array. + * + * @data must be an array type + * Use as + * @code + * _Alignas(16) uint8_t v[128]; + * LArena a = LARENA_STATIC_INITIALIZER(v); + * @endcode + */ +#define LARENA_STATIC_INITIALIZER(data) LARENA_INITIALIZER((data), sizeof(data)) + +/** Initializer for LArena with predefined memory + * Use as + * @code + * uint8_t v[128]; + * LArena a = LARENA_INITIALIZER(v, sizeof v); + * @endcode + * or + * @code + * uint8_t *m = malloc(128); + * LArena a = LARENA_INITIALIZER(m, 128); + * @endcode + */ +#define LARENA_INITIALIZER(data, sz) {.start = (data), .curr = (data), .end = (data) + sz, .tmp_allocs = 0} + +/** Initialize a LArena with an array as the backing memory to allocate from. + * The macro can be used at local scope to allocate memory automatic on the stack, + * or at global scope where the backing array will have static storage duration + * + * Use as: + * @code + * LARENA_DEFINE(my_allocator, 1024, 16) + * struct foo *foo = larena_alloc(&my_allocator); + * if (foo == NULL) { + * // out of memory + * } + * @endcode + * + * @param var_name Variable name for the allocator + * @param size_ byte size of the backing array + * @param alignment_ alignment of the backing array + */ +#define LARENA_DEFINE(var_name, size_, alignment_) \ + struct { \ + _Alignas(alignment_) uint8_t memory[size_]; \ + } var_name##_arena_memory; \ + LArena var_name = LARENA_STATIC_INITIALIZER(var_name##_arena_memory.memory) + +/** Allocate space for @type_ , returned memory is aligned to requirements + * of @type. + * + * Use as: + * @code + * LARENA_DEFINE(my_allocator, 1024) + * struct foo *foo = LARENA_ALLOC_TYPE(&my_allocator, struct foo); + * // Array types can be used too: + * struct Foo *str = LARENA_ALLOC_TYPE(&my_allocator, struct Foo[32]); + * @endcode + */ +#define LARENA_ALLOC_TYPE(allocator, type_) larena_alloc_aligned((allocator), sizeof(type_), _Alignof(type_)) +// Arena size in bytes +static inline size_t larena_size(const LArena *arena) +{ + return (size_t)(arena->end - arena->start); +} + +// Used bytes of the arena +static inline size_t larena_used(const LArena *arena) +{ + return (size_t)(arena->curr - arena->start); +} + +// Available unused bytes in the arena +static inline size_t larena_available(const LArena *arena) +{ + return larena_size(arena) - larena_used(arena); +} + +// Initialize an LArena to allocate from @mem which is @sz bytes +static inline void larena_init(LArena *arena, void *memory, size_t sz) +{ + arena->start = arena->curr = (uint8_t *)memory; + arena->end = arena->start + sz; +} + +// begin temporary allocation. All allocations afterwards will be released in larena_tmp_end +static inline LArenaTemp larena_tmp_begin(LArena *arena) +{ + LArenaTemp state = {.alloc_point = arena->curr, .arena = arena}; + arena->tmp_allocs++; + return state; +} + +// end temporary allocations, releasing all allocations since the most recent larena_tmp_begin +static inline void larena_tmp_end(LArenaTemp *state) +{ + LArena *arena = state->arena; + assert(arena->tmp_allocs > 0); + arena->curr = state->alloc_point; + arena->tmp_allocs--; +} + +// Allocate @sz bytes from the LArena. Returns NULL if not enough space +static inline void *larena_alloc(LArena *arena, size_t sz) +{ + if (sz > larena_available(arena)) { + return NULL; + } + void *start = arena->curr; + arena->curr += sz; + return start; +} + +// Allocate @sz bytes from the LArena, returned pointer is aligned to @align +// Returns NULL if not enough space +static inline void *larena_alloc_aligned(LArena *arena, size_t sz, unsigned int align) [[gnu::alloc_align(3)]] +{ + assert(align != 0); + assert((align & (align - 1)) == 0); // power of 2 + + uintptr_t curr = (uintptr_t)arena->curr; + uintptr_t aligned = (curr + (align - (uintptr_t)1)) & ~(align - (uintptr_t)1); + uint8_t *start = (uint8_t *)aligned; + + if (sz > (larena_available(arena))) { + return NULL; + } + + arena->curr = start + sz; + + return start; +} + +// reset arena, making all memory available for allocation +// also invalidates any LArenaTemp instances. +static inline void larena_reset(LArena *arena) +{ + arena->curr = arena->start; +} + +#endif diff --git a/include/lpool.h b/include/lpool.h new file mode 100644 index 0000000..88d4576 --- /dev/null +++ b/include/lpool.h @@ -0,0 +1,66 @@ +#ifndef LPOOL_H_ +#define LPOOL_H_ + +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +typedef union LPoolBlock LPoolBlock; +union LPoolBlock { + LPoolBlock *next; // next free block (when not allocated) + unsigned char data[0]; // data when allocated +}; + +/** @file + * Pool allocator. + * + * An LPool allocates fixed size memory chunks from a provided buffer, + * and memory allocations can be free'd in any order. + * + * The backing array should be suitable aligned to the types + * that will be allocated from it. Use .e.g + * @code + * _Alignas(16) char mem[1024]; + * @endcode + * as the backing buffer. + * + */ +typedef struct LPool LPool; +struct LPool { + LPoolBlock *free_list; // free list + LPoolBlock *start; // start of user buffer + LPoolBlock *end; // one past end of user buffer + uint32_t block_size; // aligned size of each block + uint32_t capacity; // number of blocks + uint32_t align; // alignment +}; + +/** Initialize the pool. + * + * Block size will be rounded up by alignment, or to size of a pointer if alignment is less that a pointer. + * Prefer to use lock sizes that's pointer aligned + * + * @param buffer backing buffer for the pool + * @param buffer_size size of @buffer + * @param block_size size of each allocation. + * @param alignment alignment of each block (must be power of 2) + */ +void lpool_init(LPool *pool, void *buffer, size_t buffer_size, uint32_t block_size, uint32_t alignment); + +// Allocate a block from the pool. Returns NULL if no more space +[[gnu::assume_aligned(_Alignof(LPoolBlock))]] +void *lpool_alloc(LPool *pool); + +// Free @p . Returns memory to the pool +void lpool_free(LPool *pool, void *p); + +// Frees all memory in the pool, resetting it to initial state. +void lpool_reset(LPool *pool); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/lalloc.c b/src/lalloc.c new file mode 100644 index 0000000..0a09167 --- /dev/null +++ b/src/lalloc.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include "lalloc.h" + +#define ALIGNMENT sizeof(BlockHeader) +#define ALIGN_UP(n) (((n) + (__typeof__(n))(ALIGNMENT) - 1) & ~((__typeof__(n))(ALIGNMENT) - 1)) +#define ALIGN_DOWN(n) ((n) & ~((__typeof__(n))(ALIGNMENT) - 1)) + +#define NULL_BLOCK (0xffffffff) +#define BLOCK_FREE ((uint32_t)1) + +static inline void block_set_free(BlockHeader *block) +{ + block->size_and_flags |= BLOCK_FREE; +} + +static inline void block_set_used(BlockHeader *block) +{ + block->size_and_flags &= ~BLOCK_FREE; +} + +static inline void block_set_size(BlockHeader *block, uint32_t size) +{ + uint32_t flags = block->size_and_flags & BLOCK_FREE; + block->size_and_flags = size | flags; +} + +uint32_t block_size(const BlockHeader *block) +{ + return block->size_and_flags & ~BLOCK_FREE; +} + +int block_is_free(const BlockHeader *block) +{ + return (block->size_and_flags & BLOCK_FREE) != 0; +} + +inline BlockHeader *block_from_offset(const LAlloc *allocator, uint32_t offset) +{ + if (offset == NULL_BLOCK) { + return NULL; + } + + return (BlockHeader *)((uintptr_t)allocator->base + offset); +} + +static inline uint32_t bh_offset(LAlloc *allocator, BlockHeader *block) +{ + if (block == NULL) { + return NULL_BLOCK; + } + + return (uint32_t)((uintptr_t)block - (uintptr_t)allocator->base); +} + +static void maybe_split_block(LAlloc *allocator, BlockHeader *block, uint32_t size) +{ + // assumes size is already aligned + uint32_t bsize = block_size(block); + + if (bsize < size + sizeof(BlockHeader) + ALIGNMENT) { + return; + } + + uint8_t *base = (uint8_t *)block; + BlockHeader *new_block = (BlockHeader *)(base + sizeof(BlockHeader) + size); + + uint32_t new_size = bsize - size - sizeof(BlockHeader); + + block_set_size(block, size); + block_set_used(block); + + new_block->size_and_flags = new_size | BLOCK_FREE; + new_block->next = block->next; + new_block->prev = bh_offset(allocator, block); + + BlockHeader *next = block_from_offset(allocator, new_block->next); + if (next) { + next->prev = bh_offset(allocator, new_block); + } + + block->next = bh_offset(allocator, new_block); +} + +static void merge_with_next(LAlloc *allocator, BlockHeader *block) +{ + BlockHeader *next = block_from_offset(allocator, block->next); + if (next == NULL || !block_is_free(next)) return; + + uint32_t new_size = block_size(block) + sizeof(BlockHeader) + block_size(next); + + block_set_size(block, new_size); + block->next = next->next; + BlockHeader *nextnext = block_from_offset(allocator, next->next); + if (nextnext) nextnext->prev = bh_offset(allocator, block); + // note - caller have to adjust next_alloc on return +} + +void lalloc_init(LAlloc *allocator, void *backing_store, uint32_t size) +{ + // backing store must be aligned, otherwise all alignment guarantees are lies + assert(((uintptr_t)backing_store & (ALIGNMENT - 1)) == 0); + assert(size >= sizeof(BlockHeader)); + assert(size < 0xfffffff0); + + allocator->base = backing_store; + allocator->size = size; + + BlockHeader *first = backing_store; + // allocator logic requires the first block to be properly aligned + // as well as size being eligned. + uint32_t usable = ALIGN_DOWN(size) - sizeof(BlockHeader); + block_set_free(first); + block_set_size(first, usable); + first->next = NULL_BLOCK; + first->prev = NULL_BLOCK; + + allocator->heap_start = first; + allocator->next_alloc = first; +} + +void *lalloc(LAlloc *allocator, uint32_t size) +{ + if (size > UINT32_MAX - (ALIGNMENT - 1)) { + return NULL; + } + size = ALIGN_UP(size); + + BlockHeader *start = allocator->next_alloc ? allocator->next_alloc : allocator->heap_start; + BlockHeader *curr = start; + + do { + if (block_is_free(curr) && block_size(curr) >= size) { + maybe_split_block(allocator, curr, size); + block_set_used(curr); + BlockHeader *next = block_from_offset(allocator, curr->next); + allocator->next_alloc = next ? next : allocator->heap_start; + return (uint8_t *)curr + sizeof(BlockHeader); + } + BlockHeader *next = block_from_offset(allocator, curr->next); + curr = next ? next : allocator->heap_start; + } while (curr != start); + + return NULL; +} + +void lfree(LAlloc *allocator, void *ptr) +{ + if (!ptr) { + return; + } + + BlockHeader *block = (BlockHeader *)((uint8_t *)ptr - sizeof(BlockHeader)); + + assert((uint8_t *)ptr - sizeof(BlockHeader) >= allocator->base && (uint8_t *)ptr < allocator->base + allocator->size); + // double free detection + assert(!block_is_free(block)); + + block_set_free(block); + merge_with_next(allocator, block); + + BlockHeader *prev = block_from_offset(allocator, block->prev); + if (prev != NULL && block_is_free(prev)) { + merge_with_next(allocator, prev); + allocator->next_alloc = prev; + } else { + allocator->next_alloc = block; + } +} diff --git a/src/lpool.c b/src/lpool.c new file mode 100644 index 0000000..88cce77 --- /dev/null +++ b/src/lpool.c @@ -0,0 +1,109 @@ +#include +#include +#include "lpool.h" + +#define ALIGN_UP(n, alignment) (((n) + (__typeof__(n))(alignment) - 1) & ~((__typeof__(n))(alignment) - 1)) + +void lpool_init(LPool *pool, void *buffer, size_t buffer_size, uint32_t block_size, uint32_t align) +{ + assert(align > 0); + assert((align & (align - 1)) == 0); // power of 2 + + // enforce minimum for storing free list pointers + + if (align < _Alignof(LPoolBlock)) { + align = _Alignof(LPoolBlock); + } + + if (block_size < sizeof(LPoolBlock)) { + block_size = sizeof(LPoolBlock); + } + // ensure initial alignment + uint32_t stride = ALIGN_UP(block_size, align); + + // align first usable block + uintptr_t start_addr = (uintptr_t)buffer; + uintptr_t first_addr = ALIGN_UP(start_addr, align); + size_t padding = first_addr - start_addr; + + if (padding + stride > buffer_size) { + pool->capacity = 0; + pool->free_list = NULL; + } else { + size_t usable = buffer_size - padding; + pool->capacity = usable / stride; + + // build intrusive free list + LPoolBlock *block = (LPoolBlock *)first_addr; + pool->free_list = block; + for (uint32_t i = 0; i < pool->capacity - 1; i++) { + LPoolBlock *next = __builtin_assume_aligned(&block->data[0] + stride, _Alignof(LPoolBlock)); + block->next = next; + block = next; + } + block->next = NULL; // last element + } + pool->block_size = stride; + pool->align = align; + pool->start = buffer; + pool->end = buffer + buffer_size; +} + +void *lpool_alloc(LPool *pool) +{ + if (!pool->free_list) { + return NULL; + } + LPoolBlock *block = pool->free_list; + pool->free_list = block->next; + +#ifdef DEBUG + memset(block, 0xCB, pool->block_size); +#endif + return block->data; +} + +void lpool_free(LPool *pool, void *p) +{ + if (p == NULL) { + return; + } + LPoolBlock *block = p; +#ifdef DEBUG + assert(block >= pool->start && block < pool->end); + assert(((uintptr_t)p - (uintptr_t)pool->start) % pool->block_size == 0); + + // double-free detection + for (LPoolBlock *f = pool->free_list; f; f = f->next) { + if (f == p) { + assert(0 && "Double free detected in pool!"); + return; + } + } + + memset(p, 0xDD, pool->block_size); +#endif + // When not in use, store pointer to next free element inline in the block + block->next = pool->free_list; + pool->free_list = p; +} + +void lpool_reset(LPool *pool) +{ + if (pool->capacity == 0) return; + + uintptr_t start_addr = (uintptr_t)pool->start; + uintptr_t first_addr = ALIGN_UP(start_addr, pool->align); + + // build intrusive free list + LPoolBlock *p = (LPoolBlock *)first_addr; + for (size_t i = 0; i < pool->capacity - 1; ++i) { + LPoolBlock *next = __builtin_assume_aligned(&p->data[0] + pool->block_size, _Alignof(LPoolBlock)); + p->next = next; + p = next; + } + + p->next = NULL; // last element + + pool->free_list = (LPoolBlock *)first_addr; +} diff --git a/test/lalloc_test.c b/test/lalloc_test.c new file mode 100644 index 0000000..849efac --- /dev/null +++ b/test/lalloc_test.c @@ -0,0 +1,50 @@ + +#include +#include +#include +#include "lalloc.h" + +void debug_alloc(const LAlloc *allocator) +{ + puts("--debug start--"); + for (BlockHeader *b = allocator->heap_start; b; b = block_from_offset(allocator, b->next)) { + printf("Block size %u free %d prev %u next %u \n", block_size(b), block_is_free(b), b->prev, b->next); + } + puts("--debug end--"); +} + +[[gnu::aligned(16)]] char buff[1023]; +int main(void) +{ + printf("bh size %zu\n", sizeof(BlockHeader)); +#define SZ 64 + LAlloc a; + char *ptrs[SZ]; + + lalloc_init(&a, buff, sizeof(buff)); + debug_alloc(&a); + ptrs[0] = lalloc(&a, 1023 - 16 * 2 - 144); + ptrs[1] = lalloc(&a, 80); + ptrs[2] = lalloc(&a, 32); + debug_alloc(&a); + lfree(&a, ptrs[0]); + ptrs[3] = lalloc(&a, 12); + ptrs[4] = lalloc(&a, 12); + debug_alloc(&a); + + lfree(&a, ptrs[1]); + lfree(&a, ptrs[2]); + lfree(&a, ptrs[3]); + lfree(&a, ptrs[4]); + + for (int i = 0; i < SZ; i++) { + ptrs[i] = lalloc(&a, rand() % 256 + 1); + } + + for (int i = 0; i < SZ; i++) { + // if (rand() % 2 == 0) { + lfree(&a, ptrs[i]); + // } + } + debug_alloc(&a); +} diff --git a/test/larena_test.c b/test/larena_test.c new file mode 100644 index 0000000..7315458 --- /dev/null +++ b/test/larena_test.c @@ -0,0 +1,33 @@ +#include +#include "larena.h" +static void test_arena_define(void) +{ + puts(__func__); + LARENA_DEFINE(arena, 1024, 32); + printf("size=%zu available=%zu\n", larena_size(&arena), larena_available(&arena)); + + larena_alloc(&arena, 1000); + printf("size=%zu available=%zu\n", larena_size(&arena), larena_available(&arena)); + + LArenaTemp tmp1 = larena_tmp_begin(&arena); + larena_alloc(&arena, 20); + printf("size=%zu available=%zu\n", larena_size(&arena), larena_available(&arena)); + + LArenaTemp tmp2 = larena_tmp_begin(&arena); + larena_alloc(&arena, 4); + printf("size=%zu available=%zu\n", larena_size(&arena), larena_available(&arena)); + + larena_tmp_end(&tmp2); + printf("size=%zu available=%zu\n", larena_size(&arena), larena_available(&arena)); + + larena_tmp_end(&tmp1); + printf("size=%zu available=%zu\n", larena_size(&arena), larena_available(&arena)); + larena_reset(&arena); + printf("size=%zu available=%zu\n", larena_size(&arena), larena_available(&arena)); +} + +int main(int argc, char *argv[]) +{ + test_arena_define(); + return 0; +} diff --git a/test/lpool_test.c b/test/lpool_test.c new file mode 100644 index 0000000..624ad77 --- /dev/null +++ b/test/lpool_test.c @@ -0,0 +1,32 @@ +#include +#include "lpool.h" + +struct TestData { + float x[3]; + float y[3]; + float z[3]; + float w[3]; +}; + +int main(int argc, char *argv[]) +{ + _Alignas(16) uint8_t store[2048]; + LPool pool; + lpool_init(&pool, store, sizeof store, sizeof(struct TestData), _Alignof(struct TestData)); + printf("align=%u capacity=%u block_size=%u\n", pool.align, pool.capacity, pool.block_size); + struct TestData *data = lpool_alloc(&pool); + size_t allocs = 1; + for (;;) { + struct TestData *t = lpool_alloc(&pool); + if (t == NULL) { + break; + } + allocs++; + } + printf("Total allocs = %zu\n", allocs); + printf("alloc succeded = %d\n", lpool_alloc(&pool) != NULL); + lpool_free(&pool, data); + printf("alloc succeded = %d\n", lpool_alloc(&pool) != NULL); + + return 0; +}