diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 5934ec5..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -flint \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml deleted file mode 100644 index a4fac68..0000000 --- a/.idea/editor.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/libfputs.iml b/.idea/libfputs.iml deleted file mode 100644 index f08604b..0000000 --- a/.idea/libfputs.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 0b76fe5..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 2ff26f1..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index fa09448..2fd17e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ set(SOURCES src/crypto.c src/parsing.c src/network.c + src/memory.c ) if ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")) diff --git a/docs/math.md b/docs/math.md index 48f1146..4c5917c 100644 --- a/docs/math.md +++ b/docs/math.md @@ -57,3 +57,11 @@ Works the same as `bresenham()` but uses the `Point` struct instead of `int` ```c Point *bresenham_p(Point p1, Point p2, size_t *sz); ``` + +## is_power_of_two + +Returns `1` if `i` is a power of two, otherwise returns `1`. + +```c +int is_power_of_two(int i); +``` diff --git a/docs/memory.md b/docs/memory.md new file mode 100644 index 0000000..95e4709 --- /dev/null +++ b/docs/memory.md @@ -0,0 +1,99 @@ +# memory + +Custom allocators and memory functions + +# Arena Allocator + +A simple arena-style allocator + +## Structs + +### 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()`. + +```c +typedef struct { + unsigned char* buf; + size_t buf_sz; + size_t offset_cur; + size_t offset_prev; +} ArenaAllocator; +``` + +## Functions + +### arena_init + +Initializes the `ArenaAllocator`. The struct must first be created by the user using `malloc()`, see the example below. +`buf_sz` is the size of the underlying buffer in bytes. + +```c +void arena_init(ArenaAllocator *allocator, size_t buf_sz); + +/* Usage */ +ArenaAllocator *a = malloc(sizeof(ArenaAllocator)); +arena_init(a, 1024); +``` + +### arena_free +Frees `allocator` and its underlying buffer. Users should set `allocator` to `NULL` after calling `arena_free()`. +```c +void arena_free(ArenaAllocator *allocator); + +/* Usage */ +arena_free(allocator); +allocator = NULL; +``` + +### arena_clear + +Resets the offset markers of the arena to `0`, but does not wipe the underlying buffer. Technically, any assigned pointers +will still work and + +```c +void arena_clear(ArenaAllocator *allocator); +``` + + +### *arena_malloc + +Request memory of `size` bytes in length from the arena. Returns `NULL` if the assignment failed. + +```c +void *arena_malloc(ArenaAllocator* allocator, size_t size); +``` + +### arena_resize_buf + +Reallocates the underlying buffer in the arena to `new_sz`. You can grow or shrink the arena using this function. Any +pointers allocated out of the arena are invalid after using this function. + +```c +void arena_resize_buf(ArenaAllocator *allocator, size_t new_sz); +``` + +### *arena_resize + +Resize an allocated pointer from the arena. See the example below for a simple use case + +```c +void *arena_resize(ArenaAllocator *allocator, void *mem, size_t old_sz, size_t new_sz); + +/* Usage */ +int *i = arena_malloc(a, sizeof(int)); +*i = 1; +long *l = arena_resize(a, i1, sizeof(int), sizeof(long)); +assert(*l == 1); +``` + +## Macros + +### arena_sz + +Convenience macro for getting the size of the arena's buffer + +```c +#define arena_sz(a) (a)->buf_sz +``` \ No newline at end of file diff --git a/include/lfmath.h b/include/lfmath.h index 34542bb..09f75a4 100644 --- a/include/lfmath.h +++ b/include/lfmath.h @@ -1,6 +1,8 @@ #ifndef LIBFLINT_H_MATH #define LIBFLINT_H_MATH +#include + #include "lfutility.h" int max_int(int a, int b); @@ -11,6 +13,8 @@ int clamp_int(int i, int low, int high); int binstr_to_int(const char *s); +int is_power_of_two(int i); + Point *bresenham(int x0, int y0, int x1, int y1, size_t *sz); Point *bresenham_p(Point p1, Point p2, size_t *sz); diff --git a/include/lfmemory.h b/include/lfmemory.h new file mode 100644 index 0000000..0aac0c3 --- /dev/null +++ b/include/lfmemory.h @@ -0,0 +1,20 @@ +#ifndef LIBFLINT_H_MEMORY +#define LIBFLINT_H_MEMORY + +#include + +typedef struct { + unsigned char* buf; + size_t buf_sz; + size_t offset_cur; + size_t offset_prev; +} ArenaAllocator; + +void arena_init(ArenaAllocator *allocator, size_t buf_sz); +void arena_free(ArenaAllocator *allocator); +void *arena_malloc(ArenaAllocator* allocator, size_t size); +void arena_resize_buf(ArenaAllocator *allocator, size_t new_sz); +void *arena_resize(ArenaAllocator *allocator, void *mem, size_t old_sz, size_t new_sz); +void arena_clear(ArenaAllocator *allocator); + +#endif // LIBFLINT_H_MEMORY diff --git a/src/math.c b/src/math.c index 53efd33..14e275b 100644 --- a/src/math.c +++ b/src/math.c @@ -77,3 +77,6 @@ Point *bresenham_p(Point p1, Point p2, size_t *sz) { return bresenham(p1.x, p1.y, p2.x, p2.y, sz); } +int is_power_of_two(int i) { + return (i & (i - 1)) == 0; +} diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..f49db3b --- /dev/null +++ b/src/memory.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include "lfmemory.h" + +#define arena_sz(a) (a)->buf_sz + +void arena_init(ArenaAllocator *allocator, size_t buf_sz) { + if (allocator == NULL) { + return; + } + + allocator->buf = malloc(sizeof(unsigned char) * buf_sz); + allocator->buf_sz = buf_sz; + allocator->offset_cur = 0; + allocator->offset_prev = 0; +} + +void arena_free(ArenaAllocator *allocator) { + free(allocator->buf); + free(allocator); +} + +void arena_clear(ArenaAllocator *allocator) { + allocator->offset_cur = 0; + allocator->offset_prev = 0; +} + +static uintptr_t align_forward(uintptr_t ptr, size_t align) { + uintptr_t p, a, m; + if (!is_power_of_two(align)) { + // TODO: Error + } + p = ptr; + a = (uintptr_t)align; + m = p & (a - 1); + + if (m != 0) { + p += a - 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; + + // Push forward to align, then change to relative offset + uintptr_t offset = align_forward(cur_ptr, align); + offset -= (uintptr_t)allocator->buf; + + if (offset + size <= allocator->buf_sz) { + void *ptr = &allocator->buf[offset]; + allocator->offset_prev = offset; + allocator->offset_cur = offset + size; + memset(ptr, 0, size); + return ptr; + } + + // Arena is full + 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; + if (!is_power_of_two(align)) { + // TODO: Error handling + } + + if (old_mem == NULL || old_sz == 0) { + return arena_malloc_align(allocator, new_sz, align); + } + + if (allocator->buf <= mem && mem < allocator->buf + allocator->buf_sz) { + if (allocator->buf + allocator->offset_prev == old_mem) { + allocator->offset_cur = allocator->offset_prev + new_sz; + if (new_sz > old_sz) { + // Zero out memory + memset(&allocator->buf[allocator->offset_cur], 0, new_sz - old_sz); + } + return old_mem; + } + + void *new_mem = arena_malloc_align(allocator, new_sz, align); + size_t copy_size = old_sz < new_sz ? old_sz : new_sz; + memmove(new_mem, old_mem, copy_size); + return new_mem; + } + + return NULL; +} + +void arena_resize_buf(ArenaAllocator *allocator, 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, size_t size) { + return arena_malloc_align(allocator, size, 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); +} + diff --git a/tests/tests.c b/tests/tests.c index 663631f..7258357 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -16,6 +16,7 @@ #include "lfcrypto.h" #include "lfparsing.h" #include "lfinput.h" +#include "lfmemory.h" #if defined(__APPLE__) || defined(__MACH__) #include "lfmacos.h" @@ -468,6 +469,30 @@ void test_macos() { } #endif +void test_memory() { + printf("\n--- MEMORY TEST ---\n"); + ArenaAllocator *a = malloc(sizeof(ArenaAllocator)); + arena_init(a, 1024); + + int *i1 = arena_malloc(a, sizeof(int)); + int *i2 = arena_malloc(a, sizeof(int)); + + *i1 = 1; + *i2 = 2; + + assert(i1 < i2); + assert(*i1 < *i2); + + long *l = arena_resize(a, i1, sizeof(int), sizeof(long)); + assert(*l == 1); + + unsigned char *c = arena_resize(a, i2, sizeof(int), sizeof(unsigned char)); + assert(*c == 2); + + arena_free(a); + printf("Passes all memory tests\n"); +} + int main() { test_ll(); test_set(); @@ -479,6 +504,7 @@ int main() { test_crypto(); test_parsing(); test_network(); + test_memory(); #if defined(__APPLE__) || defined(__MACH__) test_macos();