27 Commits

Author SHA1 Message Date
7fb13f2439 Update compile script to run tests 2024-06-02 23:35:34 +01:00
0b63bc746d Add tests 2024-06-02 23:35:26 +01:00
6ee3c762df Add terminal colour constants 2024-06-02 23:35:04 +01:00
59f1c3eb58 Add testing utilities 2024-06-02 23:34:53 +01:00
55d0c25c90 Reformat 2024-06-02 23:34:38 +01:00
cfc98e0137 Update .gitignore 2024-06-02 21:26:32 +01:00
Abdelrahman Said
32877cdeaa Fix platform detection on Apple platforms 2024-06-02 20:54:20 +01:00
Abdelrahman Said
7e2d7b28b7 Update .gitignore 2024-06-02 20:54:07 +01:00
23886f40e8 Switch to using wapp_mem_util_alloc instead of calloc 2024-06-02 20:42:10 +01:00
57de75c1f8 Set allocation flags per platform 2024-06-02 20:41:30 +01:00
9807334ac9 Complete memory allocation and deallocation function definitions 2024-06-02 20:09:56 +01:00
18448dd7c1 Define memory allocation and deallocation functions 2024-06-02 20:09:08 +01:00
62fdef8601 Add platform detection definitions 2024-06-02 20:08:28 +01:00
61c29ee564 Remove growing_arena and use base_arena 2024-06-02 01:17:03 +01:00
36bc8ab54a Ignore test files 2024-06-02 01:16:46 +01:00
a8e5913254 Add release build and compile test 2024-06-02 01:16:24 +01:00
b59aedad89 Add BasicString and StringView 2024-04-29 23:22:59 +01:00
3f5e3558b9 Add BasicString 2024-04-29 23:06:22 +01:00
bb2db0d30d Return StringUpdate from the dstr update functions 2024-04-29 22:55:40 +01:00
e75846a507 Update dstr to use Arena 2024-04-29 22:48:10 +01:00
05e56d67ea Reorganise the code 2024-04-29 21:42:33 +01:00
7164156c35 Simplify Arena 2024-04-28 22:59:37 +01:00
e67e1df9a5 Rename arena free functions 2024-04-28 22:04:23 +01:00
e206b4f4a6 Remove Allocator functions from Arena 2024-04-24 23:40:10 +01:00
05bfa73509 Revert "Simplify Arena allocator"
This reverts commit d4313fb8e4.
2024-04-24 23:18:19 +01:00
d4313fb8e4 Simplify Arena allocator 2024-04-24 23:10:55 +01:00
92db9206cc Remove complicated memory abstractions 2024-04-24 22:51:42 +01:00
29 changed files with 715 additions and 863 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,7 @@
.cache
.vscode
test
test.*
*.dSYM
compile_commands.json
libwapp.so

68
compile
View File

@@ -1,18 +1,66 @@
#!/bin/bash
BUILD_TYPE="debug"
while [[ $# > 0 ]];do
case $1 in
--release)
BUILD_TYPE="release"
shift
;;
*|-*|--*)
echo "Unknown option $1"
exit 1
;;
esac
done
CC=clang
CFLAGS="-Wall -Werror -pedantic "
LIBFLAGS="-fPIC -shared"
INCLUDE="\
-Ialiases \
-Icpath/include \
-Idstr/include \
$(find mem/include -type d | xargs -I{} echo -n "-I{} ") \
$(find src -type d | xargs -I{} echo -n "-I{} ") \
"
TEST_INCLUDE="\
$(find tests -type d | xargs -I{} echo -n "-I{} ") \
"
SRC="\
cpath/src/*.c \
dstr/src/*.c \
mem/src/*/*.c \
$(find src -type f -name "*.c" | xargs -I{} echo -n "{} ") \
"
TEST_SRC="\
$(find tests -type f -name "*.c" | xargs -I{} echo -n "{} ") \
"
CFLAGS="-O3 -shared -fPIC -Wall -Werror -pedantic"
OUT="libwapp.so"
(set -x ; $CC $CFLAGS $INCLUDE $SRC -o $OUT)
if [[ $BUILD_TYPE == "release" ]]; then
CFLAGS+="-O3"
else
CFLAGS+="-g -fsanitize=address -fsanitize=undefined"
fi
OUT="libwapp.so"
TEST_OUT="./wapptest"
# Compile tests
if [[ $(echo $TEST_SRC | xargs) != "" ]]; then
(set -x ; $CC $CFLAGS $INCLUDE $TEST_INCLUDE $SRC $TEST_SRC -o $TEST_OUT)
fi
# Run tests and exit on failure
if [[ -f $TEST_OUT ]]; then
$TEST_OUT
STATUS="$?"
rm $TEST_OUT
if [[ $STATUS != "0" ]]; then
exit 1
fi
fi
# Compile library
(set -x ; $CC $CFLAGS $LIBFLAGS $INCLUDE $SRC -o $OUT)
# Compile test.c if it exists
if [[ -f ./test.c ]]; then
(set -x ; $CC $CFLAGS $INCLUDE $SRC ./test.c -o test)
fi

View File

@@ -1,40 +0,0 @@
#ifndef MEM_ALLOCATOR_H
#define MEM_ALLOCATOR_H
#include "aliases.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef void *(MemAllocFunc)(u64 size, void *alloc_obj);
typedef void *(MemAllocAlignedFunc)(u64 size, u64 alignment, void *alloc_obj);
typedef void *(MemReallocFunc)(void *ptr, u64 size, void *alloc_obj);
typedef void *(MemReallocAlignedFunc)(void *ptr, u64 size, u64 alignment,
void *alloc_obj);
typedef void(MemFreeFunc)(void **ptr, void *alloc_obj);
typedef struct allocator Allocator;
struct allocator {
void *obj;
MemAllocFunc *alloc;
MemAllocAlignedFunc *alloc_aligned;
MemReallocFunc *realloc;
MemReallocAlignedFunc *realloc_aligned;
MemFreeFunc *free;
};
void *wapp_mem_allocator_alloc(const Allocator *allocator, u64 size);
void *wapp_mem_allocator_alloc_aligned(const Allocator *allocator, u64 size,
u64 alignment);
void *wapp_mem_allocator_realloc(const Allocator *allocator, void *ptr,
u64 size);
void *wapp_mem_allocator_realloc_aligned(const Allocator *allocator, void *ptr,
u64 size, u64 alignment);
void wapp_mem_allocator_free(const Allocator *allocator, void **ptr);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !MEM_ALLOCATOR_H

View File

@@ -1,28 +0,0 @@
#ifndef MEM_ARENA_H
#define MEM_ARENA_H
#include "aliases.h"
#include "mem_allocator.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct growing_arena Arena;
Allocator wapp_mem_arena_allocator(const Arena *arena);
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity);
void *wapp_mem_arena_alloc(Arena *arena, u64 size);
void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment);
void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 size);
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 size,
u64 alignment);
void wapp_mem_arena_clear(Arena *arena);
void wapp_mem_arena_free(Arena **arena);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !MEM_ARENA_H

View File

@@ -1,32 +0,0 @@
#ifndef MEM_CTX_H
#define MEM_CTX_H
#include "mem_allocator.h"
#include "mem_arena.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef enum {
CTX_DEST_BUFFER_MAIN,
CTX_DEST_BUFFER_TEMP,
COUNT_CTX_DEST_BUFFER,
} CTXDestBuffer;
Allocator wapp_mem_ctx_allocator(CTXDestBuffer buffer);
void wapp_mem_ctx_init(u64 main_buf_capacity, u64 temp_buf_capacity);
void *wapp_mem_ctx_alloc(CTXDestBuffer buffer, u64 size);
void *wapp_mem_ctx_alloc_aligned(CTXDestBuffer buffer, u64 size, u64 alignment);
void *wapp_mem_ctx_realloc(CTXDestBuffer buffer, void *ptr, u64 size);
void *wapp_mem_ctx_realloc_aligned(CTXDestBuffer buffer, void *ptr, u64 size,
u64 alignment);
void wapp_mem_ctx_clear(CTXDestBuffer buffer);
void wapp_mem_ctx_free(void);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !MEM_CTX_H

View File

@@ -1,15 +0,0 @@
#ifndef MEM_LIBC_H
#define MEM_LIBC_H
#include "mem_allocator.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
Allocator wapp_mem_libc_allocator(void);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !MEM_LIBC_H

View File

@@ -1,16 +0,0 @@
#ifndef MEM_UTILS_H
#define MEM_UTILS_H
#include "aliases.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void *wapp_mem_util_align_forward(void *ptr, u64 alignment);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !MEM_UTILS_H

View File

@@ -1,45 +0,0 @@
#include "mem_allocator.h"
#include <stdlib.h>
void *wapp_mem_allocator_alloc(const Allocator *allocator, u64 size) {
if (!allocator || !(allocator->alloc)) {
return NULL;
}
return allocator->alloc(size, allocator->obj);
}
void *wapp_mem_allocator_alloc_aligned(const Allocator *allocator, u64 size,
u64 alignment) {
if (!allocator || !(allocator->alloc_aligned)) {
return NULL;
}
return allocator->alloc_aligned(size, alignment, allocator->obj);
}
void *wapp_mem_allocator_realloc(const Allocator *allocator, void *ptr,
u64 size) {
if (!allocator || !(allocator->realloc)) {
return NULL;
}
return allocator->realloc(ptr, size, allocator->obj);
}
void *wapp_mem_allocator_realloc_aligned(const Allocator *allocator, void *ptr,
u64 size, u64 alignment) {
if (!allocator || !(allocator->realloc_aligned)) {
return NULL;
}
return allocator->realloc_aligned(ptr, size, alignment, allocator->obj);
}
void wapp_mem_allocator_free(const Allocator *allocator, void **ptr) {
if (!allocator || !(allocator->free)) {
return;
}
allocator->free(ptr, allocator->obj);
}

View File

@@ -1,406 +0,0 @@
#include "mem_arena.h"
#include "aliases.h"
#include "mem_allocator.h"
#include "mem_utils.h"
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifndef DEFAULT_ALIGNMENT
// Why 2 * sizeof(void *) instead of sizeof(void *)
// https://handmade.network/forums/t/6860-alignment_arena_allocator
#define DEFAULT_ALIGNMENT (2 * sizeof(void *))
#endif /* ifndef DEFAULT_ALIGNMENT */
#define HDR_MAGIC_BYTE_COUNT 8
#define HDR_MAGIC \
{ 0x57, 0x41, 0x41, 0x52, 0x4e, 0x48, 0x44, 0x52 }
#define MAX_HDR_SEARCH_LENGTH 256
#define ARENA_MINIMUM_CAPACITY 1024
typedef struct arena_alloc_hdr ArenaAllocHDR;
struct arena_alloc_hdr {
u8 magic[HDR_MAGIC_BYTE_COUNT];
u64 alloc_size;
u64 alignment;
u8 *alloc_start;
};
typedef struct base_arena BaseArena;
struct base_arena {
u8 *buf;
u8 *offset;
u64 capacity;
BaseArena *prev;
BaseArena *next;
};
struct growing_arena {
BaseArena *active_arena;
u64 count;
u64 initial_capacity;
};
internal BaseArena *find_arena_from_pointer(const Arena *arena, void *ptr);
internal bool base_arena_init(BaseArena *arena, u64 capacity);
internal void *base_arena_alloc_aligned(BaseArena *arena, u64 size,
u64 alignment);
internal void base_arena_clear(BaseArena *arena);
internal void base_arena_free(BaseArena *arena);
internal ArenaAllocHDR *find_alloc_header(BaseArena *arena, void *alloc_ptr);
internal void *mem_arena_alloc(u64 size, void *alloc_obj);
internal void *mem_arena_alloc_aligned(u64 size, u64 alignment,
void *alloc_obj);
internal void *mem_arena_realloc(void *ptr, u64 size, void *alloc_obj);
internal void *mem_arena_realloc_aligned(void *ptr, u64 size, u64 alignment,
void *alloc_obj);
// PUBLIC API
Allocator wapp_mem_arena_allocator(const Arena *arena) {
return (Allocator){
.obj = (void *)arena,
.alloc = mem_arena_alloc,
.alloc_aligned = mem_arena_alloc_aligned,
.realloc = mem_arena_realloc,
.realloc_aligned = mem_arena_realloc_aligned,
.free = NULL,
};
}
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity) {
if (!arena || *arena || base_capacity == 0) {
return false;
}
*arena = (Arena *)calloc(1, sizeof(Arena));
Arena *arena_ptr = *arena;
if (!arena_ptr) {
return false;
}
arena_ptr->active_arena = (BaseArena *)calloc(1, sizeof(BaseArena));
if (!(arena_ptr->active_arena)) {
wapp_mem_arena_free(arena);
return false;
}
if (!base_arena_init(arena_ptr->active_arena, base_capacity)) {
wapp_mem_arena_free(arena);
return false;
}
arena_ptr->count = 1;
arena_ptr->initial_capacity = base_capacity;
return true;
}
void *wapp_mem_arena_alloc(Arena *arena, u64 size) {
return wapp_mem_arena_alloc_aligned(arena, size, DEFAULT_ALIGNMENT);
}
void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment) {
if (!arena || !(arena->active_arena)) {
return NULL;
}
void *output = base_arena_alloc_aligned(arena->active_arena, size, alignment);
if (!output) {
if (arena->active_arena->next) {
arena->active_arena = arena->active_arena->next;
} else {
arena->active_arena->next = (BaseArena *)calloc(1, sizeof(BaseArena));
if (!(arena->active_arena->next)) {
return NULL;
}
if (!base_arena_init(arena->active_arena->next,
arena->initial_capacity)) {
free(arena->active_arena->next);
return NULL;
}
arena->active_arena->next->prev = arena->active_arena;
arena->active_arena = arena->active_arena->next;
++(arena->count);
}
output = base_arena_alloc_aligned(arena->active_arena, size, alignment);
if (!output) {
return NULL;
}
}
memset(output, 0, size);
return output;
}
void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 size) {
return wapp_mem_arena_realloc_aligned(arena, ptr, size, DEFAULT_ALIGNMENT);
}
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 size,
u64 alignment) {
if (!arena) {
return NULL;
}
BaseArena *base_arena = find_arena_from_pointer(arena, ptr);
if (!base_arena) {
return NULL;
}
ArenaAllocHDR *header = find_alloc_header(base_arena, ptr);
if (!header) {
return NULL;
}
if (header->alloc_start + header->alloc_size == base_arena->offset) {
// Avoid allocating new pointer and copying memory if pointer is at the end
// of the arena
i64 diff = size - header->alloc_size;
u8 *new_offset = base_arena->offset + diff;
u8 *clear_start = diff < 0 ? new_offset : base_arena->offset;
memset(clear_start, 0, llabs(diff));
header->alloc_size = size;
base_arena->offset = new_offset;
return header->alloc_start;
}
void *new_alloc = wapp_mem_arena_alloc_aligned(arena, size, alignment);
if (!new_alloc) {
return NULL;
}
u64 to_copy = size < header->alloc_size ? size : header->alloc_size;
memcpy(new_alloc, ptr, to_copy);
return new_alloc;
}
void wapp_mem_arena_clear(Arena *arena) {
if (!arena) {
return;
}
BaseArena *last_active = NULL;
while (arena->active_arena) {
base_arena_clear(arena->active_arena);
last_active = arena->active_arena;
arena->active_arena = arena->active_arena->prev;
}
arena->active_arena = last_active;
}
void wapp_mem_arena_free(Arena **arena) {
if (!arena) {
return;
}
Arena *arena_ptr = *arena;
if (!arena_ptr) {
return;
}
BaseArena *current;
BaseArena *next;
BaseArena *prev;
current = arena_ptr->active_arena->next;
while (current) {
next = current->next;
base_arena_free(current);
free(current);
current = next;
}
current = arena_ptr->active_arena->prev;
while (current) {
prev = current->prev;
base_arena_free(current);
free(current);
current = prev;
}
base_arena_free(arena_ptr->active_arena);
free(arena_ptr->active_arena);
arena_ptr->active_arena = NULL;
arena_ptr->count = 0;
arena_ptr->initial_capacity = 0;
free(*arena);
*arena = NULL;
}
// INTERNAL FUNCTIONS
internal BaseArena *find_arena_from_pointer(const Arena *arena, void *ptr) {
if (!arena || !ptr) {
return NULL;
}
// Ensure pointer is not out of bounds
u8 *alloc = (u8 *)ptr;
BaseArena *active = arena->active_arena;
if (alloc > active->buf + arena->initial_capacity) {
return NULL;
}
for (u64 i = 0; i < arena->count; ++i) {
if (alloc >= active->buf && alloc < active->buf + arena->initial_capacity) {
return (BaseArena *)active;
}
active = active->prev;
}
return NULL;
}
internal bool base_arena_init(BaseArena *arena, u64 capacity) {
if (!arena || arena->buf || capacity == 0) {
return false;
}
u64 arena_capacity =
capacity >= ARENA_MINIMUM_CAPACITY ? capacity : ARENA_MINIMUM_CAPACITY;
arena->buf = (u8 *)calloc(arena_capacity, sizeof(u8));
if (!(arena->buf)) {
return false;
}
arena->capacity = arena_capacity;
arena->offset = arena->buf;
arena->prev = arena->next = NULL;
return true;
}
internal void *base_arena_alloc_aligned(BaseArena *arena, u64 size,
u64 alignment) {
if (!arena) {
return NULL;
}
u8 *start_offset = arena->offset;
u8 *alloc_start = arena->offset + sizeof(ArenaAllocHDR);
u8 *output = wapp_mem_util_align_forward((void *)alloc_start, alignment);
if (output + size >= arena->buf + arena->capacity) {
return NULL;
}
ArenaAllocHDR *header = (ArenaAllocHDR *)start_offset;
*header = (ArenaAllocHDR){
.magic = HDR_MAGIC,
.alloc_size = size,
.alignment = alignment,
.alloc_start = output,
};
arena->offset = output + size;
return (void *)output;
}
internal void base_arena_clear(BaseArena *arena) {
if (!arena) {
return;
}
memset(arena->buf, 0, arena->offset - arena->buf);
arena->offset = arena->buf;
}
internal void base_arena_free(BaseArena *arena) {
if (!arena) {
return;
}
if (arena->buf) {
free(arena->buf);
}
arena->buf = arena->offset = NULL;
arena->capacity = 0;
arena->prev = arena->next = NULL;
}
internal ArenaAllocHDR *find_alloc_header(BaseArena *arena, void *alloc_ptr) {
persistent const u8 magic[HDR_MAGIC_BYTE_COUNT] = HDR_MAGIC;
u8 *current = (u8 *)alloc_ptr;
u8 *max_search_end = current - MAX_HDR_SEARCH_LENGTH;
u8 *arena_buf_start = arena->buf;
u8 *search_end =
max_search_end > arena_buf_start ? max_search_end : arena_buf_start;
bool match;
for (; current >= search_end; --current) {
match = true;
for (u64 i = 0; i < HDR_MAGIC_BYTE_COUNT; ++i) {
if (current[i] != magic[i]) {
match = false;
break;
}
}
if (match) {
return (ArenaAllocHDR *)current;
}
}
return NULL;
}
internal void *mem_arena_alloc(u64 size, void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;
return wapp_mem_arena_alloc(arena, size);
}
internal void *mem_arena_alloc_aligned(u64 size, u64 alignment,
void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;
return wapp_mem_arena_alloc_aligned(arena, size, alignment);
}
internal void *mem_arena_realloc(void *ptr, u64 size, void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;
return wapp_mem_arena_realloc(arena, ptr, size);
}
internal void *mem_arena_realloc_aligned(void *ptr, u64 size, u64 alignment,
void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;
return wapp_mem_arena_realloc_aligned(arena, ptr, size, alignment);
}

View File

@@ -1,106 +0,0 @@
#include "mem_ctx.h"
#include "aliases.h"
#include "mem_arena.h"
#include <assert.h>
#include <stdlib.h>
typedef struct mem_ctx MemCTX;
struct mem_ctx {
Arena *main;
Arena *temp;
bool main_initialised;
bool temp_initialised;
};
internal MemCTX g_context = {0};
internal Arena *get_arena(CTXDestBuffer buffer);
Allocator wapp_mem_ctx_allocator(CTXDestBuffer buffer) {
Arena *arena = get_arena(buffer);
return wapp_mem_arena_allocator(arena);
}
void wapp_mem_ctx_init(u64 main_buf_capacity, u64 temp_buf_capacity) {
g_context.main_initialised =
wapp_mem_arena_init(&g_context.main, main_buf_capacity);
g_context.temp_initialised =
wapp_mem_arena_init(&g_context.temp, temp_buf_capacity);
}
void *wapp_mem_ctx_alloc(CTXDestBuffer buffer, u64 size) {
Arena *arena = get_arena(buffer);
if (!arena) {
return NULL;
}
return wapp_mem_arena_alloc(arena, size);
}
void *wapp_mem_ctx_alloc_aligned(CTXDestBuffer buffer, u64 size,
u64 alignment) {
Arena *arena = get_arena(buffer);
if (!arena) {
return NULL;
}
return wapp_mem_arena_alloc_aligned(arena, size, alignment);
}
void *wapp_mem_ctx_realloc(CTXDestBuffer buffer, void *ptr, u64 size) {
Arena *arena = get_arena(buffer);
if (!arena) {
return NULL;
}
return wapp_mem_arena_realloc(arena, ptr, size);
}
void *wapp_mem_ctx_realloc_aligned(CTXDestBuffer buffer, void *ptr, u64 size,
u64 alignment) {
Arena *arena = get_arena(buffer);
if (!arena) {
return NULL;
}
return wapp_mem_arena_realloc_aligned(arena, ptr, size, alignment);
}
void wapp_mem_ctx_clear(CTXDestBuffer buffer) {
Arena *arena = get_arena(buffer);
if (!arena) {
return;
}
wapp_mem_arena_clear(arena);
}
void wapp_mem_ctx_free(void) {
wapp_mem_arena_free(&(g_context.main));
g_context.main_initialised = false;
wapp_mem_arena_free(&(g_context.temp));
g_context.temp_initialised = false;
}
internal Arena *get_arena(CTXDestBuffer buffer) {
Arena *output = NULL;
switch (buffer) {
case CTX_DEST_BUFFER_MAIN:
if (g_context.main_initialised) {
output = g_context.main;
}
break;
case CTX_DEST_BUFFER_TEMP:
if (g_context.temp_initialised) {
output = g_context.temp;
}
break;
default:
assert(false && "Not all context destination buffers are handled");
break;
}
return output;
}

View File

@@ -1,48 +0,0 @@
#include "mem_libc.h"
#include "aliases.h"
#include "mem_allocator.h"
#include <stdlib.h>
#include <string.h>
internal void *mem_libc_alloc(u64 size, void *alloc_obj);
internal void *mem_libc_alloc_aligned(u64 size, u64 alignment, void *alloc_obj);
internal void *mem_libc_realloc(void *ptr, u64 size, void *alloc_obj);
internal void mem_libc_free(void **ptr, void *alloc_obj);
Allocator wapp_mem_libc_allocator(void) {
return (Allocator){
.obj = NULL,
.alloc = mem_libc_alloc,
.alloc_aligned = mem_libc_alloc_aligned,
.realloc = mem_libc_realloc,
.realloc_aligned = NULL,
.free = mem_libc_free,
};
}
internal void *mem_libc_alloc(u64 size, void *alloc_obj) {
return calloc(1, size);
}
internal void *mem_libc_alloc_aligned(u64 size, u64 alignment,
void *alloc_obj) {
void *output = aligned_alloc(alignment, size);
if (output) {
memset(output, 0, size);
}
return output;
}
internal void *mem_libc_realloc(void *ptr, u64 size, void *alloc_obj) {
return realloc(ptr, size);
}
internal void mem_libc_free(void **ptr, void *alloc_obj) {
if (!ptr || !(*ptr)) {
return;
}
free(*ptr);
*ptr = NULL;
}

View File

@@ -1,28 +0,0 @@
#include "mem_utils.h"
#include "aliases.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
internal bool is_power_of_two(u64 num) { return (num & (num - 1)) == 0; }
void *wapp_mem_util_align_forward(void *ptr, u64 alignment) {
if (!ptr) {
return NULL;
}
assert(is_power_of_two(alignment));
uptr p = (uptr)ptr;
uptr align = (uptr)alignment;
// Similar to p % align, but it's a faster implementation that works fine
// because align is guaranteed to be a power of 2
uptr modulo = p & (align - 1);
if (modulo != 0) {
p += align - modulo;
}
return (void *)p;
}

102
src/mem/arena/mem_arena.c Normal file
View File

@@ -0,0 +1,102 @@
#include "mem_arena.h"
#include "aliases.h"
#include "mem_utils.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifndef DEFAULT_ALIGNMENT
// Why 2 * sizeof(void *) instead of sizeof(void *)
// https://handmade.network/forums/t/6860-alignment_arena_allocator
#define DEFAULT_ALIGNMENT (2 * sizeof(void *))
#endif /* ifndef DEFAULT_ALIGNMENT */
#define ARENA_MINIMUM_CAPACITY 1024
struct arena {
u8 *buf;
u8 *offset;
u64 capacity;
};
// PUBLIC API
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity, MemAllocFlags flags,
bool zero_buffer) {
if (!arena || *arena || base_capacity == 0) {
return false;
}
*arena = (Arena *)calloc(1, sizeof(Arena));
Arena *arena_ptr = *arena;
if (!arena_ptr) {
return false;
}
u64 arena_capacity = base_capacity >= ARENA_MINIMUM_CAPACITY
? base_capacity
: ARENA_MINIMUM_CAPACITY;
arena_ptr->buf = (u8 *)wapp_mem_util_alloc(
arena_capacity, WAPP_MEM_ACCESS_READ_WRITE, flags,
zero_buffer ? WAPP_MEM_INIT_INITIALISED : WAPP_MEM_INIT_UNINITIALISED);
if (!(arena_ptr->buf)) {
wapp_mem_arena_destroy(arena);
return false;
}
arena_ptr->capacity = arena_capacity;
arena_ptr->offset = arena_ptr->buf;
return true;
}
void *wapp_mem_arena_alloc(Arena *arena, u64 size) {
return wapp_mem_arena_alloc_aligned(arena, size, DEFAULT_ALIGNMENT);
}
void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment) {
if (!arena) {
return NULL;
}
u8 *alloc_start = arena->offset;
u8 *output = wapp_mem_util_align_forward((void *)alloc_start, alignment);
if (output + size >= arena->buf + arena->capacity) {
return NULL;
}
arena->offset = output + size;
memset(output, 0, size);
return (void *)output;
}
void wapp_mem_arena_clear(Arena *arena) {
if (!arena) {
return;
}
memset(arena->buf, 0, arena->offset - arena->buf);
arena->offset = arena->buf;
}
void wapp_mem_arena_destroy(Arena **arena) {
if (!arena) {
return;
}
Arena *arena_ptr = *arena;
if (arena_ptr->buf) {
wapp_mem_util_free(arena_ptr->buf, arena_ptr->capacity);
}
arena_ptr->buf = arena_ptr->offset = NULL;
arena_ptr->capacity = 0;
free(*arena);
*arena = NULL;
}

25
src/mem/arena/mem_arena.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef MEM_ARENA_H
#define MEM_ARENA_H
#include "aliases.h"
#include "mem_utils.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct arena Arena;
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity, MemAllocFlags flags,
bool zero_buffer);
void *wapp_mem_arena_alloc(Arena *arena, u64 size);
void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment);
void wapp_mem_arena_clear(Arena *arena);
void wapp_mem_arena_destroy(Arena **arena);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !MEM_ARENA_H

114
src/mem/util/mem_utils.c Normal file
View File

@@ -0,0 +1,114 @@
#include "mem_utils.h"
#include "aliases.h"
#include "platform.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#if defined(WAPP_PLATFORM_WINDOWS)
#include <Windows.h>
#include <memoryapi.h>
internal const i32 access_types[] = {
[WAPP_MEM_ACCESS_NONE] = PAGE_NOACCESS,
[WAPP_MEM_ACCESS_READ_ONLY] = PAGE_READONLY,
[WAPP_MEM_ACCESS_EXEC_ONLY] = PAGE_EXECUTE,
[WAPP_MEM_ACCESS_READ_WRITE] = PAGE_READWRITE,
[WAPP_MEM_ACCESS_READ_EXEC] = PAGE_EXECUTE_READ,
[WAPP_MEM_ACCESS_READ_WRITE_EXEC] = PAGE_EXECUTE_READWRITE,
};
internal inline void *alloc_windows(u64 size, MemAccess access,
MemAllocFlags flags);
#elif defined(WAPP_PLATFORM_POSIX)
#include <sys/mman.h>
internal const i32 access_types[] = {
[WAPP_MEM_ACCESS_NONE] = PROT_NONE,
[WAPP_MEM_ACCESS_READ_ONLY] = PROT_READ,
[WAPP_MEM_ACCESS_EXEC_ONLY] = PROT_EXEC,
[WAPP_MEM_ACCESS_READ_WRITE] = PROT_READ | PROT_WRITE,
[WAPP_MEM_ACCESS_READ_EXEC] = PROT_READ | PROT_EXEC,
[WAPP_MEM_ACCESS_READ_WRITE_EXEC] = PROT_READ | PROT_WRITE | PROT_EXEC,
};
internal inline void *alloc_posix(u64 size, MemAccess access,
MemAllocFlags flags);
#else
#error "Unrecognised platform"
#endif
internal bool is_power_of_two(u64 num) { return (num & (num - 1)) == 0; }
void *wapp_mem_util_align_forward(void *ptr, u64 alignment) {
if (!ptr) {
return NULL;
}
assert(is_power_of_two(alignment));
uptr p = (uptr)ptr;
uptr align = (uptr)alignment;
// Similar to p % align, but it's a faster implementation that works fine
// because align is guaranteed to be a power of 2
uptr modulo = p & (align - 1);
if (modulo != 0) {
p += align - modulo;
}
return (void *)p;
}
void *wapp_mem_util_alloc(u64 size, MemAccess access, MemAllocFlags flags,
MemInitType type) {
#if defined(WAPP_PLATFORM_WINDOWS)
// Ensure memory is committed if it's meant to be initialised
if (type == WAPP_MEM_INIT_INITIALISED) {
flags |= WAPP_MEM_ALLOC_COMMIT;
}
void *output = alloc_windows(size, access, flags);
#elif defined(WAPP_PLATFORM_POSIX)
void *output = alloc_posix(size, access, flags);
#else
return NULL;
#endif
if (type == WAPP_MEM_INIT_INITIALISED) {
memset(output, 0, size);
}
return output;
}
void wapp_mem_util_free(void *ptr, u64 size) {
#if defined(WAPP_PLATFORM_WINDOWS)
VirtualFree(ptr, size, MEM_RELEASE);
#elif defined(WAPP_PLATFORM_POSIX)
munmap(ptr, size);
#endif
}
#ifdef WAPP_PLATFORM_WINDOWS
internal inline void *alloc_windows(u64 size, MemAccess access,
MemAllocFlags flags) {
return VirtualAlloc2(NULL, NULL, (SIZE_T)size, flags, access_types[access]);
}
#endif
#if defined(WAPP_PLATFORM_POSIX)
internal inline void *alloc_posix(u64 size, MemAccess access,
MemAllocFlags flags) {
i32 alloc_flags = flags | MAP_ANON | MAP_PRIVATE;
#if defined(WAPP_PLATFORM_LINUX) || defined(WAPP_PLATFORM_GNU) || \
defined(WAPP_PLATFORM_NET_BSD)
alloc_flags |= MAP_NORESERVE;
#endif
return mmap(NULL, size, access_types[access], alloc_flags, -1, 0);
}
#endif

60
src/mem/util/mem_utils.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef MEM_UTILS_H
#define MEM_UTILS_H
#include "aliases.h"
#include "platform.h"
#if defined(WAPP_PLATFORM_WINDOWS)
#include <Windows.h>
#include <memoryapi.h>
#elif defined(WAPP_PLATFORM_POSIX)
#include <sys/mman.h>
#else
#error "Unrecognised platform"
#endif
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef enum mem_access {
WAPP_MEM_ACCESS_NONE,
WAPP_MEM_ACCESS_READ_ONLY,
WAPP_MEM_ACCESS_EXEC_ONLY,
WAPP_MEM_ACCESS_READ_WRITE,
WAPP_MEM_ACCESS_READ_EXEC,
WAPP_MEM_ACCESS_READ_WRITE_EXEC,
} MemAccess;
typedef enum mem_alloc_flags {
#if defined(WAPP_PLATFORM_WINDOWS)
WAPP_MEM_ALLOC_RESERVE = MEM_RESERVE,
WAPP_MEM_ALLOC_COMMIT = MEM_COMMIT,
#elif defined(WAPP_PLATFORM_LINUX) || defined(WAPP_PLATFORM_GNU)
WAPP_MEM_ALLOC_RESERVE = 0,
WAPP_MEM_ALLOC_COMMIT = MAP_POPULATE,
#elif defined(WAPP_PLATFORM_FREE_BSD)
WAPP_MEM_ALLOC_RESERVE = 0,
WAPP_MEM_ALLOC_COMMIT = MAP_PREFAULT_READ,
#elif defined(WAPP_PLATFORM_BSD) || defined(WAPP_PLATFORM_UNIX) || \
defined(WAPP_PLATFORM_APPLE)
WAPP_MEM_ALLOC_RESERVE = 0,
WAPP_MEM_ALLOC_COMMIT = 0,
#endif
} MemAllocFlags;
typedef enum mem_init_type {
WAPP_MEM_INIT_UNINITIALISED,
WAPP_MEM_INIT_INITIALISED,
} MemInitType;
void *wapp_mem_util_align_forward(void *ptr, u64 alignment);
void *wapp_mem_util_alloc(u64 size, MemAccess access, MemAllocFlags flags,
MemInitType type);
void wapp_mem_util_free(void *ptr, u64 size);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !MEM_UTILS_H

63
src/platform/platform.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef PLATFORM_H
#define PLATFORM_H
// clang-format off
#if defined(__ANDROID__)
#define WAPP_PLATFORM_ANDROID
#define WAPP_PLATFORM_POSIX
#elif defined(__FreeBSD__)
#define WAPP_PLATFORM_FREE_BSD
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#elif defined(__NetBSD__)
#define WAPP_PLATFORM_NET_BSD
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#elif defined(__OpenBSD__)
#define WAPP_PLATFORM_OPEN_BSD
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#elif defined(__DragonFly__)
#define WAPP_PLATFORM_DRAGON_FLY
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#elif defined(__bsdi__)
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#elif defined(__linux__) || defined(linux) || defined(__linux) || defined(__gnu_linux__)
#define WAPP_PLATFORM_LINUX
#define WAPP_PLATFORM_POSIX
#elif defined(__GNU__) || defined(__gnu_hurd__)
#define WAPP_PLATFORM_GNU
#define WAPP_PLATFORM_POSIX
#elif defined(__APPLE__) || defined(__MACH__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define WAPP_PLATFORM_IOS
#define WAPP_PLATFORM_APPLE
#define WAPP_PLATFORM_POSIX
#elif TARGET_OS_MAC
#define WAPP_PLATFORM_MACOS
#define WAPP_PLATFORM_APPLE
#define WAPP_PLATFORM_POSIX
#else
#error "Unrecognised Apple platform"
#endif
#elif defined(_WIN64)
#define WAPP_PLATFORM_WINDOWS64
#define WAPP_PLATFORM_WINDOWS
#elif defined(_WIN32)
#define WAPP_PLATFORM_WINDOWS32
#define WAPP_PLATFORM_WINDOWS
#elif defined(__CYGWIN__)
#define WAPP_PLATFORM_CYGWIN
#define WAPP_PLATFORM_WINDOWS
#elif defined(__unix__) || defined(__unix)
#define WAPP_PLATFORM_UNIX
#define WAPP_PLATFORM_POSIX
#else
#error "Unrecognised platform"
#endif
// clang-format on
#endif // !PLATFORM_H

View File

@@ -0,0 +1,34 @@
#ifndef BASIC_STRING_H
#define BASIC_STRING_H
#include "aliases.h"
#include <stdio.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct bstr BasicString;
struct bstr {
u64 size;
const char *buf;
};
typedef struct strvw StringView;
struct strvw {
const u64 size;
const char *buf;
};
#define new_string(STR) \
{ .size = strlen(STR), .buf = STR }
#define wapp_bstr_new(STR) ((BasicString)new_string(STR))
#define wapp_strvw_new(STR) ((StringView)new_string(STR))
#define wapp_string_print(STR) (printf("%.*s\n", (i32)STR.size, STR.buf))
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !BASIC_STRING_H

View File

@@ -1,7 +1,6 @@
#include "dstr.h"
#include "aliases.h"
#include "mem_allocator.h"
#include "mem_libc.h"
#include "mem_arena.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -9,45 +8,42 @@
// Use this scalar to allocate extra memory in order to avoid having to
// constantly reallocate
#define CAPACITY_SCALAR 8
#define MINIMUM_DSTR_CAPACITY 1024
struct dstr {
Allocator allocator;
u64 capacity;
u64 size;
char buf[];
};
String *wapp_dstr_with_capacity(u64 capacity, const Allocator *allocator) {
Allocator alloc;
if (allocator) {
alloc = *allocator;
} else {
alloc = wapp_mem_libc_allocator();
String *wapp_dstr_with_capacity(u64 capacity, Arena *arena) {
if (!arena) {
return NULL;
}
String *out =
(String *)wapp_mem_allocator_alloc(&alloc, sizeof(String) + capacity + 1);
(String *)wapp_mem_arena_alloc(arena, sizeof(String) + capacity + 1);
if (!out) {
return NULL;
}
out->allocator = alloc;
out->capacity = capacity;
out->size = 0;
return out;
}
String *wapp_dstr_from_string(const char *str, const Allocator *allocator) {
if (!str) {
String *wapp_dstr_from_string(const char *str, Arena *arena) {
if (!str || !arena) {
return NULL;
}
u64 length = strlen(str);
u64 capacity = length * CAPACITY_SCALAR;
capacity =
capacity >= MINIMUM_DSTR_CAPACITY ? capacity : MINIMUM_DSTR_CAPACITY;
String *out = wapp_dstr_with_capacity(capacity, allocator);
String *out = wapp_dstr_with_capacity(capacity, arena);
if (!out) {
return NULL;
}
@@ -58,55 +54,45 @@ String *wapp_dstr_from_string(const char *str, const Allocator *allocator) {
return out;
}
void wapp_dstr_update(String **dst, const char *src) {
StringUpdate wapp_dstr_update(String **dst, const char *src, Arena *arena) {
if (!dst || !(*dst)) {
return;
return (StringUpdate){.updated = false, .str = *dst};
}
u64 length = strlen(src);
String *str = *dst;
if (length < str->capacity) {
memset(str->buf, 0, str->capacity);
str->size = length;
strncpy(str->buf, src, length + 1);
} else {
u64 capacity = length * CAPACITY_SCALAR;
String *tmp = (String *)wapp_mem_allocator_realloc(
&(str->allocator), *dst, sizeof(String) + capacity + 1);
if (!tmp) {
return;
if (length >= str->capacity) {
if (!arena) {
return (StringUpdate){.updated = false, .str = *dst};
}
tmp->capacity = capacity;
tmp->size = length;
strncpy(tmp->buf, src, length + 1);
String *new_str = wapp_dstr_from_string(src, arena);
if (!new_str) {
return (StringUpdate){.updated = false, .str = *dst};
}
*dst = tmp;
}
}
void wapp_dstr_free(String **str) {
if (!str || !(*str)) {
return;
return (StringUpdate){.updated = true, .str = new_str};
}
String *str_ptr = *str;
wapp_mem_allocator_free(&(str_ptr->allocator), (void **)str);
memset(str->buf, 0, str->capacity);
str->size = length;
strncpy(str->buf, src, length + 1);
return (StringUpdate){.updated = true, .str = *dst};
}
void wapp_dstr_concat(String **dst, const char *src) {
StringUpdate wapp_dstr_concat(String **dst, const char *src, Arena *arena) {
if (!dst || !(*dst)) {
return;
return (StringUpdate){.updated = false, .str = *dst};
}
u64 src_length = strlen(src);
if (src_length == 0) {
return;
return (StringUpdate){.updated = false, .str = *dst};
}
u64 new_length = (*dst)->size + src_length;
@@ -117,42 +103,7 @@ void wapp_dstr_concat(String **dst, const char *src) {
strncpy(str, (*dst)->buf, (*dst)->size);
strncat(str, src, new_length + 1 - (*dst)->size);
wapp_dstr_update(dst, str);
}
void wapp_dstr_append(String **dst, char c) {
if (!dst || !(*dst)) {
return;
}
u64 new_length = (*dst)->size + 1;
char str[new_length + 1];
memset(str, 0, new_length + 1);
strncpy(str, (*dst)->buf, (*dst)->size);
str[(*dst)->size] = c;
wapp_dstr_update(dst, str);
}
void wapp_dstr_resize(String **str) {
if (!str || !(*str)) {
return;
}
String *str_ptr = *str;
u64 capacity = (*str)->size;
String *tmp = (String *)wapp_mem_allocator_realloc(
&(str_ptr->allocator), *str, sizeof(String) + capacity + 1);
if (!tmp) {
return;
}
tmp->capacity = capacity;
*str = tmp;
return wapp_dstr_update(dst, str, arena);
}
void wapp_dstr_clear(String *str) {

View File

@@ -2,7 +2,8 @@
#define DSTR_H
#include "aliases.h"
#include "mem_allocator.h"
#include "mem_arena.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
@@ -10,13 +11,16 @@ extern "C" {
typedef struct dstr String;
String *wapp_dstr_with_capacity(u64 capacity, const Allocator *allocator);
String *wapp_dstr_from_string(const char *str, const Allocator *allocator);
void wapp_dstr_update(String **dst, const char *src);
void wapp_dstr_free(String **str);
void wapp_dstr_concat(String **dst, const char *src);
void wapp_dstr_append(String **dst, char c);
void wapp_dstr_resize(String **str);
typedef struct string_update StringUpdate;
struct string_update {
bool updated;
String *str;
};
String *wapp_dstr_with_capacity(u64 capacity, Arena *arena);
String *wapp_dstr_from_string(const char *str, Arena *arena);
StringUpdate wapp_dstr_update(String **dst, const char *src, Arena *arena);
StringUpdate wapp_dstr_concat(String **dst, const char *src, Arena *arena);
void wapp_dstr_clear(String *str);
void wapp_dstr_print(const String *str);
i64 wapp_dstr_find(const String *str, const char *substr);

View File

@@ -0,0 +1,48 @@
#ifndef TERM_COLOUR_H
#define TERM_COLOUR_H
#include "platform.h"
// clang-format off
#if defined(WAPP_PLATFORM_WINDOWS)
#define TERM_COLOUR_CLEAR ""
#define TERM_COLOUR_BOLD ""
#define TERM_COLOUR_FG_BLACK ""
#define TERM_COLOUR_FG_RED ""
#define TERM_COLOUR_FG_GREEN ""
#define TERM_COLOUR_FG_YELLOW ""
#define TERM_COLOUR_FG_BLUE ""
#define TERM_COLOUR_FG_MAGENTA ""
#define TERM_COLOUR_FG_CYAN ""
#define TERM_COLOUR_FG_WHITE ""
#define TERM_COLOUR_FG_BR_BLACK ""
#define TERM_COLOUR_FG_BR_RED ""
#define TERM_COLOUR_FG_BR_GREEN ""
#define TERM_COLOUR_FG_BR_YELLOW ""
#define TERM_COLOUR_FG_BR_BLUE ""
#define TERM_COLOUR_FG_BR_MAGENTA ""
#define TERM_COLOUR_FG_BR_CYAN ""
#define TERM_COLOUR_FG_BR_WHITE ""
#elif defined(WAPP_PLATFORM_POSIX)
#define TERM_COLOUR_CLEAR "\033[0m"
#define TERM_COLOUR_BOLD "\033[1m"
#define TERM_COLOUR_FG_BLACK "\033[30m"
#define TERM_COLOUR_FG_RED "\033[31m"
#define TERM_COLOUR_FG_GREEN "\033[32m"
#define TERM_COLOUR_FG_YELLOW "\033[33m"
#define TERM_COLOUR_FG_BLUE "\033[34m"
#define TERM_COLOUR_FG_MAGENTA "\033[35m"
#define TERM_COLOUR_FG_CYAN "\033[36m"
#define TERM_COLOUR_FG_WHITE "\033[37m"
#define TERM_COLOUR_FG_BR_BLACK "\033[90m"
#define TERM_COLOUR_FG_BR_RED "\033[91m"
#define TERM_COLOUR_FG_BR_GREEN "\033[92m"
#define TERM_COLOUR_FG_BR_YELLOW "\033[93m"
#define TERM_COLOUR_FG_BR_BLUE "\033[94m"
#define TERM_COLOUR_FG_BR_MAGENTA "\033[95m"
#define TERM_COLOUR_FG_BR_CYAN "\033[96m"
#define TERM_COLOUR_FG_BR_WHITE "\033[97m"
#endif
// clang-format on
#endif // !TERM_COLOUR_H

48
src/tester/tester.c Normal file
View File

@@ -0,0 +1,48 @@
#include "tester.h"
#include "aliases.h"
#include "termcolour/termcolour.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
internal void print_test_result(TestFuncResult result);
void run_tests(TestFunc *func1, ...) {
printf("\n");
print_test_result(func1());
va_list args;
va_start(args, func1);
TestFunc *func = NULL;
while ((func = va_arg(args, TestFunc *))) {
TestFuncResult result = func();
print_test_result(result);
if (!result.success) {
exit(EXIT_FAILURE);
}
}
va_end(args);
printf("\n");
}
internal void print_test_result(TestFuncResult result) {
const char *colour;
const char *result_text;
if (result.success) {
colour = TERM_COLOUR_FG_BR_GREEN;
result_text = "PASSED";
} else {
colour = TERM_COLOUR_FG_BR_RED;
result_text = "FAILED";
}
printf("[%s%s%s%s] %s\n", colour, TERM_COLOUR_BOLD, result_text,
TERM_COLOUR_CLEAR, result.name);
}

26
src/tester/tester.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef TESTER_H
#define TESTER_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#define TEST_RESULT(BOOL) ((TestFuncResult){.name = __func__, .success = BOOL})
typedef struct test_func_result TestFuncResult;
struct test_func_result {
const char *name;
bool success;
};
typedef TestFuncResult(TestFunc)(void);
void run_tests(TestFunc *func1, ...);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !TESTER_H

59
tests/arena/test_arena.c Normal file
View File

@@ -0,0 +1,59 @@
#include "test_arena.h"
#include "aliases.h"
#include "mem_arena.h"
#include "mem_utils.h"
#include "tester.h"
#include <stdbool.h>
#include <stdlib.h>
#define ARENA_CAPACITY 1024
internal Arena *arena = NULL;
internal u64 count = 20;
internal i32 *array = NULL;
TestFuncResult test_arena_init(void) {
bool result = wapp_mem_arena_init(&arena, ARENA_CAPACITY,
WAPP_MEM_ALLOC_RESERVE, false);
return TEST_RESULT(result);
}
TestFuncResult test_arena_alloc_succeeds_when_within_capacity(void) {
array = wapp_mem_arena_alloc(arena, count * sizeof(i32));
bool result = array != NULL;
for (u64 i = 0; i < count; ++i) {
array[i] = i * 10;
}
return TEST_RESULT(result);
}
TestFuncResult test_arena_alloc_fails_when_over_capacity(void) {
u8 *bytes = wapp_mem_arena_alloc(arena, ARENA_CAPACITY * 2);
bool result = bytes == NULL;
return TEST_RESULT(result);
}
TestFuncResult test_arena_clear(void) {
wapp_mem_arena_clear(arena);
bool result = true;
for (u64 i = 0; i < count; ++i) {
if (array[i] != 0) {
result = false;
break;
}
}
return TEST_RESULT(result);
}
TestFuncResult test_arena_destroy(void) {
wapp_mem_arena_destroy(&arena);
bool result = arena == NULL;
return TEST_RESULT(result);
}

20
tests/arena/test_arena.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef TEST_ARENA_H
#define TEST_ARENA_H
#include "tester.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
TestFuncResult test_arena_init(void);
TestFuncResult test_arena_alloc_succeeds_when_within_capacity(void);
TestFuncResult test_arena_alloc_fails_when_over_capacity(void);
TestFuncResult test_arena_clear(void);
TestFuncResult test_arena_destroy(void);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !TEST_ARENA_H

11
tests/wapptest.c Normal file
View File

@@ -0,0 +1,11 @@
#include "test_arena.h"
#include "tester.h"
#include <stdlib.h>
int main(void) {
run_tests(test_arena_init, test_arena_alloc_succeeds_when_within_capacity,
test_arena_alloc_fails_when_over_capacity, test_arena_clear,
test_arena_destroy);
return EXIT_SUCCESS;
}