summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/kit/CMakeLists.txt11
-rw-r--r--source/kit/allocator.c18
-rw-r--r--source/kit/allocator.h25
-rw-r--r--source/kit/array_ref.c41
-rw-r--r--source/kit/array_ref.h61
-rw-r--r--source/kit/async_function.h12
-rw-r--r--source/kit/dynamic_array.c48
-rw-r--r--source/kit/dynamic_array.h108
-rw-r--r--source/kit/input_buffer.c107
-rw-r--r--source/kit/input_buffer.h31
-rw-r--r--source/kit/input_stream.c62
-rw-r--r--source/kit/input_stream.h32
-rw-r--r--source/kit/string_ref.c1
-rw-r--r--source/kit/string_ref.h20
-rw-r--r--source/kit_test/test.h2
-rw-r--r--source/test/unittests/CMakeLists.txt3
-rw-r--r--source/test/unittests/array_ref.test.c42
-rw-r--r--source/test/unittests/async_function.test.c2
-rw-r--r--source/test/unittests/dynamic_array.test.c174
-rw-r--r--source/test/unittests/input_buffer.test.c20
-rw-r--r--source/test/unittests/input_stream.test.c23
21 files changed, 832 insertions, 11 deletions
diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt
index 24d1aed..761f572 100644
--- a/source/kit/CMakeLists.txt
+++ b/source/kit/CMakeLists.txt
@@ -1,6 +1,13 @@
target_sources(
${KIT_LIBRARY}
PRIVATE
- async_function.c
+ input_buffer.c input_stream.c string_ref.c
+ async_function.c allocator.c array_ref.c dynamic_array.c
PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/async_function.h>)
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/allocator.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/string_ref.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/dynamic_array.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/async_function.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_stream.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_buffer.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/array_ref.h>)
diff --git a/source/kit/allocator.c b/source/kit/allocator.c
new file mode 100644
index 0000000..fbad11f
--- /dev/null
+++ b/source/kit/allocator.c
@@ -0,0 +1,18 @@
+#include "allocator.h"
+
+#include <stdlib.h>
+
+static void *allocate(void *_, size_t size) {
+ return malloc(size);
+}
+
+static void deallocate(void *_, void *pointer) {
+ free(pointer);
+}
+
+struct kit_allocator kit_alloc_default() {
+ struct kit_allocator alloc = { .state = NULL,
+ .allocate = allocate,
+ .deallocate = deallocate };
+ return alloc;
+}
diff --git a/source/kit/allocator.h b/source/kit/allocator.h
new file mode 100644
index 0000000..24ad5aa
--- /dev/null
+++ b/source/kit/allocator.h
@@ -0,0 +1,25 @@
+#ifndef KIT_ALLOCATOR_H
+#define KIT_ALLOCATOR_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *(*kit_allocate_fn)(void *state, size_t size);
+typedef void (*kit_deallocate_fn)(void *state, void *pointer);
+
+struct kit_allocator {
+ void *state;
+ kit_allocate_fn allocate;
+ kit_deallocate_fn deallocate;
+};
+
+struct kit_allocator kit_alloc_default();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/array_ref.c b/source/kit/array_ref.c
new file mode 100644
index 0000000..2e46324
--- /dev/null
+++ b/source/kit/array_ref.c
@@ -0,0 +1,41 @@
+#include "array_ref.h"
+
+#include <string.h>
+
+_Bool ar_equal_bytes(ptrdiff_t left_element_size, ptrdiff_t left_size,
+ void const *left_data,
+ ptrdiff_t right_element_size,
+ ptrdiff_t right_size, void const *right_data) {
+ if (left_element_size != right_element_size)
+ return 0;
+ if (left_size != right_size)
+ return 0;
+ for (ptrdiff_t i = 0; i < left_size; i++)
+ if (memcmp((char const *) left_data + i * left_element_size,
+ (char const *) right_data + i * left_element_size,
+ left_element_size) != 0)
+ return 0;
+ return 1;
+}
+
+int ar_compare(ptrdiff_t left_element_size, ptrdiff_t left_size,
+ void const *left_data, ptrdiff_t right_element_size,
+ ptrdiff_t right_size, void const *right_data,
+ ar_compare_fn compare) {
+ if (left_element_size < right_element_size)
+ return -1;
+ if (left_element_size > right_element_size)
+ return 1;
+ for (ptrdiff_t i = 0; i < left_size && i < right_size; i++) {
+ int const c = compare(
+ (char const *) left_data + i * left_element_size,
+ (char const *) right_data + i * left_element_size);
+ if (c != 0)
+ return c;
+ }
+ if (left_size < right_size)
+ return -1;
+ if (left_size > right_size)
+ return 1;
+ return 0;
+}
diff --git a/source/kit/array_ref.h b/source/kit/array_ref.h
new file mode 100644
index 0000000..649af8b
--- /dev/null
+++ b/source/kit/array_ref.h
@@ -0,0 +1,61 @@
+#ifndef KIT_ARRAY_REF_H
+#define KIT_ARRAY_REF_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*ar_compare_fn)(void const *left, void const *right);
+
+_Bool ar_equal_bytes(ptrdiff_t left_element_size, ptrdiff_t left_size,
+ void const *left_data,
+ ptrdiff_t right_element_size,
+ ptrdiff_t right_size, void const *right_data);
+
+int ar_compare(ptrdiff_t left_element_size, ptrdiff_t left_size,
+ void const *left_data, ptrdiff_t right_element_size,
+ ptrdiff_t right_size, void const *right_data,
+ ar_compare_fn compare);
+
+#define AR(name_, type_) \
+ struct { \
+ ptrdiff_t size; \
+ type_ *values; \
+ } name_
+
+#define AR_CONST(name_, type_) \
+ struct { \
+ ptrdiff_t size; \
+ type_ const *values; \
+ } name_
+
+#define AR_TYPE(name_, element_type_) \
+ struct name_ { \
+ ptrdiff_t size; \
+ element_type_ *values; \
+ }
+
+#define AR_TYPE_CONST(name_, element_type_) \
+ struct name_ { \
+ ptrdiff_t size; \
+ element_type_ const *values; \
+ }
+
+#define AR_EQUAL(left_, right_) \
+ ar_equal_bytes(sizeof((left_).values[0]), (left_).size, \
+ (left_).values, sizeof((right_).values[0]), \
+ (right_).size, (right_).values)
+
+#define AR_COMPARE(left_, right_, compare_) \
+ ar_compare(sizeof((left_).values[0]), (left_).size, \
+ (left_).values, sizeof((right_).values[0]), \
+ (right_).size, (right_).values, \
+ (ar_compare_fn) (compare_))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/async_function.h b/source/kit/async_function.h
index 77a88ef..69b9453 100644
--- a/source/kit/async_function.h
+++ b/source/kit/async_function.h
@@ -1,15 +1,15 @@
-#ifndef AF_AF_H
-#define AF_AF_H
+#ifndef KIT_ASYNC_FUNCTION_H
+#define KIT_ASYNC_FUNCTION_H
#ifdef __cplusplus
extern "C" {
#endif
enum af_request {
- af_request_resume,
- af_request_join,
- af_request_resume_and_join,
- af_request_execute
+ af_request_execute = 0,
+ af_request_resume = 1,
+ af_request_join = 2,
+ af_request_resume_and_join = 3
};
typedef struct {
diff --git a/source/kit/dynamic_array.c b/source/kit/dynamic_array.c
new file mode 100644
index 0000000..a3ba3dc
--- /dev/null
+++ b/source/kit/dynamic_array.c
@@ -0,0 +1,48 @@
+#include "dynamic_array.h"
+
+#include <string.h>
+
+void da_init(struct da_void *array, ptrdiff_t element_size,
+ ptrdiff_t size, struct kit_allocator alloc) {
+ memset(array, 0, sizeof(struct da_void));
+
+ if (size > 0)
+ array->values = alloc.allocate(alloc.state, element_size * size);
+
+ if (array->values != NULL) {
+ array->capacity = size;
+ array->size = size;
+ }
+
+ array->alloc = alloc;
+}
+
+static ptrdiff_t eval_capacity(ptrdiff_t current_cap,
+ ptrdiff_t required_cap) {
+ if (current_cap == 0)
+ return required_cap;
+ ptrdiff_t cap = current_cap;
+ while (cap < required_cap) cap *= 2;
+ return cap;
+}
+
+void da_resize(struct da_void *array, ptrdiff_t element_size,
+ ptrdiff_t size) {
+ if (size <= array->capacity) {
+ array->size = size;
+ } else {
+ ptrdiff_t capacity = eval_capacity(array->capacity, size);
+
+ void *bytes = array->alloc.allocate(array->alloc.state,
+ element_size * capacity);
+ if (bytes != NULL) {
+ if (array->size > 0)
+ memcpy(bytes, array->values, element_size * array->size);
+ if (array->values != NULL)
+ array->alloc.deallocate(array->alloc.state, array->values);
+ array->capacity = capacity;
+ array->size = size;
+ array->values = bytes;
+ }
+ }
+} \ No newline at end of file
diff --git a/source/kit/dynamic_array.h b/source/kit/dynamic_array.h
new file mode 100644
index 0000000..245da63
--- /dev/null
+++ b/source/kit/dynamic_array.h
@@ -0,0 +1,108 @@
+#ifndef KIT_DYNAMIC_ARRAY_H
+#define KIT_DYNAMIC_ARRAY_H
+
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct da_void {
+ ptrdiff_t capacity;
+ ptrdiff_t size;
+ void *values;
+ struct kit_allocator alloc;
+};
+
+void da_init(struct da_void *array, ptrdiff_t element_size,
+ ptrdiff_t size, struct kit_allocator alloc);
+
+void da_resize(struct da_void *array, ptrdiff_t element_size,
+ ptrdiff_t size);
+
+/* Declare dynamic array type.
+ */
+#define DA_TYPE(name_, element_type_) \
+ struct name_ { \
+ ptrdiff_t capacity; \
+ ptrdiff_t size; \
+ element_type_ *values; \
+ struct kit_allocator alloc; \
+ }
+
+/* Declare dynamic array.
+ */
+#define DA(name_, element_type_) \
+ struct { \
+ ptrdiff_t capacity; \
+ ptrdiff_t size; \
+ element_type_ *values; \
+ struct kit_allocator alloc; \
+ } name_
+
+/* Initialize dynamic array.
+ */
+#define DA_INIT(array_, size_, alloc_) \
+ da_init((struct da_void *) &(array_), sizeof((array_).values[0]), \
+ (size_), (alloc_))
+
+/* Declare and initialize dynamic array.
+ */
+#define DA_CREATE(name_, element_type_, size_) \
+ DA(name_, element_type_); \
+ DA_INIT(name_, (size_), kit_alloc_default())
+
+/* Destroy dynamic array.
+ */
+#define DA_DESTROY(array_) \
+ { \
+ if ((array_).values != NULL) \
+ (array_).alloc.deallocate((array_).alloc.state, \
+ (array_).values); \
+ }
+
+/* Resize dynamic array.
+ */
+#define DA_RESIZE(array_, size_) \
+ da_resize((struct da_void *) &(array_), \
+ sizeof((array_).values[0]), size_)
+
+/* Append a value to dynamic array.
+ */
+#define DA_APPEND(array_, value_) \
+ { \
+ ptrdiff_t const kit_index_back_ = (array_).size; \
+ DA_RESIZE((array_), kit_index_back_ + 1); \
+ if (kit_index_back_ < (array_).size) \
+ (array_).values[kit_index_back_] = (value_); \
+ }
+
+/* Insert a value into dynamic array.
+ */
+#define DA_INSERT(array_, index_, value_) \
+ { \
+ ptrdiff_t const kit_index_back_ = (array_).size; \
+ ptrdiff_t const kit_indert_n_ = (index_); \
+ DA_RESIZE((array_), kit_index_back_ + 1); \
+ if (kit_index_back_ + 1 == (array_).size) { \
+ for (ptrdiff_t kit_i_ = kit_index_back_; \
+ kit_i_ > kit_indert_n_; kit_i_--) \
+ (array_).values[kit_i_] = (array_).values[kit_i_ - 1]; \
+ (array_).values[kit_indert_n_] = (value_); \
+ } \
+ }
+
+/* Erase a value from dynamic array.
+ */
+#define DA_ERASE(array_, index_) \
+ { \
+ for (ptrdiff_t i_ = (index_) + 1; i_ < (array_).size; i_++) \
+ (array_).values[i_ - 1] = (array_).values[i_]; \
+ DA_RESIZE((array_), (array_).size - 1); \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/input_buffer.c b/source/kit/input_buffer.c
new file mode 100644
index 0000000..e1e0cea
--- /dev/null
+++ b/source/kit/input_buffer.c
@@ -0,0 +1,107 @@
+#include "input_buffer.h"
+
+#include <string.h>
+
+struct internal_buffer {
+ ptrdiff_t ref_count;
+ struct is_handle upstream;
+ struct kit_allocator alloc;
+ DA(data, char);
+};
+
+static struct internal_buffer *buf_init(struct is_handle upstream,
+ struct kit_allocator alloc) {
+ struct internal_buffer *buf;
+ buf = alloc.allocate(alloc.state, sizeof *buf);
+ if (buf != NULL) {
+ memset(buf, 0, sizeof *buf);
+ buf->ref_count = 1;
+ buf->upstream = upstream;
+ buf->alloc = alloc;
+ DA_INIT(buf->data, 0, alloc);
+ }
+ return buf;
+}
+
+static struct kit_allocator buf_alloc(void *p) {
+ return ((struct internal_buffer *) p)->alloc;
+}
+
+static void buf_acquire(void *p) {
+ struct internal_buffer *buf = (struct internal_buffer *) p;
+ buf->ref_count++;
+}
+
+static void buf_release(void *p) {
+ struct internal_buffer *buf = (struct internal_buffer *) p;
+ if (--buf->ref_count == 0) {
+ DA_DESTROY(buf->data);
+ buf->alloc.deallocate(buf->alloc.state, buf);
+ }
+}
+
+static void buf_adjust(void *p, ptrdiff_t size) {
+ struct internal_buffer *buf = (struct internal_buffer *) p;
+ ptrdiff_t offset = buf->data.size;
+ if (offset < size) {
+ DA_RESIZE(buf->data, size);
+ out_str destination = { .size = size - offset,
+ .values = buf->data.values + offset };
+ ptrdiff_t n = IS_READ(buf->upstream, destination);
+ DA_RESIZE(buf->data, offset + n);
+ }
+}
+
+static ptrdiff_t min(ptrdiff_t a, ptrdiff_t b) {
+ if (a < b)
+ return a;
+ return b;
+}
+
+static ptrdiff_t buf_read(void *p, ptrdiff_t offset,
+ out_str destination) {
+ struct internal_buffer *buf = (struct internal_buffer *) p;
+ ptrdiff_t n = min(destination.size, buf->data.size - offset);
+ memcpy(destination.values, buf->data.values + offset, n);
+ return n;
+}
+
+struct ib_handle ib_wrap(struct is_handle upstream,
+ struct kit_allocator alloc) {
+ struct ib_handle buf;
+ memset(&buf, 0, sizeof buf);
+ buf.error = 0;
+ DA_INIT(buf.data, 0, alloc);
+ buf.internal = buf_init(upstream, alloc);
+ if (buf.internal == NULL)
+ buf.error = 1;
+ return buf;
+}
+
+struct ib_handle ib_read(struct ib_handle buf, ptrdiff_t size) {
+ struct ib_handle next;
+ memset(&next, 0, sizeof next);
+ if (buf.error) {
+ next.error = 1;
+ } else {
+ buf_acquire(buf.internal);
+ buf_adjust(buf.internal, buf.offset + size);
+ DA_INIT(next.data, size, buf_alloc(buf.internal));
+ if (next.data.size != size)
+ next.error = 1;
+ out_str destination = { .size = next.data.size,
+ .values = next.data.values };
+ ptrdiff_t n = buf_read(buf.internal, buf.offset, destination);
+ next.offset = buf.offset + n;
+ next.internal = buf.internal;
+ DA_RESIZE(next.data, n);
+ if (next.data.size != n)
+ next.error = 1;
+ }
+ return next;
+}
+
+void ib_destroy(struct ib_handle buf) {
+ buf_release(buf.internal);
+ DA_DESTROY(buf.data);
+}
diff --git a/source/kit/input_buffer.h b/source/kit/input_buffer.h
new file mode 100644
index 0000000..38ece8d
--- /dev/null
+++ b/source/kit/input_buffer.h
@@ -0,0 +1,31 @@
+#ifndef KIT_INPUT_BUFFER_H
+#define KIT_INPUT_BUFFER_H
+
+#include "dynamic_array.h"
+#include "input_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ib_handle {
+ _Bool error;
+ ptrdiff_t offset;
+ void *internal;
+ DA(data, char);
+};
+
+struct ib_handle ib_wrap(struct is_handle upstream,
+ struct kit_allocator alloc);
+
+struct ib_handle ib_read(struct ib_handle buf, ptrdiff_t size);
+
+void ib_destroy(struct ib_handle buf);
+
+#define IB_WRAP(upstream) ib_wrap(upstream, kit_alloc_default())
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/input_stream.c b/source/kit/input_stream.c
new file mode 100644
index 0000000..500e83d
--- /dev/null
+++ b/source/kit/input_stream.c
@@ -0,0 +1,62 @@
+#include "input_stream.h"
+
+#include <string.h>
+
+enum input_stream_type { input_stream_cstr };
+
+struct is_state_basic {
+ ptrdiff_t type;
+ struct kit_allocator alloc;
+};
+
+struct is_state_cstr {
+ ptrdiff_t type;
+ struct kit_allocator alloc;
+ cstr string;
+};
+
+static _Bool check_type(void *state, ptrdiff_t type) {
+ struct is_state_basic *basic = (struct is_state_basic *) state;
+ return basic != NULL && basic->type == type;
+}
+
+static ptrdiff_t min(ptrdiff_t a, ptrdiff_t b) {
+ if (a < b)
+ return a;
+ return b;
+}
+
+static ptrdiff_t read_cstr(void *state, out_str destination) {
+ if (!check_type(state, input_stream_cstr))
+ return 0;
+ struct is_state_cstr *cstr = (struct is_state_cstr *) state;
+ ptrdiff_t size = min(destination.size, cstr->string.size);
+ memcpy(destination.values, cstr->string.values, size);
+ cstr->string.values += size;
+ cstr->string.size -= size;
+ return size;
+}
+
+struct is_handle is_wrap_string(cstr string,
+ struct kit_allocator alloc) {
+ struct is_handle in;
+ memset(&in, 0, sizeof in);
+ struct is_state_cstr *state =
+ (struct is_state_cstr *) alloc.allocate(
+ alloc.state, sizeof(struct is_state_cstr));
+ if (state != NULL) {
+ memset(state, 0, sizeof *state);
+ state->type = input_stream_cstr;
+ state->string = string;
+ state->alloc = alloc;
+ in.state = state;
+ in.read = read_cstr;
+ }
+ return in;
+}
+
+void is_destroy(struct is_handle in) {
+ struct is_state_basic *basic = (struct is_state_basic *) in.state;
+ if (basic != NULL)
+ basic->alloc.deallocate(basic->alloc.state, in.state);
+}
diff --git a/source/kit/input_stream.h b/source/kit/input_stream.h
new file mode 100644
index 0000000..c59a720
--- /dev/null
+++ b/source/kit/input_stream.h
@@ -0,0 +1,32 @@
+#ifndef KIT_INPUT_STREAM_H
+#define KIT_INPUT_STREAM_H
+
+#include "allocator.h"
+#include "string_ref.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef ptrdiff_t (*is_read_fn)(void *state, out_str destination);
+
+struct is_handle {
+ void *state;
+ is_read_fn read;
+};
+
+struct is_handle is_wrap_string(cstr string,
+ struct kit_allocator alloc);
+
+void is_destroy(struct is_handle in);
+
+#define IS_WRAP_STRING(string) \
+ is_wrap_string((string), kit_alloc_default())
+
+#define IS_READ(in, destination) (in).read((in).state, (destination))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/string_ref.c b/source/kit/string_ref.c
new file mode 100644
index 0000000..b7d385f
--- /dev/null
+++ b/source/kit/string_ref.c
@@ -0,0 +1 @@
+#include "string_ref.h"
diff --git a/source/kit/string_ref.h b/source/kit/string_ref.h
new file mode 100644
index 0000000..d1af90f
--- /dev/null
+++ b/source/kit/string_ref.h
@@ -0,0 +1,20 @@
+#ifndef KIT_STRING_REF_H
+#define KIT_STRING_REF_H
+
+#include "array_ref.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+AR_TYPE(string_ref, char);
+AR_TYPE_CONST(string_cref, char);
+
+typedef struct string_ref out_str;
+typedef struct string_cref cstr;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit_test/test.h b/source/kit_test/test.h
index da6c59b..09df7e9 100644
--- a/source/kit_test/test.h
+++ b/source/kit_test/test.h
@@ -10,7 +10,7 @@ extern "C" {
#include <string.h>
#ifndef KIT_TEST_FILE
-# define kit_test
+# define KIT_TEST_FILE kit_test
#endif
#ifndef KIT_TESTS_SIZE_LIMIT
diff --git a/source/test/unittests/CMakeLists.txt b/source/test/unittests/CMakeLists.txt
index d5cc573..ff99c9c 100644
--- a/source/test/unittests/CMakeLists.txt
+++ b/source/test/unittests/CMakeLists.txt
@@ -1,4 +1,5 @@
target_sources(
${KIT_TEST_SUITE}
PRIVATE
- async_function.test.c main.test.c)
+ async_function.test.c main.test.c array_ref.test.c
+ input_stream.test.c input_buffer.test.c dynamic_array.test.c)
diff --git a/source/test/unittests/array_ref.test.c b/source/test/unittests/array_ref.test.c
new file mode 100644
index 0000000..d8cbcec
--- /dev/null
+++ b/source/test/unittests/array_ref.test.c
@@ -0,0 +1,42 @@
+#include "../../kit/array_ref.h"
+
+#define KIT_TEST_FILE array_ref
+#include "../../kit_test/test.h"
+
+TEST("array ref equal") {
+ int foo[] = { 1, 2, 3, 4, 5, 6, 7 };
+ int bar[] = { 3, 4, 5 };
+
+ AR(foo_ref, int) = { .size = 3, .values = foo + 2 };
+ AR(bar_ref, int) = { .size = 3, .values = bar };
+
+ REQUIRE(AR_EQUAL(foo_ref, bar_ref));
+}
+
+static int compare(int const *left, int const *right) {
+ return *left - *right;
+}
+
+TEST("array ref compare") {
+ int foo[] = { 1, 2, 3, 5 };
+ int bar[] = { 1, 2, 4, 5 };
+
+ AR(foo_ref, int) = { .size = 3, .values = foo };
+ AR(bar_ref, int) = { .size = 3, .values = bar };
+
+ REQUIRE(AR_COMPARE(foo_ref, bar_ref, compare) < 0);
+ REQUIRE(AR_COMPARE(bar_ref, foo_ref, compare) > 0);
+ REQUIRE(AR_COMPARE(foo_ref, foo_ref, compare) == 0);
+}
+
+TEST("array ref different element sizes") {
+ int foo[] = { 1, 2, 3 };
+ char bar[] = { 1, 2, 3 };
+
+ AR(foo_ref, int) = { .size = 3, .values = foo };
+ AR(bar_ref, char) = { .size = 3, .values = bar };
+
+ REQUIRE(!AR_EQUAL(foo_ref, bar_ref));
+ REQUIRE(AR_COMPARE(foo_ref, bar_ref, compare) > 0);
+ REQUIRE(AR_COMPARE(bar_ref, foo_ref, compare) < 0);
+}
diff --git a/source/test/unittests/async_function.test.c b/source/test/unittests/async_function.test.c
index 1f50445..4fe2c60 100644
--- a/source/test/unittests/async_function.test.c
+++ b/source/test/unittests/async_function.test.c
@@ -1,6 +1,6 @@
#include "../../kit/async_function.h"
-#define KIT_TEST_FILE async_function_test
+#define KIT_TEST_FILE async_function
#include "../../kit_test/test.h"
CORO(int, test_foo) {
diff --git a/source/test/unittests/dynamic_array.test.c b/source/test/unittests/dynamic_array.test.c
new file mode 100644
index 0000000..fad8d6d
--- /dev/null
+++ b/source/test/unittests/dynamic_array.test.c
@@ -0,0 +1,174 @@
+#include "../../kit/dynamic_array.h"
+
+#define KIT_TEST_FILE dynamic_array
+#include "../../kit_test/test.h"
+
+TEST("dynamic array empty") {
+ DA_CREATE(v, char, 0);
+
+ REQUIRE(v.size == 0);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array resize") {
+ DA_CREATE(v, char, 0);
+ DA_RESIZE(v, 10);
+
+ REQUIRE(v.size == 10);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array grow") {
+ DA_CREATE(v, char, 2);
+ DA_RESIZE(v, 10);
+
+ REQUIRE(v.size == 10);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array diminish") {
+ DA_CREATE(v, char, 10);
+ DA_RESIZE(v, 9);
+
+ REQUIRE(v.size == 9);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array of chars") {
+ DA_CREATE(v, char, 100);
+
+ REQUIRE(v.size == 100);
+ REQUIRE(v.capacity >= 100);
+ REQUIRE(v.values != NULL);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array push") {
+ DA_CREATE(v, char, 0);
+ DA_APPEND(v, 'x');
+
+ REQUIRE(v.size == 1);
+ REQUIRE(v.values[0] == 'x');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array insert front") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_INSERT(v, 0, 'x');
+
+ REQUIRE(v.size == 4);
+ REQUIRE(v.values[0] == 'x');
+ REQUIRE(v.values[1] == 'a');
+ REQUIRE(v.values[2] == 'b');
+ REQUIRE(v.values[3] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array insert back") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_INSERT(v, 3, 'x');
+
+ REQUIRE(v.size == 4);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'b');
+ REQUIRE(v.values[2] == 'c');
+ REQUIRE(v.values[3] == 'x');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array insert middle") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_INSERT(v, 2, 'x');
+
+ REQUIRE(v.size == 4);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'b');
+ REQUIRE(v.values[2] == 'x');
+ REQUIRE(v.values[3] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array erase front") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_ERASE(v, 0);
+
+ REQUIRE(v.size == 2);
+ REQUIRE(v.values[0] == 'b');
+ REQUIRE(v.values[1] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array erase back") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_ERASE(v, 2);
+
+ REQUIRE(v.size == 2);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'b');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array erase middle") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_ERASE(v, 1);
+
+ REQUIRE(v.size == 2);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array of ints") {
+ DA_CREATE(v, int, 10);
+ DA_RESIZE(v, 5);
+ v.values[4] = 42;
+ DA_APPEND(v, 43);
+
+ REQUIRE(v.size == 6);
+ REQUIRE(v.values[4] == 42);
+ REQUIRE(v.values[5] == 43);
+
+ DA_DESTROY(v);
+}
diff --git a/source/test/unittests/input_buffer.test.c b/source/test/unittests/input_buffer.test.c
new file mode 100644
index 0000000..c7edb8f
--- /dev/null
+++ b/source/test/unittests/input_buffer.test.c
@@ -0,0 +1,20 @@
+#include "../../kit/input_buffer.h"
+
+#define KIT_TEST_FILE input_buffer
+#include "../../kit_test/test.h"
+
+TEST("input buffer") {
+ cstr text = { .size = 3, .values = "foo" };
+ struct is_handle in = IS_WRAP_STRING(text);
+ struct ib_handle first = IB_WRAP(in);
+
+ struct ib_handle second = ib_read(first, 3);
+
+ REQUIRE(!second.error);
+ REQUIRE(second.data.size == 3);
+ REQUIRE(AR_EQUAL(text, second.data));
+
+ ib_destroy(second);
+ ib_destroy(first);
+ is_destroy(in);
+}
diff --git a/source/test/unittests/input_stream.test.c b/source/test/unittests/input_stream.test.c
new file mode 100644
index 0000000..9fef47a
--- /dev/null
+++ b/source/test/unittests/input_stream.test.c
@@ -0,0 +1,23 @@
+#include "../../kit/input_stream.h"
+
+#define KIT_TEST_FILE input_stream
+#include "../../kit_test/test.h"
+
+TEST("input stream wrap string") {
+ char foo[] = "test";
+ char bar[] = "test";
+
+ cstr foo_ref = { .size = sizeof(foo) - 1, .values = foo };
+ cstr bar_ref = { .size = sizeof(bar) - 1, .values = bar };
+
+ struct is_handle in = IS_WRAP_STRING(foo_ref);
+
+ char buf[4];
+ out_str buf_ref = { .size = sizeof(buf), .values = buf };
+
+ REQUIRE(IS_READ(in, buf_ref) == buf_ref.size);
+ REQUIRE(AR_EQUAL(foo_ref, bar_ref));
+ REQUIRE(AR_EQUAL(buf_ref, bar_ref));
+
+ is_destroy(in);
+}