16 Commits

13 changed files with 496 additions and 164 deletions

3
.gitignore vendored
View File

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

63
compile
View File

@@ -1,13 +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="\
$(find src -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="\
$(find src -type f -name *.c | xargs -I{} echo -n "{} ") \
$(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

@@ -13,30 +13,16 @@
#define ARENA_MINIMUM_CAPACITY 1024
typedef struct base_arena BaseArena;
struct base_arena {
struct arena {
u8 *buf;
u8 *offset;
u64 capacity;
BaseArena *prev;
BaseArena *next;
};
struct growing_arena {
BaseArena *active_arena;
u64 count;
u64 initial_capacity;
};
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_destroy(BaseArena *arena);
// PUBLIC API
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity) {
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity, MemAllocFlags flags,
bool zero_buffer) {
if (!arena || *arena || base_capacity == 0) {
return false;
}
@@ -47,19 +33,21 @@ bool wapp_mem_arena_init(Arena **arena, u64 base_capacity) {
return false;
}
arena_ptr->active_arena = (BaseArena *)calloc(1, sizeof(BaseArena));
if (!(arena_ptr->active_arena)) {
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;
}
if (!base_arena_init(arena_ptr->active_arena, base_capacity)) {
wapp_mem_arena_destroy(arena);
return false;
}
arena_ptr->count = 1;
arena_ptr->initial_capacity = base_capacity;
arena_ptr->capacity = arena_capacity;
arena_ptr->offset = arena_ptr->buf;
return true;
}
@@ -69,130 +57,6 @@ void *wapp_mem_arena_alloc(Arena *arena, u64 size) {
}
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_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_destroy(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_destroy(current);
free(current);
current = next;
}
current = arena_ptr->active_arena->prev;
while (current) {
prev = current->prev;
base_arena_destroy(current);
free(current);
current = prev;
}
base_arena_destroy(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 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;
}
@@ -206,10 +70,12 @@ internal void *base_arena_alloc_aligned(BaseArena *arena, u64 size,
arena->offset = output + size;
memset(output, 0, size);
return (void *)output;
}
internal void base_arena_clear(BaseArena *arena) {
void wapp_mem_arena_clear(Arena *arena) {
if (!arena) {
return;
}
@@ -218,16 +84,19 @@ internal void base_arena_clear(BaseArena *arena) {
arena->offset = arena->buf;
}
internal void base_arena_destroy(BaseArena *arena) {
void wapp_mem_arena_destroy(Arena **arena) {
if (!arena) {
return;
}
if (arena->buf) {
free(arena->buf);
Arena *arena_ptr = *arena;
if (arena_ptr->buf) {
wapp_mem_util_free(arena_ptr->buf, arena_ptr->capacity);
}
arena->buf = arena->offset = NULL;
arena->capacity = 0;
arena->prev = arena->next = NULL;
arena_ptr->buf = arena_ptr->offset = NULL;
arena_ptr->capacity = 0;
free(*arena);
*arena = NULL;
}

View File

@@ -2,15 +2,17 @@
#define MEM_ARENA_H
#include "aliases.h"
#include "mem_utils.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct growing_arena Arena;
typedef struct arena Arena;
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity);
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);

View File

@@ -1,8 +1,43 @@
#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; }
@@ -26,3 +61,54 @@ void *wapp_mem_util_align_forward(void *ptr, u64 alignment) {
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

View File

@@ -2,12 +2,56 @@
#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
}

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,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;
}