commit - 59fee5094b35f3615cf87b70557e51183d424245
commit + c2fe01f04c367f396e0b649d18e524eaffa648b8
blob - 9cba2e65ed130289ac232a6ecae68ad0623aefa2
blob + 264777d0a1fa6a52b037b51ba0694c74914ab320
--- README.md
+++ README.md
## Documentation
-Extensive documentation can be found [here](https://fputs.com/docs/libflint/)
+Extensive documentation can be found [here](https://fputs.com/docs/libflint/). You can also check out `tests/tests.c` to
+see example usage from most of the library's API.
## Requirements
## Libraries
-`libflint` includes [uthash](https://github.com/troydhanson/uthash) for a hash table implementation. `uthash` is a single header file included in the source code of `libflint`. See the top of `include/uthash.h` for license information
+`libflint` includes [uthash](https://github.com/troydhanson/uthash) for a hash table implementation. `uthash` is a single header file included in the source code of `libflint`. See the top of `include/uthash.h` for license information
\ No newline at end of file
blob - 95e4709d9d572a60f692544d31c005e94627d3d9
blob + 4cf1f9bd9d11ce0b874ac37afa6c232a205d15df
--- docs/memory.md
+++ docs/memory.md
Custom allocators and memory functions
-# Arena Allocator
+## Arena Allocator
A simple arena-style allocator
### ArenaAllocator
Represents an arena allocator. `ArenaAllocator` holds its own buffer, but managing its size is left to the user. Like
-most structs in `libflint`, it must be malloced first before being passed to `arena_init()`.
+most structs in `libflint`, it must be malloc'd first before being passed to `arena_init()`.
```c
typedef struct {
```c
#define arena_sz(a) (a)->buf_sz
+```
+
+## Pool Allocator
+
+A pool-based allocator for chunks of the same size. Useful for quickly allocating and using memory of the same size
+without worrying about order; most operations are `O(1)`
+
+## Structs
+
+### PoolAllocator
+
+Represents a pool allocator. `PoolAllocator` holds its own buffer, but managing its size is left to the user. Like
+most structs in `libflint`, it must be malloc'd first before being passed to `pool_init()`.
+
+```c
+typedef struct {
+ unsigned char *buf;
+ size_t buf_sz;
+ size_t chunk_size;
+
+ List *free_list;
+} PoolAllocator;
+```
+
+## Functions
+
+### pool_init
+
+Initializes the `PoolAllocator`. The struct must first be created by the user using `malloc()`, see the example below.
+
+- `buf_sz`: Size of the underlying buffer in bytes
+- `chunk_sz`: Size of each chunk in bytes
+- `chunk_align`: The alignment of the chunks. The`LF_DEFAULT_ALIGNMENT` macro is available as a good default for basic types
+
+```c
+void pool_init(PoolAllocator *allocator, size_t buf_sz, size_t chunk_sz, size_t chunk_align);
+
+/* Usage */
+PoolAllocator *pool = malloc(sizeof(PoolAllocator));
+pool_init(pool, 64, 16, LF_DEFAULT_ALIGNMENT);
+```
+
+### pool_free
+
+Return a single chunk back to the pool. `ptr` is a pointer to the allocated chunk.
+
+```c
+void pool_free(PoolAllocator *allocator, void *ptr);
+```
+
+### pool_free_all
+
+Returns all chunks back to the pool. Any pointers received before this call are now invalid and must be reasigned with
+`pool_alloc` or set to `NULL`.
+
+```c
+void pool_free_all(PoolAllocator *allocator);
+```
+
+### pool_alloc
+
+Allocate a chunk from the pool. Returns `NULL` on failure.
+
+```c
+void *pool_alloc(PoolAllocator *allocator);
+```
+
+### pool_destroy
+
+Destroys the underlying buffer and `List` in `allocator`, then frees it. User is responsible for setting `allocator` to
+`NULL` afterwards.
+
+```c
+void pool_destroy(PoolAllocator *allocator);
+```
+
+## Macros
+
+### pool_count_available
+
+Returns the amount of chunks left available in the pool
+
+```c
+#define pool_count_available(x) (x)->free_list->size
+```
+
+### LF_DEFAULT_ALIGNMENT
+
+The default alignment for allocators. Use this as a simple default (defaults to 16 bytes on amd64 systems) when storing
+basic types
+
+```c
+#ifndef DEFAULT_ALIGNMENT
+#define LF_DEFAULT_ALIGNMENT (2*sizeof(void*))
+#endif // DEFAULT_ALIGNMENT
```
\ No newline at end of file
blob - 0aac0c339051fca83211429572341b6655965bd0
blob + 9bcb2fd8c09c5f4ce210a98b0d1240e559cd0c60
--- include/lfmemory.h
+++ include/lfmemory.h
#include <stddef.h>
+#include "lflinkedlist.h"
+
+#ifndef DEFAULT_ALIGNMENT
+#define LF_DEFAULT_ALIGNMENT (2*sizeof(void*))
+#endif // DEFAULT_ALIGNMENT
+
typedef struct {
unsigned char* buf;
size_t buf_sz;
void *arena_resize(ArenaAllocator *allocator, void *mem, size_t old_sz, size_t new_sz);
void arena_clear(ArenaAllocator *allocator);
+typedef struct {
+ unsigned char *buf;
+ size_t buf_sz;
+ size_t chunk_size;
+
+ List *free_list;
+} PoolAllocator;
+
+void pool_init(PoolAllocator *allocator, size_t buf_sz, size_t chunk_sz, size_t chunk_align);
+void pool_free(PoolAllocator *allocator, void *ptr);
+void pool_free_all(PoolAllocator *allocator);
+void *pool_alloc(PoolAllocator *allocator);
+void pool_destroy(PoolAllocator *allocator);
+
+#define pool_count_available(x) (x)->free_list->size
+
#endif // LIBFLINT_H_MEMORY
blob - 2debcc006910996058f73fe80e4164fce92aac56
blob + 92300fd9a1d2f3ca5d8c7f587fd4709c4cc80a85
--- mkdocs.yml
+++ mkdocs.yml
- 'Linked List': 'linkedlist.md'
- 'Math': 'math.md'
- 'macOS': 'macos.md'
+ - 'Memory': 'memory.md'
- 'Network': 'network.md'
- 'Parsing': 'parsing.md'
- 'Set': 'set.md'
blob - f49db3be38495fbe7d6d41c118aaf54002386323
blob + 488ad0691f02bab72076981e01e6009ae4aeace9
--- src/memory.c
+++ src/memory.c
allocator->offset_prev = 0;
}
-static uintptr_t align_forward(uintptr_t ptr, size_t align) {
- uintptr_t p, a, m;
+static uintptr_t align_forward_uintptr(const uintptr_t ptr, const uintptr_t align) {
if (!is_power_of_two(align)) {
// TODO: Error
}
- p = ptr;
- a = (uintptr_t)align;
- m = p & (a - 1);
+ uintptr_t p = ptr;
+ const uintptr_t m = p & (align - 1);
if (m != 0) {
- p += a - m;
+ p += align - m;
}
return p;
}
-static void *arena_malloc_align(ArenaAllocator *allocator, size_t size, size_t align) {
- uintptr_t cur_ptr = (uintptr_t)allocator->buf + (uintptr_t)allocator->offset_cur;
+static uintptr_t align_forward_size(const size_t ptr, const size_t align) {
+ if (!is_power_of_two(align)) {
+ // TODO: Error
+ }
+ uintptr_t p = ptr;
+ const uintptr_t m = p & (align - 1);
+ if (m != 0) {
+ p += align - m;
+ }
+ return p;
+}
+
+static void *arena_malloc_align(ArenaAllocator *allocator, const size_t size, size_t align) {
+ uintptr_t cur_ptr = (uintptr_t)allocator->buf + allocator->offset_cur;
+
// Push forward to align, then change to relative offset
- uintptr_t offset = align_forward(cur_ptr, align);
+ uintptr_t offset = align_forward_uintptr(cur_ptr, align);
offset -= (uintptr_t)allocator->buf;
if (offset + size <= allocator->buf_sz) {
return NULL;
}
-static void *arena_resize_align(ArenaAllocator *allocator, void *mem, size_t old_sz, size_t new_sz, size_t align) {
- unsigned char *old_mem = (unsigned char *)mem;
+static void *arena_resize_align(ArenaAllocator *allocator, void *mem, const size_t old_sz, const size_t new_sz, size_t align) {
+ unsigned char *old_mem = mem;
if (!is_power_of_two(align)) {
// TODO: Error handling
}
return NULL;
}
-void arena_resize_buf(ArenaAllocator *allocator, size_t new_sz) {
+void arena_resize_buf(ArenaAllocator *allocator, const size_t new_sz) {
allocator->buf = realloc(allocator->buf, sizeof(unsigned char) * new_sz);
}
-#ifndef DEFAULT_ALIGNMENT
-#define DEFAULT_ALIGNMENT (2*sizeof(void*))
-#endif // DEFAULT_ALIGNMENT
+void *arena_malloc(ArenaAllocator *allocator, const size_t size) {
+ return arena_malloc_align(allocator, size, LF_DEFAULT_ALIGNMENT);
+}
-void *arena_malloc(ArenaAllocator *allocator, size_t size) {
- return arena_malloc_align(allocator, size, DEFAULT_ALIGNMENT);
+void *arena_resize(ArenaAllocator *allocator, void *mem, const size_t old_sz, const size_t new_sz) {
+ return arena_resize_align(allocator, mem, old_sz, new_sz, LF_DEFAULT_ALIGNMENT);
}
-void *arena_resize(ArenaAllocator *allocator, void *mem, size_t old_sz, size_t new_sz) {
- return arena_resize_align(allocator, mem, old_sz, new_sz, DEFAULT_ALIGNMENT);
+void pool_init(PoolAllocator *allocator, size_t buf_sz, size_t chunk_sz, size_t chunk_align) {
+ if (allocator == NULL) {
+ return;
+ }
+
+ allocator->buf = malloc(sizeof(unsigned char) * buf_sz);
+ uintptr_t istart = (uintptr_t)allocator->buf;
+ uintptr_t start = align_forward_uintptr(istart, chunk_align);
+ allocator->buf_sz = buf_sz - (start - istart);
+
+ allocator->chunk_size = align_forward_size(chunk_sz, chunk_align);
+ if (allocator->chunk_size < sizeof(void *) || allocator->buf_sz < allocator->chunk_size) {
+ //TODO: Handle error better
+ return;
+ }
+
+ allocator->free_list = malloc(sizeof(List));
+ ll_init(allocator->free_list, NULL);
+
+ pool_free_all(allocator);
}
+void pool_free(PoolAllocator *allocator, void *ptr) {
+ ListNode *node = NULL;
+ const void *start = allocator->buf;
+ const void *end = &allocator->buf[allocator->buf_sz];
+
+ if (ptr == NULL) {
+ return;
+ }
+
+ if (!(start <= ptr && ptr < end)) {
+ // TODO: Handle error better
+ return;
+ }
+
+ ll_ins_next(allocator->free_list, allocator->free_list->tail, ptr);
+}
+
+void pool_free_all(PoolAllocator *allocator) {
+ size_t chunk_count = allocator->buf_sz / allocator->chunk_size;
+ for (size_t i = 0; i < chunk_count; ++i) {
+ ll_ins_next(allocator->free_list, allocator->free_list->head, &allocator->buf[i * allocator->chunk_size]);
+ }
+}
+
+void *pool_alloc(PoolAllocator *allocator) {
+ ListNode *node = allocator->free_list->head;
+ if (node == NULL) {
+ // TODO: Handle error better
+ return NULL;
+ }
+
+ void *tmp;
+ ll_remove(allocator->free_list, allocator->free_list->head, &tmp);
+ return memset(tmp, 0, allocator->chunk_size);
+}
+
+void pool_destroy(PoolAllocator *allocator) {
+ ll_destroy(allocator->free_list);
+ free(allocator->free_list);
+ free(allocator->buf);
+ free(allocator);
+}
blob - bab701fb776df5b8b0f3ea23dc82bea98513860d
blob + dd8d3030cda7f370c807c1542cfa63c45f924233
--- src/vector.c
+++ src/vector.c
vec->capacity = vec_len(vec);
-#ifdef __OpenBSD__
+#if !defined(__OpenBSD__) || defined(__linux)
vec->elements = reallocarray(vec->elements, vec->capacity, sizeof(void *));
#else
vec->elements = reallocf(vec->elements, sizeof(void *) * vec->capacity);
blob - 725835799545adfb64abdf73c9c56d2acf7de59f
blob + ed45525483916ceb313c69c4254b3aa9c84e38a0
--- tests/tests.c
+++ tests/tests.c
void test_memory() {
printf("\n--- MEMORY TEST ---\n");
- ArenaAllocator *a = malloc(sizeof(ArenaAllocator));
- arena_init(a, 1024);
+ ArenaAllocator *arena = malloc(sizeof(ArenaAllocator));
+ arena_init(arena, 1024);
- int *i1 = arena_malloc(a, sizeof(int));
- int *i2 = arena_malloc(a, sizeof(int));
+ int *i1 = arena_malloc(arena, sizeof(int));
+ int *i2 = arena_malloc(arena, sizeof(int));
*i1 = 1;
*i2 = 2;
assert(i1 < i2);
assert(*i1 < *i2);
- long *l = arena_resize(a, i1, sizeof(int), sizeof(long));
+ long *l = arena_resize(arena, i1, sizeof(int), sizeof(long));
assert(*l == 1);
- unsigned char *c = arena_resize(a, i2, sizeof(int), sizeof(unsigned char));
- assert(*c == 2);
+ unsigned char *char_test = arena_resize(arena, i2, sizeof(int), sizeof(unsigned char));
+ assert(*char_test == 2);
- arena_free(a);
+ arena_free(arena);
+ arena = NULL;
+
+ PoolAllocator *pool = malloc(sizeof(PoolAllocator));
+ pool_init(pool, 64, 16, LF_DEFAULT_ALIGNMENT);
+ void *a = pool_alloc(pool);
+ void *b = pool_alloc(pool);
+ void *c = pool_alloc(pool);
+ void *d = pool_alloc(pool);
+
+ assert(a != NULL);
+ assert(b != NULL);
+ assert(c != NULL);
+ assert(d != NULL);
+
+ assert(pool_count_available(pool) == 0);
+ pool_free(pool, d);
+ d = NULL;
+ assert(pool_count_available(pool) == 1);
+
+ pool_destroy(pool);
printf("Passes all memory tests\n");
}