summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2023-10-07 17:12:38 -0400
committerAndrew Opalach <andrew@akon.city> 2023-10-07 17:44:10 -0400
commitb07771669ae029d07b76a58ed526da7bc29ec27b (patch)
treea379a6b638a64060de1c220537f803b6402ffdea /include
downloadlibalabaster-b07771669ae029d07b76a58ed526da7bc29ec27b.tar.gz
libalabaster-b07771669ae029d07b76a58ed526da7bc29ec27b.tar.bz2
libalabaster-b07771669ae029d07b76a58ed526da7bc29ec27b.zip
Add libalabaster
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'include')
-rw-r--r--include/al/array.h11
-rw-r--r--include/al/array_sort.h19
-rw-r--r--include/al/array_typed.h151
-rw-r--r--include/al/atomic.h42
-rw-r--r--include/al/lib.h157
-rw-r--r--include/al/list.h132
-rw-r--r--include/al/log.h31
-rw-r--r--include/al/random.h28
-rw-r--r--include/al/ring_buffer.h31
-rw-r--r--include/al/str.h161
-rw-r--r--include/al/test.h142
-rw-r--r--include/al/types.h61
-rw-r--r--include/al/wstr.h81
13 files changed, 1047 insertions, 0 deletions
diff --git a/include/al/array.h b/include/al/array.h
new file mode 100644
index 0000000..07fd794
--- /dev/null
+++ b/include/al/array.h
@@ -0,0 +1,11 @@
+#ifndef _AL_ARRAY_H
+#define _AL_ARRAY_H
+
+#include "lib.h"
+
+#define AL_ARRAY_MALLOC al_malloc
+#define AL_ARRAY_REALLOC al_realloc
+#define AL_ARRAY_FREE al_free
+#include "../../src/array.c"
+
+#endif //_AL_ARRAY_H
diff --git a/include/al/array_sort.h b/include/al/array_sort.h
new file mode 100644
index 0000000..51011f4
--- /dev/null
+++ b/include/al/array_sort.h
@@ -0,0 +1,19 @@
+#ifndef _AL_ARRAY_SORT_H
+#define _AL_ARRAY_SORT_H
+
+#define AL_INSERSION_SORT(data, type, count, cmp) \
+ do { \
+ s32 i = 1; \
+ while (i < (s32)count) { \
+ s32 j = i - 1; \
+ type x = data[i]; \
+ while (j >= 0 && cmp(&data[j], &x) > 0) { \
+ data[j + 1] = data[j]; \
+ j--; \
+ } \
+ data[j + 1] = x; \
+ i++; \
+ } \
+ } while (0);
+
+#endif // _AL_ARRAY_SORT_H
diff --git a/include/al/array_typed.h b/include/al/array_typed.h
new file mode 100644
index 0000000..678aef4
--- /dev/null
+++ b/include/al/array_typed.h
@@ -0,0 +1,151 @@
+#ifndef _AL_ARRAY_TYPED_H
+#define _AL_ARRAY_TYPED_H
+
+#ifndef HAVE_GNU_EXTENSIONS
+#error "missing required GNU C extensions"
+#endif
+
+#define _array(N, ...) AL_PASTER(array, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_init(N, ...) AL_PASTER(_array_init, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_data(N, ...) AL_PASTER(_array_data, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_reserve(N, ...) AL_PASTER(_array_reserve, AL_PASTER(N, __VA_ARGS__))
+#define __al_array_reserve_internal(N, ...) AL_PASTER(_array_reserve_internal, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_at(N, ...) AL_PASTER(_array_at, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_last(N, ...) AL_PASTER(_array_last, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_push(N, ...) AL_PASTER(_array_push, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_pop(N, ...) AL_PASTER(_array_pop, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_remove_at(N, ...) AL_PASTER(_array_remove_at, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_pop_at(N, ...) AL_PASTER(_array_pop_at, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_insert(N, ...) AL_PASTER(_array_insert, AL_PASTER(N, __VA_ARGS__))
+#define _al_array_free(N, ...) AL_PASTER(_array_free, AL_PASTER(N, __VA_ARGS__))
+
+#define array(...) AL_EMPTY_DEFAULT(_array, __VA_ARGS__)
+#define al_array_init(...) AL_EMPTY_DEFAULT(_al_array_init, __VA_ARGS__)
+#define al_array_data(...) AL_EMPTY_DEFAULT(_al_array_data, __VA_ARGS__)
+#define _al_array_reserve_internal(...) AL_EMPTY_DEFAULT(__al_array_reserve_internal, __VA_ARGS__)
+#define al_array_reserve(...) AL_EMPTY_DEFAULT(_al_array_reserve, __VA_ARGS__)
+#define al_array_at(...) AL_EMPTY_DEFAULT(_al_array_at, __VA_ARGS__)
+#define al_array_last(...) AL_EMPTY_DEFAULT(_al_array_last, __VA_ARGS__)
+#define al_array_push(...) AL_EMPTY_DEFAULT(_al_array_push, __VA_ARGS__)
+#define al_array_pop(...) AL_EMPTY_DEFAULT(_al_array_pop, __VA_ARGS__)
+#define al_array_remove_at(...) AL_EMPTY_DEFAULT(_al_array_remove_at, __VA_ARGS__)
+#define al_array_pop_at(...) AL_EMPTY_DEFAULT(_al_array_pop_at, __VA_ARGS__)
+#define al_array_insert(...) AL_EMPTY_DEFAULT(_al_array_insert, __VA_ARGS__)
+#define al_array_free(...) AL_EMPTY_DEFAULT(_al_array_free, __VA_ARGS__)
+
+#define AL_ARRAY_DEFINE_FUNCTIONS(N, T, ...) \
+ static inline T *al_array_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 i) \
+ { \
+ return &al_array_data(N, __VA_ARGS__)(a)[i]; \
+ } \
+ static inline T *al_array_last(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a) \
+ { \
+ return al_array_at(N, __VA_ARGS__)(a, a->size - 1); \
+ } \
+ static void al_array_reserve(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 size); \
+ static inline void al_array_push(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, T *value) \
+ { \
+ al_array_reserve(N, __VA_ARGS__)(a, a->size + 1); \
+ *al_array_at(N, __VA_ARGS__)(a, a->size++) = *value; \
+ } \
+ static inline void al_array_pop(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, T *ret) \
+ { \
+ *ret = *al_array_at(N, __VA_ARGS__)(a, --a->size); \
+ } \
+ static inline void al_array_remove_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 i) \
+ { \
+ if (UNLIKELY(i == a->size - 1)) { \
+ a->size--; \
+ } else { \
+ al_memmove(al_array_at(N, __VA_ARGS__)(a, i), al_array_at(N, __VA_ARGS__)(a, i + 1), \
+ sizeof(T) * (--a->size - i)); \
+ } \
+ } \
+ static inline void al_array_pop_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 i, T *ret) \
+ { \
+ *ret = *al_array_at(N, __VA_ARGS__)(a, i); \
+ al_array_remove_at(N, __VA_ARGS__)(a, i); \
+ } \
+ static inline void al_array_insert(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, T *item, u32 i) \
+ { \
+ if (a->size > i) { \
+ al_array_reserve(N, __VA_ARGS__)(a, a->size + 1); \
+ al_memmove(al_array_at(N, __VA_ARGS__)(a, i + 1), al_array_at(N, __VA_ARGS__)(a, i), \
+ sizeof(T) * (a->size - i)); \
+ a->size++; \
+ } else { \
+ al_array_reserve(N, __VA_ARGS__)(a, i + 1); \
+ a->size = i + 1; \
+ } \
+ *al_array_at(N, __VA_ARGS__)(a, i) = *item; \
+ } \
+ static inline void _al_array_reserve_internal(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 size) \
+ { \
+ if (size < al_grow_limit) { \
+ size = al_next_power_of_two(size + 1); \
+ } else { \
+ size = (size + al_grow_limit) & ~al_grow_limit; \
+ } \
+ if (a->alloc >= size) return; \
+ a->data = (!a->data) ? al_malloc(sizeof(T) * size) : al_realloc(a->data, sizeof(T) * size); \
+ a->alloc = size; \
+ } \
+ static inline void al_array_free(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a) \
+ { \
+ if (a->data != NULL) al_free(a->data); \
+ }
+
+#define AL_ARRAY_DEFINE(N, T) \
+ typedef struct array(N) array(N); \
+ struct array(N) { \
+ u32 size; \
+ u32 alloc; \
+ T *data; \
+ }; \
+ static inline void al_array_init(N)(array(N) *a) \
+ { \
+ a->size = 0; \
+ a->alloc = 0; \
+ a->data = NULL; \
+ } \
+ static inline T *al_array_data(N)(array(N) *a) \
+ { \
+ return a->data; \
+ } \
+ AL_ARRAY_DEFINE_FUNCTIONS(N, T,) \
+ static inline void al_array_reserve(N)(array(N) *a, u32 size) \
+ { \
+ _al_array_reserve_internal(N)(a, size); \
+ }
+
+#define AL_ARRAY_INLINE_STORAGE_DEFINE(N, T, S) \
+ typedef struct array(N, S) array(N, S); \
+ struct array(N, S) { \
+ u32 size; \
+ u32 alloc; \
+ T *data; \
+ T inline_data[S]; \
+ }; \
+ static inline void al_array_init(N, S)(array(N, S) *a) \
+ { \
+ a->size = 0; \
+ a->alloc = S; \
+ a->data = NULL; \
+ } \
+ static inline T *al_array_data(N, S)(array(N, S) *a) \
+ { \
+ if (a->data) return a->data; \
+ else return a->inline_data; \
+ } \
+ AL_ARRAY_DEFINE_FUNCTIONS(N, T, S) \
+ static inline void al_array_reserve(N, S)(array(N, S) *a, u32 size) \
+ { \
+ if (S >= size) return; \
+ bool copy_inline = a->data == NULL; \
+ _al_array_reserve_internal(N, S)(a, size); \
+ if (UNLIKELY(copy_inline)) { \
+ al_memcpy(a->data, a->inline_data, sizeof(T) * a->size); \
+ } \
+ }
+
+#endif // _AL_ARRAY_TYPED_H
diff --git a/include/al/atomic.h b/include/al/atomic.h
new file mode 100644
index 0000000..5a1b495
--- /dev/null
+++ b/include/al/atomic.h
@@ -0,0 +1,42 @@
+#ifndef _AL_ATOMIC_H
+#define _AL_ATOMIC_H
+
+#include <c89atomic.h>
+
+#include "types.h"
+
+#define AL_ATOMIC_RELAXED c89atomic_memory_order_relaxed
+#define AL_ATOMIC_CONSUME c89atomic_memory_order_consume
+#define AL_ATOMIC_ACQUIRE c89atomic_memory_order_acquire
+#define AL_ATOMIC_RELEASE c89atomic_memory_order_release
+#define AL_ATOMIC_ACQ_REL c89atomic_memory_order_acq_rel
+#define AL_ATOMIC_SEQ_CST c89atomic_memory_order_seq_cst
+
+#define al_atomic_ptr_load(p, order) c89atomic_load_explicit_ptr(((volatile void **)p), order)
+#define al_atomic_ptr_store(p, v, order) c89atomic_store_explicit_ptr(((volatile void **)p), (void *)v, order)
+
+#define atomic_size_t volatile c89atomic_uint64
+#define al_atomic_size_t_load(p, order) ((size_t)c89atomic_load_explicit_64(p, order))
+#define al_atomic_size_t_store(p, v, order) c89atomic_store_explicit_64(p, ((size_t)v), order)
+
+#define atomic_s64 volatile c89atomic_int64
+#define al_atomic_s64_load(p, order) ((s64)c89atomic_load_explicit_i64(p, order))
+#define al_atomic_s64_store(p, v, order) c89atomic_store_explicit_i64(p, ((s64)v), order)
+
+#define atomic_f64 volatile f64
+#define al_atomic_f64_load(p, order) ((f64)c89atomic_load_explicit_f64(p, order))
+#define al_atomic_f64_store(p, v, order) c89atomic_store_explicit_f64(p, ((f64)v), order)
+
+#define atomic_s32 volatile c89atomic_int32
+#define al_atomic_s32_load(p, order) ((s32)c89atomic_load_explicit_i32(p, order))
+#define al_atomic_s32_store(p, v, order) c89atomic_store_explicit_i32(p, ((s32)v), order)
+
+#define atomic_u8 volatile c89atomic_uint8
+#define al_atomic_u8_load(p, order) c89atomic_load_explicit_8(p, order)
+#define al_atomic_u8_store(p, v, order) c89atomic_store_explicit_8(p, ((u8)v), order)
+
+#define atomic_bool volatile c89atomic_uint8
+#define al_atomic_bool_load(p, order) c89atomic_load_explicit_8(p, order)
+#define al_atomic_bool_store(p, v, order) c89atomic_store_explicit_8(p, ((bool)v), order)
+
+#endif // _AL_ATOMIC_H
diff --git a/include/al/lib.h b/include/al/lib.h
new file mode 100644
index 0000000..360ba38
--- /dev/null
+++ b/include/al/lib.h
@@ -0,0 +1,157 @@
+#ifndef _AL_LIB_H
+#define _AL_LIB_H
+
+#include "types.h"
+
+#if defined(__clang__) || defined(__GNUC__)
+#define AL_UNUSED_FUNCTION_PUSH \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
+#define AL_UNUSED_FUNCTION_POP \
+ _Pragma("GCC diagnostic pop")
+#else
+#define AL_UNUSED_FUNCTION_PUSH
+#define AL_UNUSED_FUNCTION_POP
+#endif
+
+#define AL_USE_STDLIB 1
+
+#if AL_USE_STDLIB
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+
+void al_malloc_init(void);
+void *al_malloc(size_t n);
+void *al_calloc(size_t n, size_t size);
+void *al_realloc(void *ptr, size_t n);
+void al_free(void *ptr);
+#ifdef HAVE_POSIX_MEMALIGN
+int al_posix_memalign(void **ptr, size_t alignment, size_t n);
+#endif
+void al_malloc_stats(u32 *current, u32 *max, u32 *total);
+void al_set_alloc(void *(*malloc_func)(size_t), void *(*calloc_func)(size_t, size_t),
+ void *(*realloc_func)(void *, size_t), void (*free_func)(void *));
+
+#define al_memcpy memcpy
+#define al_memmove memmove
+#define al_memchr memchr
+#define al_memcmp memcmp
+#define al_memset memset
+#define al_bzero bzero
+#define al_strlen strlen
+#ifdef _DEBUG_
+#define al_assert assert
+#else
+#define al_assert(expr) do { (void)sizeof(expr); } while (0)
+#endif
+
+#define al_alloc_object(type) (type *)al_calloc(1, sizeof(type))
+
+AL_UNUSED_FUNCTION_PUSH
+
+#define AL_FORCE_DISABLE_OUTPUT 0
+
+static inline s32 al_printf(const char *fmt, ...)
+{
+#if AL_FORCE_DISABLE_OUTPUT
+ return 0;
+#else
+ va_list args;
+ va_start(args, fmt);
+ s32 ret = vprintf(fmt, args);
+ va_end(args);
+ return ret;
+#endif
+}
+
+static inline void *al_memrchr(const void *s, s32 c, size_t n)
+{
+ const u8 *src = (const u8 *)s + n - 1;
+ u8 d = (u8)c;
+ while (n--) {
+ if (*src == d) return (void *)src;
+ src--;
+ }
+ return NULL;
+}
+
+AL_UNUSED_FUNCTION_POP
+#endif
+
+#ifndef LIKELY
+#ifdef HAVE_BUILTIN_EXPECT
+#define LIKELY(x) __builtin_expect((x), 1)
+#else
+#define LIKELY(x) x
+#endif
+#endif
+
+#ifndef UNLIKELY
+#ifdef HAVE_BUILTIN_EXPECT
+#define UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define UNLIKELY(x) x
+#endif
+#endif
+
+#if __GNUC__ >= 3
+#define AL_MAX(a, b) \
+({ \
+ __typeof__(a) _a = a; \
+ __typeof__(b) _b = b; \
+ _a > _b ? _a : _b; \
+})
+#define AL_MIN(a, b) \
+({ \
+ __typeof__(a) _a = a; \
+ __typeof__(b) _b = b; \
+ _a < _b ? _a : _b; \
+})
+#else
+#define AL_MAX(x, y) (((x) > (y)) ? (x) : (y))
+#define AL_MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#define AL_CLAMP(n, lo, hi) AL_MIN(hi, AL_MAX(lo, n))
+
+#define AL_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+#ifndef _MSVC_
+#define AL_EMPTY_DEFAULT(...) AL_EMPTY_DEFAULT_0(__VA_ARGS__, , )
+#define AL_EMPTY_DEFAULT_0(FUNC, T, S, ...) FUNC(T, S)
+#define AL_PASTER_EVALUATOR(X, Y) X##_##Y
+#define AL_PASTER(X, Y) AL_PASTER_EVALUATOR(X, Y)
+#endif
+
+AL_UNUSED_FUNCTION_PUSH
+
+static inline size_t al_next_power_of_two(size_t n)
+{
+ n--;
+ n |= n >> 1;
+ n |= n >> 2;
+ n |= n >> 4;
+ n |= n >> 8;
+ return ++n;
+}
+
+static inline bool al_isspace(char c)
+{
+ return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v');
+}
+
+static inline u32 al_u32_inc_wrap(u32 v)
+{
+ return (v == UINT32_MAX) ? 0 : v + 1;
+}
+
+AL_UNUSED_FUNCTION_PUSH
+
+extern size_t al_page_size;
+extern size_t al_grow_limit;
+void al_set_page_size(size_t size);
+
+#endif // _AL_LIB_H
diff --git a/include/al/list.h b/include/al/list.h
new file mode 100644
index 0000000..6e1ba29
--- /dev/null
+++ b/include/al/list.h
@@ -0,0 +1,132 @@
+#ifndef _AL_LIST_H
+#define _AL_LIST_H
+
+#include "lib.h"
+
+#define AL_LIST_DEFINE(type, name) \
+ AL_UNUSED_FUNCTION_PUSH \
+ typedef struct list_##name list_##name; \
+ struct list_##name { \
+ type value; \
+ list_##name *next; \
+ list_##name *prev; \
+ }; \
+ static inline list_##name *list_new_node_##name(type value) { \
+ list_##name *node = (list_##name *)al_malloc(sizeof(list_##name)); \
+ node->value = value; \
+ return node; \
+ } \
+ static inline void list_free_node_##name(list_##name *l) { \
+ free(l); \
+ } \
+ static inline list_##name *list_last_##name(list_##name *l) { \
+ if (l) { \
+ while (l->next) l = l->next; \
+ } \
+ return l; \
+ } \
+ static inline list_##name *list_nth_##name(list_##name *l, u32 n) { \
+ while (n-- > 0 && l) l = l->next; \
+ return l; \
+ } \
+ static inline list_##name *list_append_##name(list_##name *l, type value) { \
+ list_##name *node = list_new_node_##name(value); \
+ node->next = NULL; \
+ if (l) { \
+ list_##name *last = list_last_##name(l); \
+ node->prev = last; \
+ last->next = node; \
+ return l; \
+ } else { \
+ node->prev = NULL; \
+ return node; \
+ } \
+ } \
+ static inline list_##name *list_prepend_##name(list_##name *l, type value) { \
+ list_##name *node = list_new_node_##name(value); \
+ node->next = l; \
+ node->prev = NULL; \
+ if (l) l->prev = node; \
+ return node; \
+ } \
+ static inline list_##name *list_insert_before_##name(list_##name *l, list_##name *s, type value) { \
+ if (!l) { \
+ list_##name *node = list_new_node_##name(value); \
+ node->next = NULL; \
+ node->prev = NULL; \
+ return node; \
+ } else if (!s) { \
+ return list_append_##name(l, value); \
+ } else if (s == l) { \
+ return list_prepend_##name(l, value); \
+ } else { \
+ list_##name *node = list_new_node_##name(value); \
+ s->prev->next = node; \
+ node->prev = s->prev; \
+ node->next = s; \
+ s->prev = node; \
+ return l; \
+ } \
+ } \
+ static inline list_##name *list_insert_after_##name(list_##name *l, list_##name *s, type value) { \
+ if (!l) { \
+ list_##name *node = list_new_node_##name(value); \
+ node->next = NULL; \
+ node->prev = NULL; \
+ return node; \
+ } else if (!s || !s->next) { \
+ return list_append_##name(l, value); \
+ } else { \
+ list_##name *node = list_new_node_##name(value); \
+ s->next->prev = node; \
+ node->next = s->next; \
+ node->prev = s; \
+ s->next = node; \
+ return l; \
+ } \
+ } \
+ static inline list_##name *list_remove_##name(list_##name *l, list_##name *n) { \
+ if (!l) return l; \
+ else if (l == n) { \
+ if (n->next) { \
+ n->next->prev = NULL; \
+ } \
+ l = n->next; \
+ list_free_node_##name(n); \
+ } else { \
+ n->prev->next = n->next; \
+ if (n->next) { \
+ n->next->prev = n->prev; \
+ } \
+ list_free_node_##name(n); \
+ } \
+ return l; \
+ } \
+ AL_UNUSED_FUNCTION_POP \
+
+#define list(name) list_##name
+
+// Get element at nth position.
+#define al_list_nth(name, list, n) list_nth_##name(list, n)
+// Get the last element in the list.
+#define al_list_last(name, list) list_last_##name(list)
+
+// Append value to the end of the list.
+// If list is NULL, create new list.
+#define al_list_append(name, list, value) list_append_##name(list, value)
+// Prepend value to the front of the list.
+// If list is NULL, create new list.
+#define al_list_prepend(name, list, value) list_prepend_##name(list, value)
+
+// Insert value before/after sibling.
+// If list is NULL, create and return new list.
+// If sibiling is NULL (or sibiling->next in after case) append the value to the end of the list.
+#define al_list_insert_before(name, list, sibling, value) list_insert_before_##name(list, sibling, value)
+#define al_list_insert_after(name, list, sibling, value) list_insert_after_##name(list, sibling, value)
+
+#define al_list_remove(name, list, node) list_remove_##name(list, node)
+
+#define al_list_foreach(name, list, node) \
+ for (list_##name *node = list; node; node = node->next)
+
+#endif // _AL_LIST_H
diff --git a/include/al/log.h b/include/al/log.h
new file mode 100644
index 0000000..8723635
--- /dev/null
+++ b/include/al/log.h
@@ -0,0 +1,31 @@
+#ifndef _AL_LOG_H
+#define _AL_LOG_H
+
+#include <al/types.h>
+#include <al/lib.h>
+
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+
+void al_set_print(void *userdata, s32 (*print_func)(void *, char *));
+
+s32 _al_log(const char *level, const char *section, const char *name, const s32 line,
+ const char *func, const char *fmt, ...);
+s32 _al_logv(const char *level, const char *section, const char *name, const s32 line,
+ const char *func, const char *fmt, va_list args);
+s32 _al_log_nop(const char *section, const char *fmt, ...);
+
+#define al_log(level, section, fmt, ...) \
+ _al_log(#level, section, __FILENAME__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
+#define al_logv(level, section, fmt, args) \
+ _al_logv(#level, section, __FILENAME__, __LINE__, __FUNCTION__, fmt, args)
+
+#define al_log_info(section, fmt, ...) al_log(info, section, fmt, ##__VA_ARGS__)
+#define al_log_warn(section, fmt, ...) al_log(warn, section, fmt, ##__VA_ARGS__)
+#define al_log_error(section, fmt, ...) al_log(error, section, fmt, ##__VA_ARGS__)
+#ifdef _DEBUG_
+#define al_log_debug(section, fmt, ...) al_log(debug, section, fmt, ##__VA_ARGS__)
+#else
+#define al_log_debug(section, fmt, ...) _al_log_nop(section, fmt, ##__VA_ARGS__)
+#endif
+
+#endif //_AL_LOG_H
diff --git a/include/al/random.h b/include/al/random.h
new file mode 100644
index 0000000..f5210b6
--- /dev/null
+++ b/include/al/random.h
@@ -0,0 +1,28 @@
+#ifndef _AL_RANDOM_H
+#define _AL_RANDOM_H
+
+#include "lib.h"
+
+#if AL_USE_STDLIB
+#include <time.h>
+#endif
+
+static u16 _al_rand_value = 1;
+
+AL_UNUSED_FUNCTION_PUSH
+
+static void al_rand_init(void)
+{
+ srand((u32)time(NULL));
+}
+
+// TEMP: "Random" that guarantees we wont get collisions for the current use-case.
+static u16 al_rand_u16(void)
+{
+ if (_al_rand_value == UINT16_MAX) _al_rand_value = 1;
+ return _al_rand_value++;
+}
+
+AL_UNUSED_FUNCTION_POP
+
+#endif // _AL_RANDOM_H
diff --git a/include/al/ring_buffer.h b/include/al/ring_buffer.h
new file mode 100644
index 0000000..359d087
--- /dev/null
+++ b/include/al/ring_buffer.h
@@ -0,0 +1,31 @@
+#ifndef _AL_RING_BUFFER_H
+#define _AL_RING_BUFFER_H
+
+#include "types.h"
+#include "atomic.h"
+
+struct al_ring_buffer {
+ u8 *start;
+ u8 *end;
+ volatile u8 *read;
+ volatile u8 *write;
+};
+
+void al_ring_buffer_init(struct al_ring_buffer *buf, u8 *data, size_t length);
+
+size_t al_ring_buffer_space(struct al_ring_buffer *buf);
+u8 *al_ring_buffer_write_chunk(struct al_ring_buffer *buf, size_t *size);
+void al_ring_buffer_append(struct al_ring_buffer *buf, u8 *ptr, size_t n);
+
+size_t al_ring_buffer_occupied(struct al_ring_buffer *buf);
+u8 *al_ring_buffer_read_chunk(struct al_ring_buffer *buf, size_t *size);
+void al_ring_buffer_consume(struct al_ring_buffer *buf, u8 *ptr, size_t n);
+
+size_t al_ring_buffer_write(struct al_ring_buffer *buf, u8 *data, size_t n);
+size_t al_ring_buffer_read(struct al_ring_buffer *buf, u8 *ptr, size_t n);
+size_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, size_t offset, size_t n);
+size_t al_ring_buffer_discard(struct al_ring_buffer *buf, size_t n);
+
+void al_ring_buffer_reset(struct al_ring_buffer *buf);
+
+#endif // _AL_RING_BUFFER_H
diff --git a/include/al/str.h b/include/al/str.h
new file mode 100644
index 0000000..a43d4ba
--- /dev/null
+++ b/include/al/str.h
@@ -0,0 +1,161 @@
+#ifndef _AL_STR_H
+#define _AL_STR_H
+
+#include "lib.h"
+
+typedef struct {
+ char *data;
+ u32 len;
+ u32 alloc;
+} str;
+
+#define AL_STR_EMPTY (str){ NULL, 0, 0 }
+
+#define al_str_at(s, i) (s)->data[i]
+#define al_str_atr(s, i) (s)->data[(s)->len + i]
+
+#ifndef __cplusplus
+#define al_str_c(s) (&((str){ ((char *)s), (u32)al_strlen(s), 0 }))
+#define al_str_w(s, i, n) (&((str){ &(s)[i], ((u32)n), 0 }))
+#define al_str_substr(s, start, end) al_str_w((s)->data, start, ((end) - (start)))
+#else
+#define al_str_cc(s) (((str){ ((char *)s), (u32)al_strlen(s), 0 }))
+#define al_str_ww(s, i, n) (((str){ &(s)[i], ((u32)n), 0 }))
+#define al_str_substr(s, start, end) al_str_ww((s)->data, start, ((end) - (start)))
+#endif
+
+#define AL_STR_FMT "%.*s"
+#define AL_STR_PRINTF(s) (s)->len, (s)->data
+#define AL_STR_PRINTF_MAXLEN(s, l) AL_MIN((s)->len, (l)), (s)->data
+
+AL_UNUSED_FUNCTION_PUSH
+
+static inline void al_str_free(str *s)
+{
+ if (s->alloc > 0) al_free(s->data);
+}
+
+static inline u32 al_str_reserve(str *s, u32 size)
+{
+ if (size < al_grow_limit) {
+ size = (u32)al_next_power_of_two(size + 1);
+ } else {
+ size = (u32)((size + al_grow_limit) & ~al_grow_limit);
+ }
+
+ if (size <= s->alloc) return s->alloc;
+
+ s->data = (char *)((!s->data) ? al_malloc(size) : al_realloc(s->data, size));
+
+ return size;
+}
+
+static inline void al_str_sized(str *s, u32 size)
+{
+ *s = AL_STR_EMPTY;
+ s->alloc = al_str_reserve(s, size);
+}
+
+static inline void al_str_clone(str *dest, str *src)
+{
+ al_str_sized(dest, src->len);
+ al_memcpy(dest->data, src->data, src->len);
+ dest->len = src->len;
+}
+
+static inline void al_str_from(str *s, const char *c_str)
+{
+ if (!c_str) {
+ *s = AL_STR_EMPTY;
+ return;
+ }
+ size_t len = al_strlen(c_str);
+ al_str_sized(s, (u32)len);
+ al_memcpy(s->data, c_str, len);
+ s->len = (u32)len;
+}
+
+// Returned c_str must be free'd by the user.
+static inline char *al_str_to_c_str(str *s)
+{
+ char *c = (char *)al_malloc(s->len + 1);
+ al_memcpy(c, s->data, s->len);
+ c[s->len] = '\0';
+ return c;
+}
+
+static inline void al_str_cat(str *s, str *c)
+{
+ s->alloc = al_str_reserve(s, s->len + c->len);
+ al_memcpy(s->data + s->len, c->data, c->len);
+ s->len += c->len;
+}
+
+static inline void al_str_insert(str *s, str *c, u32 i)
+{
+ s->alloc = al_str_reserve(s, s->len + c->len);
+ char *_i = s->data + i;
+ al_memmove(_i + c->len, _i, s->len - i);
+ al_memcpy(_i, c->data, c->len);
+ s->len += c->len;
+}
+
+static inline void al_str_prepend(str *s, str *c)
+{
+ al_str_insert(s, c, 0);
+}
+
+static inline s32 al_str_cmp(str *s, str *c, u32 start, u32 len)
+{
+ if (start + len > s->len || len > c->len) {
+ return -1;
+ }
+ return al_memcmp(s->data + start, c->data, len);
+}
+
+static inline bool al_str_eq(str *s, str *c)
+{
+ return al_str_cmp(s, c, 0, s->len > c->len ? s->len : c->len) == 0;
+}
+
+static inline s32 al_str_find(str *s, char c)
+{
+ char *loc = (char *)al_memchr(s->data, c, s->len);
+ if (!loc) return -1;
+ return (s32)(loc - s->data);
+}
+
+static inline s32 al_str_rfind(str *s, char c)
+{
+ char *loc = (char *)al_memrchr(s->data, c, s->len);
+ if (!loc) return -1;
+ return (s32)(loc - s->data);
+}
+
+// extremely slow.
+static inline s32 al_str_find_str(str *s, str *c)
+{
+ if (c->len > s->len) return -1;
+ for (u32 i = 0; i < s->len - c->len; i++) {
+ if (s->data[i] != c->data[0]) continue;
+ if (al_str_cmp(s, c, i, c->len) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static inline u32 al_str_hash(str *s)
+{
+ u32 hash = 5381;
+ for (u32 i = 0; i < s->len; i++) {
+ hash = ((hash << 5) + hash) + s->data[i]; /* hash * 33 + c */
+ }
+ return hash;
+}
+
+s64 al_str_to_long(str *s, s32 base);
+bool al_str_tok(str *s, char sep, str *tok);
+bool al_str_get_line(str *buffer, str *line);
+
+#endif // _AL_STR_H
diff --git a/include/al/test.h b/include/al/test.h
new file mode 100644
index 0000000..41395ce
--- /dev/null
+++ b/include/al/test.h
@@ -0,0 +1,142 @@
+#ifndef _AL_TEST_H
+#define _AL_TEST_H
+
+#include "types.h"
+#include "lib.h"
+
+#define AL_TEST_START_GROUP(name) \
+ al_printf("Group \""name"\":\n")
+
+#define AL_TEST_END_GROUP() \
+ return true
+
+#define AL_TEST_RUN(test) \
+ if (!test()) return false;
+
+#define AL_TEST_START(name) \
+ al_printf(" "name"...")
+
+#define AL_TEST_END() \
+ al_printf("OK\n"); \
+ return true
+
+#define AL_TEST_DEF(a, b, T, cmp, explain) \
+ do { \
+ AL_TEST_TYPE_##T _a = a; \
+ AL_TEST_TYPE_##T _b = b; \
+ if (cmp) { \
+ al_printf("FAIL "#a"("AL_TEST_FMT_##T") "explain" "#b"("AL_TEST_FMT_##T")\n", \
+ AL_TEST_PRINTF_##T(_a), AL_TEST_PRINTF_##T(_b)); \
+ return false; \
+ } \
+ } while (0)
+
+#define AL_TEST_EQ(a, b, T) \
+ AL_TEST_DEF(a, b, T, (AL_TEST_CMP_##T(_a, _b) != 0), \
+ "is not equal to")
+
+#define AL_TEST_NEQ(a, b, T) \
+ AL_TEST_DEF(a, b, T, (AL_TEST_CMP_##T(_a, _b) == 0), \
+ "is equal to")
+
+#define AL_TEST_GT(a, b, T) \
+ AL_TEST_DEF(a, b, T, (AL_TEST_CMP_##T(_a, _b) != 1), \
+ "is less than or equal to")
+
+#define AL_TEST_GTE(a, b, T) \
+ AL_TEST_DEF(a, b, T, (AL_TEST_CMP_##T(_a, _b) != 0 && AL_TEST_CMP_##T(_a, _b) != 1), \
+ "is less than")
+
+#define AL_TEST_LT(a, b, T) \
+ AL_TEST_DEF(a, b, T, (AL_TEST_CMP_##T(_a, _b) != -1), \
+ "is greater than or equal to")
+
+#define AL_TEST_LTE(a, b, T) \
+ AL_TEST_DEF(a, b, T, (AL_TEST_CMP_##T(_a, _b) != 0 && AL_TEST_CMP_##T(_a, _b) != -1), \
+ "is greater than")
+
+#define AL_TEST_TRUE(a) \
+ AL_TEST_DEF(a, true, bool, (AL_TEST_CMP_bool(_a, _b) != 0), \
+ "is not")
+
+#define AL_TEST_FALSE(a) \
+ AL_TEST_DEF(a, false, bool, (AL_TEST_CMP_bool(_a, _b) != 0), \
+ "is not")
+
+static inline s32 al_test_bool_cmp(bool a, bool b)
+{
+ return (a == b) ? 0 : 1;
+}
+
+#define AL_TEST_CMP_bool al_test_bool_cmp
+#define AL_TEST_TYPE_bool bool
+
+#define AL_TEST_FMT_bool "%s"
+#define AL_TEST_PRINTF_bool(b) (b ? "true" : "false")
+
+static inline s32 al_test_u32_cmp(u32 a, u32 b)
+{
+ if (a == b) return 0;
+ else if (a < b) return -1;
+ else return 1;
+}
+
+#define AL_TEST_CMP_u32 al_test_u32_cmp
+#define AL_TEST_TYPE_u32 u32
+
+#define AL_TEST_FMT_u32 "%d"
+#define AL_TEST_PRINTF_u32(i) i
+
+static inline s32 al_test_s32_cmp(s32 a, s32 b)
+{
+ if (a == b) return 0;
+ else if (a < b) return -1;
+ else return 1;
+}
+
+#define AL_TEST_CMP_s32 al_test_s32_cmp
+#define AL_TEST_TYPE_s32 s32
+
+#define AL_TEST_FMT_s32 "%i"
+#define AL_TEST_PRINTF_s32(i) i
+
+static inline s32 al_test_size_t_cmp(size_t a, size_t b)
+{
+ if (a == b) return 0;
+ else if (a < b) return -1;
+ else return 1;
+}
+
+#define AL_TEST_CMP_s64 al_test_s64_cmp
+#define AL_TEST_TYPE_s64 s64
+
+#define AL_TEST_FMT_s64 "%li"
+#define AL_TEST_PRINTF_s64(i) i
+
+static inline s32 al_test_s64_cmp(s64 a, s64 b)
+{
+ if (a == b) return 0;
+ else if (a < b) return -1;
+ else return 1;
+}
+
+#define AL_TEST_CMP_size_t al_test_size_t_cmp
+#define AL_TEST_TYPE_size_t size_t
+
+#define AL_TEST_FMT_size_t "%zu"
+#define AL_TEST_PRINTF_size_t(i) i
+
+static inline s32 al_test_ptr_cmp(void *a, void *b)
+{
+ if (a == b) return 0;
+ else if (a < b) return -1;
+ else return 1;
+}
+
+#define AL_TEST_CMP_ptr al_test_ptr_cmp
+#define AL_TEST_TYPE_ptr void *
+
+#define AL_TEST_FMT_ptr "%p"
+#define AL_TEST_PRINTF_ptr(i) i
+
+#endif // _AL_TEST_H
diff --git a/include/al/types.h b/include/al/types.h
new file mode 100644
index 0000000..c164127
--- /dev/null
+++ b/include/al/types.h
@@ -0,0 +1,61 @@
+#ifndef _AL_TYPES_H
+#define _AL_TYPES_H
+
+#ifdef _POSIX_C_SOURCE
+#if _POSIX_C_SOURCE < 200112L
+#error "libalabaster requires _POSIX_C_SOURCE >= 200112L"
+#endif
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#ifdef _XOPEN_SOURCE
+#if _XOPEN_SOURCE < 500
+#error "libalabaster requires _XOPEN_SOURCE >= 500"
+#endif
+#else
+#define _XOPEN_SOURCE 500
+#endif
+
+#if _WIN32 || _WIN64
+#if _WIN64
+#define PTR_IS_64
+#else
+#define PTR_IS_32
+#endif
+#elif __GNUC__
+#if __x86_64__ || __ppc64__
+#define PTR_IS_64
+#else
+#define PTR_IS_32
+#endif
+#elif UINTPTR_MAX > UINT_MAX
+#define PTR_IS_64
+#else
+#define PTR_IS_32
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <float.h>
+
+typedef uint8_t u8;
+typedef int8_t s8;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+typedef float f32;
+typedef double f64;
+
+#ifdef _MSC_VER
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#endif
+
+#endif // _AL_TYPES_H
diff --git a/include/al/wstr.h b/include/al/wstr.h
new file mode 100644
index 0000000..c2e8a07
--- /dev/null
+++ b/include/al/wstr.h
@@ -0,0 +1,81 @@
+#ifndef _AL_WSTR_H
+#define _AL_WSTR_H
+
+#include "types.h"
+#include "lib.h"
+#include "str.h"
+
+#include <wchar.h>
+
+typedef struct {
+ wchar_t *data;
+ u32 len;
+ u32 alloc;
+} wstr;
+
+#define al_wstr_at(w, i) (w)->data[i]
+#define al_wstr_atr(w, i) (w)->data[(w)->len + i]
+
+#define AL_WSTR_FMT "%.*ls"
+#define AL_WSTR_PRINTF(w) (w)->len, (w)->data
+#define AL_WSTR_PRINTF_MAXLEN(w, l) AL_MIN((w)->len, (l)), (w)->data
+
+static inline s32 al_wchar_width(wchar_t wc)
+{
+ return wcwidth(wc);
+}
+
+static inline void al_wstr_free(wstr *w)
+{
+ if (w->alloc > 0) al_free(w->data);
+}
+
+static inline u32 al_wstr_reserve(wstr *w, u32 size)
+{
+ if (size < al_grow_limit) {
+ size = al_next_power_of_two(size + 1);
+ } else {
+ size = (size + al_grow_limit) & ~al_grow_limit;
+ }
+
+ if (size <= w->alloc) return w->alloc;
+
+ w->data = (wchar_t *)((!w->data) ? al_malloc(size) : al_realloc(w->data, size));
+
+ return size;
+}
+
+static inline void al_wstr_sized(wstr *w, u32 size)
+{
+ w->len = 0;
+ w->alloc = 0;
+ w->data = NULL;
+ w->alloc = al_wstr_reserve(w, size * sizeof(wchar_t));
+ al_memset(w->data, 0, w->alloc);
+}
+
+static inline void al_wstr_from_cstr(wstr *w, char *c)
+{
+ size_t n = mbstowcs(NULL, c, 0) + 1;
+ al_wstr_sized(w, n);
+ w->len = mbstowcs(w->data, c, n);
+ w->data[w->len] = L'\0';
+}
+
+static inline void al_wstr_from_str(wstr *w, str *s)
+{
+ char *c_str = al_str_to_c_str(s);
+ al_wstr_from_cstr(w, c_str);
+ al_free(c_str);
+}
+
+static inline s32 al_wstr_width(wstr *w)
+{
+ s32 width = 0;
+ for (u32 i = 0; i < w->len; i++) {
+ width += al_wchar_width(al_wstr_at(w, i));
+ }
+ return width;
+}
+
+#endif // _AL_WSTR_H