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();