From 5118224c53c25204e8da9e2a1be62ad00da1c5da Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Mon, 25 May 2026 22:51:07 +0100 Subject: [PATCH] Add wapp locally --- wapp/base/array/array.c | 266 ++++++++++ wapp/base/array/array.h | 216 ++++++++ wapp/base/dbl_list/dbl_list.c | 259 ++++++++++ wapp/base/dbl_list/dbl_list.h | 184 +++++++ wapp/base/mem/allocator/mem_allocator.c | 35 ++ wapp/base/mem/allocator/mem_allocator.h | 50 ++ wapp/base/mem/utils/mem_utils.c | 25 + wapp/base/mem/utils/mem_utils.h | 19 + wapp/base/queue/queue.c | 108 ++++ wapp/base/queue/queue.h | 100 ++++ wapp/base/strings/str8/str8.c | 480 ++++++++++++++++++ wapp/base/strings/str8/str8.h | 130 +++++ wapp/base/wapp_base.c | 14 + wapp/base/wapp_base.h | 14 + wapp/common/aliases/aliases.h | 67 +++ wapp/common/assert/assert.h | 61 +++ wapp/common/misc/misc_utils.h | 63 +++ wapp/common/platform/platform.h | 114 +++++ wapp/common/wapp_common.h | 11 + wapp/log/log.c | 102 ++++ wapp/log/log.h | 34 ++ wapp/log/wapp_log.c | 10 + wapp/log/wapp_log.h | 11 + wapp/os/allocators/arena/mem_arena.c | 192 +++++++ wapp/os/allocators/arena/mem_arena.h | 45 ++ .../os/allocators/arena/mem_arena_allocator.c | 87 ++++ .../os/allocators/arena/mem_arena_allocator.h | 46 ++ wapp/os/cpath/cpath.c | 136 +++++ wapp/os/cpath/cpath.h | 45 ++ wapp/os/file/file.c | 129 +++++ wapp/os/file/file.h | 75 +++ wapp/os/file/posix/file_posix.c | 134 +++++ wapp/os/file/posix/file_posix.h | 27 + wapp/os/file/win/file_win.c | 177 +++++++ wapp/os/file/win/file_win.h | 31 ++ wapp/os/mem/mem_os.c | 30 ++ wapp/os/mem/mem_os.h | 33 ++ wapp/os/mem/mem_os_ops.h | 30 ++ wapp/os/mem/posix/mem_os_posix.c | 36 ++ wapp/os/mem/posix/mem_os_posix.h | 35 ++ wapp/os/mem/win/mem_os_win.c | 37 ++ wapp/os/mem/win/mem_os_win.h | 29 ++ wapp/os/shell/commander/commander.c | 101 ++++ wapp/os/shell/commander/commander.h | 29 ++ wapp/os/shell/commander/commander_output.h | 42 ++ .../shell/commander/posix/commander_posix.c | 25 + wapp/os/shell/commander/win/commander_win.c | 24 + .../shell/termcolour/posix/termcolour_posix.c | 36 ++ wapp/os/shell/termcolour/termcolour.c | 18 + wapp/os/shell/termcolour/termcolour.h | 26 + wapp/os/shell/termcolour/terminal_colours.h | 39 ++ wapp/os/shell/termcolour/win/termcolour_win.c | 73 +++ wapp/os/shell/utils/shell_utils.h | 26 + wapp/os/wapp_os.c | 24 + wapp/os/wapp_os.h | 24 + wapp/prng/wapp_prng.c | 8 + wapp/prng/wapp_prng.h | 9 + wapp/prng/xorshift/xorshift.c | 135 +++++ wapp/prng/xorshift/xorshift.h | 30 ++ wapp/testing/tester/tester.c | 55 ++ wapp/testing/tester/tester.h | 36 ++ wapp/testing/wapp_testing.c | 10 + wapp/testing/wapp_testing.h | 10 + wapp/uuid/uuid.c | 58 +++ wapp/uuid/uuid.h | 50 ++ wapp/uuid/wapp_uuid.c | 10 + wapp/uuid/wapp_uuid.h | 11 + wapp/wapp.c | 14 + wapp/wapp.h | 14 + 69 files changed, 4664 insertions(+) create mode 100644 wapp/base/array/array.c create mode 100644 wapp/base/array/array.h create mode 100644 wapp/base/dbl_list/dbl_list.c create mode 100644 wapp/base/dbl_list/dbl_list.h create mode 100644 wapp/base/mem/allocator/mem_allocator.c create mode 100644 wapp/base/mem/allocator/mem_allocator.h create mode 100644 wapp/base/mem/utils/mem_utils.c create mode 100644 wapp/base/mem/utils/mem_utils.h create mode 100644 wapp/base/queue/queue.c create mode 100644 wapp/base/queue/queue.h create mode 100644 wapp/base/strings/str8/str8.c create mode 100644 wapp/base/strings/str8/str8.h create mode 100644 wapp/base/wapp_base.c create mode 100644 wapp/base/wapp_base.h create mode 100644 wapp/common/aliases/aliases.h create mode 100644 wapp/common/assert/assert.h create mode 100644 wapp/common/misc/misc_utils.h create mode 100644 wapp/common/platform/platform.h create mode 100644 wapp/common/wapp_common.h create mode 100644 wapp/log/log.c create mode 100644 wapp/log/log.h create mode 100644 wapp/log/wapp_log.c create mode 100644 wapp/log/wapp_log.h create mode 100644 wapp/os/allocators/arena/mem_arena.c create mode 100644 wapp/os/allocators/arena/mem_arena.h create mode 100644 wapp/os/allocators/arena/mem_arena_allocator.c create mode 100644 wapp/os/allocators/arena/mem_arena_allocator.h create mode 100644 wapp/os/cpath/cpath.c create mode 100644 wapp/os/cpath/cpath.h create mode 100644 wapp/os/file/file.c create mode 100644 wapp/os/file/file.h create mode 100644 wapp/os/file/posix/file_posix.c create mode 100644 wapp/os/file/posix/file_posix.h create mode 100644 wapp/os/file/win/file_win.c create mode 100644 wapp/os/file/win/file_win.h create mode 100644 wapp/os/mem/mem_os.c create mode 100644 wapp/os/mem/mem_os.h create mode 100644 wapp/os/mem/mem_os_ops.h create mode 100644 wapp/os/mem/posix/mem_os_posix.c create mode 100644 wapp/os/mem/posix/mem_os_posix.h create mode 100644 wapp/os/mem/win/mem_os_win.c create mode 100644 wapp/os/mem/win/mem_os_win.h create mode 100644 wapp/os/shell/commander/commander.c create mode 100644 wapp/os/shell/commander/commander.h create mode 100644 wapp/os/shell/commander/commander_output.h create mode 100644 wapp/os/shell/commander/posix/commander_posix.c create mode 100644 wapp/os/shell/commander/win/commander_win.c create mode 100644 wapp/os/shell/termcolour/posix/termcolour_posix.c create mode 100644 wapp/os/shell/termcolour/termcolour.c create mode 100644 wapp/os/shell/termcolour/termcolour.h create mode 100644 wapp/os/shell/termcolour/terminal_colours.h create mode 100644 wapp/os/shell/termcolour/win/termcolour_win.c create mode 100644 wapp/os/shell/utils/shell_utils.h create mode 100644 wapp/os/wapp_os.c create mode 100644 wapp/os/wapp_os.h create mode 100644 wapp/prng/wapp_prng.c create mode 100644 wapp/prng/wapp_prng.h create mode 100644 wapp/prng/xorshift/xorshift.c create mode 100644 wapp/prng/xorshift/xorshift.h create mode 100644 wapp/testing/tester/tester.c create mode 100644 wapp/testing/tester/tester.h create mode 100644 wapp/testing/wapp_testing.c create mode 100644 wapp/testing/wapp_testing.h create mode 100644 wapp/uuid/uuid.c create mode 100644 wapp/uuid/uuid.h create mode 100644 wapp/uuid/wapp_uuid.c create mode 100644 wapp/uuid/wapp_uuid.h create mode 100644 wapp/wapp.c create mode 100644 wapp/wapp.h diff --git a/wapp/base/array/array.c b/wapp/base/array/array.c new file mode 100644 index 0000000..e47c125 --- /dev/null +++ b/wapp/base/array/array.c @@ -0,0 +1,266 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "./array.h" +#include "../../common/assert/assert.h" +#include "../mem/allocator/mem_allocator.h" +#include "../../common/misc/misc_utils.h" +#include "../../common/aliases/aliases.h" +#include + +#define _array_header(ARRAY) (ArrayHeader *)(wapp_pointer_offset(ARRAY, (i64)sizeof(ArrayHeader) * -1)) + +wapp_persist inline void _array_validate(const GenericArray array, u64 item_size); + +u64 _array_count(GenericArray array) { + wapp_debug_assert(array != NULL, "`array` should not be NULL"); + + ArrayHeader *header = _array_header(array); + wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); + + return header->count; +} + +u64 _array_capacity(GenericArray array) { + wapp_debug_assert(array != NULL, "`array` should not be NULL"); + + ArrayHeader *header = _array_header(array); + wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); + + return header->capacity; +} + +u64 _array_item_size(GenericArray array) { + wapp_debug_assert(array != NULL, "`array` should not be NULL"); + + ArrayHeader *header = _array_header(array); + wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); + + return header->item_size; +} + +void _array_set_count(GenericArray array, u64 count) { + wapp_debug_assert(array != NULL, "`array` should not be NULL"); + + ArrayHeader *header = _array_header(array); + wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); + + header->count = count; +} + +void *_array_get(GenericArray array, u64 index, u64 item_size) { + wapp_runtime_assert(array != NULL, "`array` should not be NULL"); + _array_validate(array, item_size); + + ArrayHeader *header = _array_header(array); + wapp_runtime_assert(index < header->count, "`index` is out of bounds"); + + return wapp_pointer_offset(array, header->item_size * index); +} + +void _array_set(GenericArray array, u64 index, void *value, u64 item_size) { + void *item = _array_get(array, index, item_size); + + ArrayHeader *header = _array_header(array); + memcpy(item, value, header->item_size); +} + +void _array_append_capped(GenericArray array, void *value, u64 item_size) { + wapp_runtime_assert(array != NULL, "`array` should not be NULL"); + _array_validate(array, item_size); + + ArrayHeader *header = _array_header(array); + if (header->count >= header->capacity) { return; } + + u64 index = (header->count)++; + _array_set(array, index, value, item_size); +} + +void _array_extend_capped(GenericArray dst, const GenericArray src, u64 item_size) { + wapp_runtime_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); + _array_validate(dst, item_size); + _array_validate(src, item_size); + + ArrayHeader *src_header = _array_header(src); + ArrayHeader *dst_header = _array_header(dst); + u64 remaining_capacity = dst_header->capacity - dst_header->count; + + u64 copy_count = src_header->count < remaining_capacity ? src_header->count : remaining_capacity; + void *dst_ptr = wapp_pointer_offset(dst, dst_header->count * dst_header->item_size); + memcpy(dst_ptr, src, copy_count * src_header->item_size); + dst_header->count += copy_count; +} + +void _array_copy_capped(GenericArray dst, const GenericArray src, u64 item_size) { + wapp_runtime_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); + _array_validate(dst, item_size); + _array_validate(src, item_size); + + _array_clear(dst, item_size); + + ArrayHeader *src_header = _array_header(src); + ArrayHeader *dst_header = _array_header(dst); + u64 copy_count = src_header->count < dst_header->capacity ? src_header->count : dst_header->capacity; + memcpy((void *)dst, (void *)src, copy_count * src_header->item_size); + dst_header->count = copy_count; +} + +GenericArray _array_append_alloc(const Allocator *allocator, GenericArray array, void *value, + ArrayInitFlags flags, u64 item_size) { + wapp_runtime_assert(allocator != NULL && array != NULL, "`allocator` and `array` should not be NULL"); + _array_validate(array, item_size); + + GenericArray output = array; + + ArrayHeader *header = _array_header(array); + if (header->count >= header->capacity) { + u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(header->capacity * 2); + output = (GenericArray )_array_alloc_capacity(allocator, new_capacity, flags, + header->item_size); + if (!output) { + output = array; + goto RETURN_ARRAY_APPEND_ALLOC; + } + _array_copy_capped(output, array, item_size); + } + + _array_append_capped(output, value, item_size); + + if ((flags & ARRAY_INIT_FILLED) == ARRAY_INIT_FILLED) { + _array_set_count(output, _array_capacity(output)); + } + +RETURN_ARRAY_APPEND_ALLOC: + return output; +} + +GenericArray _array_extend_alloc(const Allocator *allocator, GenericArray dst, const GenericArray src, + ArrayInitFlags flags, u64 item_size) { + wapp_runtime_assert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL"); + _array_validate(dst, item_size); + _array_validate(src, item_size); + + GenericArray output = dst; + + ArrayHeader *src_header = _array_header(src); + ArrayHeader *dst_header = _array_header(dst); + u64 remaining_capacity = dst_header->capacity - dst_header->count; + if (src_header->count >= remaining_capacity) { + u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(dst_header->capacity * 2); + output = (GenericArray )_array_alloc_capacity(allocator, new_capacity, + flags, dst_header->item_size); + if (!output) { + output = dst; + goto RETURN_ARRAY_EXTEND_ALLOC; + } + _array_copy_capped(output, dst, item_size); + } + + _array_extend_capped(output, src, item_size); + + if ((flags & ARRAY_INIT_FILLED) == ARRAY_INIT_FILLED) { + _array_set_count(output, _array_capacity(output)); + } + +RETURN_ARRAY_EXTEND_ALLOC: + return output; +} + +GenericArray _array_copy_alloc(const Allocator *allocator, GenericArray dst, const GenericArray src, + ArrayInitFlags flags, u64 item_size) { + wapp_runtime_assert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL"); + _array_validate(dst, item_size); + _array_validate(src, item_size); + + GenericArray output = dst; + + ArrayHeader *src_header = _array_header(src); + ArrayHeader *dst_header = _array_header(dst); + if (src_header->count >= dst_header->capacity) { + u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(dst_header->capacity * 2); + output = (GenericArray )_array_alloc_capacity(allocator, new_capacity, + flags, src_header->item_size); + if (!output) { + output = dst; + goto RETURN_ARRAY_COPY_ALLOC; + } + } + + _array_copy_capped(output, src, item_size); + + if ((flags & ARRAY_INIT_FILLED) == ARRAY_INIT_FILLED) { + _array_set_count(output, _array_capacity(output)); + } + +RETURN_ARRAY_COPY_ALLOC: + return output; +} + +void *_array_pop(GenericArray array, u64 item_size) { + wapp_runtime_assert(array != NULL, "`array` should not be NULL"); + _array_validate(array, item_size); + + ArrayHeader *header = _array_header(array); + if (header->count == 0) { return NULL; } + + u64 index = header->count - 1; + void *out = _array_get(array, index, item_size); + --(header->count); + return out; +} + +void _array_clear(GenericArray array, u64 item_size) { + wapp_runtime_assert(array != NULL, "`array` should not be NULL"); + _array_validate(array, item_size); + + ArrayHeader *header = _array_header(array); + header->count = 0; +} + +u64 _array_calc_alloc_size(u64 capacity, u64 item_size) { + return sizeof(ArrayHeader) + item_size * capacity; +} + +GenericArray _array_alloc_capacity(const Allocator *allocator, u64 capacity, ArrayInitFlags flags, + u64 item_size) { + wapp_runtime_assert(allocator != NULL, "`allocator` should not be NULL"); + + GenericArray output = NULL; + + u64 allocation_size = _array_calc_alloc_size(capacity, item_size); + void *buffer = wapp_mem_allocator_alloc(allocator, allocation_size); + if (!buffer) { + goto RETURN_ARRAY_ALLOC; + } + + output = _array_from_preallocated_buffer(buffer, allocation_size, flags, item_size); + +RETURN_ARRAY_ALLOC: + return output; +} + + +GenericArray _array_from_preallocated_buffer(void *buffer, u64 buffer_size, ArrayInitFlags flags, + u64 item_size) { + wapp_runtime_assert(buffer != NULL, "`buffer` should not be NULL"); + + i64 data_buffer_size = (i64)buffer_size - (i64)(sizeof(ArrayHeader)); + if (data_buffer_size <= 0) { + return NULL; + } + + u64 item_capacity = (u64)data_buffer_size / item_size; + ArrayHeader *header = (ArrayHeader *)buffer; + GenericArray output = (u8 *)(header + 1); + header->magic = WAPP_ARRAY_MAGIC; + header->count = flags & ARRAY_INIT_FILLED ? item_capacity : 0; + header->capacity = item_capacity; + header->item_size = item_size; + + return output; +} + +wapp_persist inline void _array_validate(const GenericArray array, u64 item_size) { + ArrayHeader *header = _array_header(array); + wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); + wapp_runtime_assert(item_size == header->item_size, "Invalid item type provided"); +} diff --git a/wapp/base/array/array.h b/wapp/base/array/array.h new file mode 100644 index 0000000..f996a23 --- /dev/null +++ b/wapp/base/array/array.h @@ -0,0 +1,216 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef ARRAY_H +#define ARRAY_H + +#include "../mem/allocator/mem_allocator.h" +#include "../../common/misc/misc_utils.h" +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#define WAPP_ARRAY_MAGIC (u64)0x57415f415252 + +#define _calc_array_count(TYPE, ...) wapp_misc_utils_va_args_count(TYPE, __VA_ARGS__) +#define _calc_array_capacity(TYPE, ...) wapp_misc_utils_u64_round_up_pow2(_calc_array_count(TYPE, __VA_ARGS__) * 2) + +typedef struct Str8 Str8; + +// NOTE (Abdelrahman): Typedefs to distinguish arrays from regular pointers +typedef void *GenericArray; +typedef void **VoidPtrArray; +typedef c8 *C8Array; +typedef c16 *C16Array; +typedef c32 *C32Array; +typedef u8 *U8Array; +typedef u16 *U16Array; +typedef u32 *U32Array; +typedef u64 *U64Array; +typedef b8 *B8Array; +typedef i8 *I8Array; +typedef i16 *I16Array; +typedef i32 *I32Array; +typedef i64 *I64Array; +typedef f32 *F32Array; +typedef f64 *F64Array; +typedef f128 *F128Array; +typedef uptr *UptrArray; +typedef iptr *IptrArray; +typedef Str8 *Str8Array; + +typedef enum { + ARRAY_INIT_NONE = 0, + ARRAY_INIT_FILLED = 1 << 1, +} ArrayInitFlags; + +#ifdef WAPP_PLATFORM_CPP +#define wapp_array(TYPE, ...) ([&]() { \ + u64 capacity = _calc_array_capacity(TYPE, __VA_ARGS__); \ + \ + TYPE items[_calc_array_capacity(TYPE, __VA_ARGS__)] = {__VA_ARGS__}; \ + \ + wapp_persist u8 array[ \ + sizeof(ArrayHeader) + _calc_array_capacity(TYPE, __VA_ARGS__) * sizeof(TYPE) \ + ] = {0}; \ + ArrayHeader *header = (ArrayHeader *)array; \ + header->magic = WAPP_ARRAY_MAGIC; \ + header->count = _calc_array_count(TYPE, __VA_ARGS__); \ + header->capacity = _calc_array_capacity(TYPE, __VA_ARGS__); \ + header->item_size = sizeof(TYPE); \ + \ + u8 *buf = (u8 *)(header + 1); \ + memcpy(buf, items, capacity * sizeof(TYPE)); \ + return (TYPE *)buf; \ +}()) +#define wapp_array_with_capacity(TYPE, CAPACITY, FLAGS) ([&]() { \ + wapp_persist u8 array[ \ + sizeof(ArrayHeader) + CAPACITY * sizeof(TYPE) \ + ] = {0}; \ + ArrayHeader *header = (ArrayHeader *)array; \ + header->magic = WAPP_ARRAY_MAGIC; \ + header->count = (FLAGS & ARRAY_INIT_FILLED) ? CAPACITY : 0; \ + header->capacity = CAPACITY; \ + header->item_size = sizeof(TYPE); \ + \ + return (TYPE *)(header + 1); \ +}()) +#define wapp_array_pop(TYPE, ARRAY) ([&]() { \ + if (ARRAY == NULL || _array_count((GenericArray)ARRAY) == 0) { \ + TYPE result{}; \ + return result; \ + } \ + \ + return *((TYPE *)_array_pop((GenericArray)ARRAY, sizeof(TYPE))); \ +}()) +#else +#define _stack_array(TYPE, SIZE) struct {ArrayHeader header; \ + TYPE items[SIZE]; \ + wapp_misc_utils_reserve_padding(sizeof(ArrayHeader) + \ + sizeof(TYPE) * SIZE);} +#define wapp_array(TYPE, ...) \ + ((TYPE *)( \ + (_stack_array(TYPE, _calc_array_capacity(TYPE, __VA_ARGS__))){ \ + .header = { \ + .magic = WAPP_ARRAY_MAGIC, \ + .count = _calc_array_count(TYPE, __VA_ARGS__), \ + .capacity = _calc_array_capacity(TYPE, __VA_ARGS__), \ + .item_size = sizeof(TYPE), \ + }, \ + .items = {__VA_ARGS__}, \ + }.items \ + )) +#define wapp_array_with_capacity(TYPE, CAPACITY, FLAGS) \ + ((TYPE *)( \ + (_stack_array(TYPE, CAPACITY)){ \ + .header = { \ + .magic = WAPP_ARRAY_MAGIC, \ + .count = (FLAGS & ARRAY_INIT_FILLED) ? CAPACITY : 0, \ + .capacity = CAPACITY, \ + .item_size = sizeof(TYPE), \ + }, \ + .items = {0}, \ + }.items \ + )) +#define wapp_array_pop(TYPE, ARRAY) \ + (ARRAY == NULL || _array_count((GenericArray)ARRAY) == 0 ? \ + (TYPE){0} : \ + *((TYPE *)_array_pop((GenericArray)ARRAY, sizeof(TYPE))) \ + ) +#endif // !WAPP_PLATFORM_CPP + +#define wapp_array_count(ARRAY) \ + (_array_count((GenericArray)ARRAY)) +#define wapp_array_capacity(ARRAY) \ + (_array_capacity((GenericArray)ARRAY)) +#define wapp_array_item_size(ARRAY) \ + (_array_item_size((GenericArray)ARRAY)) +#define wapp_array_set_count(ARRAY, COUNT) \ + (_array_set_count((GenericArray)ARRAY, COUNT)) +#define wapp_array_get(TYPE, ARRAY, INDEX) \ + ((TYPE *)_array_get((GenericArray)ARRAY, \ + INDEX, \ + sizeof(TYPE))) +#define wapp_array_set(TYPE, ARRAY, INDEX, VALUE_PTR) \ + (_array_set((GenericArray)ARRAY, \ + INDEX, \ + (u8 *)VALUE_PTR, \ + sizeof(TYPE))) +#define wapp_array_append_capped(TYPE, ARRAY, VALUE_PTR) \ + (_array_append_capped((GenericArray)ARRAY, \ + (u8 *)VALUE_PTR, \ + sizeof(TYPE))) +#define wapp_array_extend_capped(TYPE, DST_ARRAY, SRC_ARRAY) \ + (_array_extend_capped((GenericArray)DST_ARRAY, \ + (GenericArray)SRC_ARRAY, \ + sizeof(TYPE))) +#define wapp_array_copy_capped(TYPE, DST_ARRAY, SRC_ARRAY) \ + (_array_copy_capped((GenericArray)DST_ARRAY, \ + (GenericArray)SRC_ARRAY, \ + sizeof(TYPE))) +#define wapp_array_append_alloc(TYPE, ALLOCATOR_PTR, ARRAY, VALUE_PTR, FLAGS) \ + ((TYPE *)_array_append_alloc(ALLOCATOR_PTR, \ + (GenericArray)ARRAY, \ + (u8 *)VALUE_PTR, \ + FLAGS, \ + sizeof(TYPE))) +#define wapp_array_extend_alloc(TYPE, ALLOCATOR_PTR, DST_ARRAY, SRC_ARRAY, FLAGS) \ + ((TYPE *)_array_extend_alloc(ALLOCATOR_PTR, \ + (GenericArray)DST_ARRAY, \ + (GenericArray)SRC_ARRAY, \ + FLAGS, \ + sizeof(TYPE))) +#define wapp_array_copy_alloc(TYPE, ALLOCATOR_PTR, DST_ARRAY, SRC_ARRAY, FLAGS) \ + ((TYPE *)_array_copy_alloc(ALLOCATOR_PTR, \ + (GenericArray)DST_ARRAY, \ + (GenericArray)SRC_ARRAY, \ + FLAGS, \ + sizeof(TYPE))) +#define wapp_array_clear(TYPE, ARRAY) \ + (_array_clear((GenericArray)ARRAY, \ + sizeof(TYPE))) +#define wapp_array_calc_alloc_size(TYPE, CAPACITY) _array_calc_alloc_size(CAPACITY, sizeof(TYPE)) +#define wapp_array_alloc_capacity(TYPE, ALLOCATOR_PTR, CAPACITY, FLAGS) \ + ((TYPE *)_array_alloc_capacity(ALLOCATOR_PTR, CAPACITY, FLAGS, sizeof(TYPE))) +#define wapp_array_from_preallcated_buffer(TYPE, BUFFER, BUFFER_SIZE) \ + ((TYPE *)_array_from_preallcated_buffer(BUFFER, BUFFER_SIZE, sizeof(TYPE))) + + +typedef struct header ArrayHeader; +struct header { + u64 magic; + u64 count; + u64 capacity; + u64 item_size; +}; + +u64 _array_count(GenericArray array); +u64 _array_capacity(GenericArray array); +u64 _array_item_size(GenericArray array); +void _array_set_count(GenericArray array, u64 count); +void *_array_get(GenericArray array, u64 index, u64 item_size); +void _array_set(GenericArray array, u64 index, void *value, u64 item_size); +void _array_append_capped(GenericArray array, void *value, u64 item_size); +void _array_extend_capped(GenericArray dst, const GenericArray src, u64 item_size); +void _array_copy_capped(GenericArray dst, const GenericArray src, u64 item_size); +GenericArray _array_append_alloc(const Allocator *allocator, GenericArray array, void *value, + ArrayInitFlags flags, u64 item_size); +GenericArray _array_extend_alloc(const Allocator *allocator, GenericArray dst, const GenericArray src, + ArrayInitFlags flags, u64 item_size); +GenericArray _array_copy_alloc(const Allocator *allocator, GenericArray dst, const GenericArray src, + ArrayInitFlags flags, u64 item_size); +void *_array_pop(GenericArray array, u64 item_size); +void _array_clear(GenericArray array, u64 item_size); +u64 _array_calc_alloc_size(u64 capacity, u64 item_size); +GenericArray _array_alloc_capacity(const Allocator *allocator, u64 capacity, ArrayInitFlags flags, + u64 item_size); +GenericArray _array_from_preallocated_buffer(void *buffer, u64 buffer_size, ArrayInitFlags flags, + u64 item_size); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !ARRAY_H diff --git a/wapp/base/dbl_list/dbl_list.c b/wapp/base/dbl_list/dbl_list.c new file mode 100644 index 0000000..8937738 --- /dev/null +++ b/wapp/base/dbl_list/dbl_list.c @@ -0,0 +1,259 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "./dbl_list.h" +#include "../mem/allocator/mem_allocator.h" +#include "../../common/assert/assert.h" +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" +#include + +wapp_intern GenericList _node_to_list(GenericNode *node, u64 item_size); +wapp_intern inline void _dbl_list_validate(const GenericList *list, u64 item_size); +wapp_intern inline void _dbl_list_node_validate(const GenericList *list, const GenericNode *node, u64 item_size); + +GenericList *_dbl_list_alloc(const Allocator *allocator, u64 item_size) { + wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL"); + + GenericList *list = wapp_mem_allocator_alloc(allocator, sizeof(GenericList)); + if (!list) { goto DBL_LIST_ALLOC_RETURN; } + + memset((void *)list, 0, sizeof(GenericList)); + list->magic = WAPP_DBL_LIST_MAGIC; + list->item_size = item_size; + +DBL_LIST_ALLOC_RETURN: + return list; +} + +GenericNode *_dbl_list_node_alloc(const Allocator *allocator, void *item, u64 item_size) { + wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL"); + + GenericNode *node = wapp_mem_allocator_alloc(allocator, sizeof(GenericNode)); + if (!node) { goto DBL_LIST_NODE_ALLOC_RETURN; } + + memset((void *)node, 0, sizeof(GenericNode)); + node->item = item; + node->header.magic = WAPP_DBL_NODE_MAGIC; + node->header.item_size = item_size; + +DBL_LIST_NODE_ALLOC_RETURN: + return node; +} + +GenericNode *_dbl_list_get(const GenericList *list, u64 index, u64 item_size) { + wapp_debug_assert(list != NULL, "`list` should not be NULL"); + _dbl_list_validate(list, item_size); + wapp_runtime_assert(index < list->node_count, "`index` is out of bounds"); + + GenericNode *output = NULL; + GenericNode *current = list->first; + for (u64 i = 1; i <= index; ++i) { + current = current->header.next; + } + + output = current; + + return output; +} + +void _dbl_list_push_front(GenericList *list, GenericNode *node, u64 item_size) { + wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL"); + _dbl_list_validate(list, item_size); + _dbl_list_node_validate(list, node, item_size); + + GenericList node_list = _node_to_list(node, item_size); + + if (list->node_count == 0) { + *list = node_list; + return; + } + + list->node_count += node_list.node_count; + + GenericNode *first = list->first; + if (first) { + first->header.prev = node_list.last; + } + + list->first = node_list.first; + node_list.last->header.next = first; +} + +void _dbl_list_push_back(GenericList *list, GenericNode *node, u64 item_size) { + wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL"); + _dbl_list_validate(list, item_size); + _dbl_list_node_validate(list, node, item_size); + + GenericList node_list = _node_to_list(node, item_size); + + if (list->node_count == 0) { + *list = node_list; + return; + } + + list->node_count += node_list.node_count; + + GenericNode *last = list->last; + if (last) { + last->header.next = node_list.first; + } + + list->last = node_list.last; + node_list.first->header.prev = last; +} + +void _dbl_list_insert(GenericList *list, GenericNode *node, u64 index, u64 item_size) { + wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL"); + _dbl_list_validate(list, item_size); + _dbl_list_node_validate(list, node, item_size); + + if (index == 0) { + _dbl_list_push_front(list, node, item_size); + return; + } else if (index == list->node_count) { + _dbl_list_push_back(list, node, item_size); + return; + } + + GenericNode *dst_node = _dbl_list_get(list, index, item_size); + if (!dst_node) { + return; + } + + GenericList node_list = _node_to_list(node, item_size); + + list->node_count += node_list.node_count; + + GenericNode *prev = dst_node->header.prev; + + dst_node->header.prev = node_list.last; + prev->header.next = node_list.first; + + node_list.first->header.prev = prev; + node_list.last->header.next = dst_node; +} + +GenericNode *_dbl_list_pop_front(GenericList *list, u64 item_size) { + wapp_debug_assert(list != NULL, "`list` should not be NULL"); + _dbl_list_validate(list, item_size); + + GenericNode *output = NULL; + + if (list->node_count == 0) { + goto RETURN_LIST_POP_FRONT; + } + + output = list->first; + + if (list->node_count == 1) { + *list = (GenericList){.magic = WAPP_DBL_LIST_MAGIC, .item_size = item_size}; + goto RETURN_LIST_POP_FRONT; + } + + --(list->node_count); + list->first = output->header.next; + + output->header.prev = output->header.next = NULL; + +RETURN_LIST_POP_FRONT: + return output; +} + +GenericNode *_dbl_list_pop_back(GenericList *list, u64 item_size) { + wapp_debug_assert(list != NULL, "`list` should not be NULL"); + _dbl_list_validate(list, item_size); + + GenericNode *output = NULL; + + if (list->node_count == 0) { + goto RETURN_LIST_POP_BACK; + } + + output = list->last; + + if (list->node_count == 1) { + *list = (GenericList){.magic = WAPP_DBL_LIST_MAGIC, .item_size = item_size}; + goto RETURN_LIST_POP_BACK; + } + + --(list->node_count); + list->last = output->header.prev; + + output->header.prev = output->header.next = NULL; + +RETURN_LIST_POP_BACK: + return output; +} + +GenericNode *_dbl_list_remove(GenericList *list, u64 index, u64 item_size) { + wapp_debug_assert(list != NULL, "`list` should not be NULL"); + _dbl_list_validate(list, item_size); + + GenericNode *output = NULL; + + if (index == 0) { + output = _dbl_list_pop_front(list, item_size); + goto RETURN_LIST_REMOVE; + } else if (index == list->node_count) { + output = _dbl_list_pop_back(list, item_size); + goto RETURN_LIST_REMOVE; + } + + output = _dbl_list_get(list, index, item_size); + if (!output) { + goto RETURN_LIST_REMOVE; + } + + output->header.prev->header.next = output->header.next; + output->header.next->header.prev = output->header.prev; + + --(list->node_count); + + output->header.prev = output->header.next = NULL; + +RETURN_LIST_REMOVE: + return output; +} + +void _dbl_list_empty(GenericList *list, u64 item_size) { + wapp_debug_assert(list != NULL, "`list` should not be NULL"); + _dbl_list_validate(list, item_size); + + u64 count = list->node_count; + for (u64 i = 0; i < count; ++i) { + _dbl_list_pop_back(list, item_size); + } +} + +wapp_intern GenericList _node_to_list(GenericNode *node, u64 item_size) { + GenericList output = { + .magic = WAPP_DBL_LIST_MAGIC, + .first = node, + .last = node, + .node_count = 1, + .item_size = item_size, + }; + + while (output.first->header.prev != NULL) { + output.first = output.first->header.prev; + ++(output.node_count); + } + + while (output.last->header.next != NULL) { + output.last = output.last->header.next; + ++(output.node_count); + } + + return output; +} + +wapp_intern inline void _dbl_list_validate(const GenericList *list, u64 item_size) { + wapp_runtime_assert(list->magic == WAPP_DBL_LIST_MAGIC, "`list` isn't a valid wapp list type"); + wapp_runtime_assert(list->item_size == item_size, "Invalid item provided"); +} + +wapp_intern inline void _dbl_list_node_validate(const GenericList *list, const GenericNode *node, u64 item_size) { + wapp_runtime_assert(node->header.magic == WAPP_DBL_NODE_MAGIC, "`node` isn't a valid wapp node type"); + wapp_runtime_assert(list->item_size == node->header.item_size, "Mismatched `list` and `node` types"); + wapp_runtime_assert(node->header.item_size == item_size, "Invalid item provided"); +} diff --git a/wapp/base/dbl_list/dbl_list.h b/wapp/base/dbl_list/dbl_list.h new file mode 100644 index 0000000..eb9c68f --- /dev/null +++ b/wapp/base/dbl_list/dbl_list.h @@ -0,0 +1,184 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef DBL_LIST_H +#define DBL_LIST_H + +#include "../mem/allocator/mem_allocator.h" +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#define WAPP_DBL_LIST_MAGIC (u64)0x57415f444c5354 +#define WAPP_DBL_NODE_MAGIC (u64)0x57415f444e44 + +typedef struct GenericNode GenericNode; + +typedef struct { + u64 magic; + u64 item_size; + GenericNode *prev; + GenericNode *next; +} NodeHeader; + +struct GenericNode { + NodeHeader header; + void *item; +}; + +typedef struct { + u64 magic; + u64 node_count; + u64 item_size; + GenericNode *first; + GenericNode *last; +} GenericList; + +// NOTE (Abdelrahman): GenericList typedefs for readability +typedef GenericList VoidPtrList; +typedef GenericList C8List; +typedef GenericList C16List; +typedef GenericList C32List; +typedef GenericList U8List; +typedef GenericList U16List; +typedef GenericList U32List; +typedef GenericList U64List; +typedef GenericList B8List; +typedef GenericList I8List; +typedef GenericList I16List; +typedef GenericList I32List; +typedef GenericList I64List; +typedef GenericList F32List; +typedef GenericList F64List; +typedef GenericList F128List; +typedef GenericList UptrList; +typedef GenericList IptrList; +typedef GenericList Str8List; + +// NOTE (Abdelrahman): GenericNode typedefs for readability +typedef GenericNode VoidPtrNode; +typedef GenericNode C8Node; +typedef GenericNode C16Node; +typedef GenericNode C32Node; +typedef GenericNode U8Node; +typedef GenericNode U16Node; +typedef GenericNode U32Node; +typedef GenericNode U64Node; +typedef GenericNode B8Node; +typedef GenericNode I8Node; +typedef GenericNode I16Node; +typedef GenericNode I32Node; +typedef GenericNode I64Node; +typedef GenericNode F32Node; +typedef GenericNode F64Node; +typedef GenericNode F128Node; +typedef GenericNode UptrNode; +typedef GenericNode IptrNode; +typedef GenericNode Str8Node; + +#ifdef WAPP_PLATFORM_CPP +#define wapp_dbl_list(TYPE) \ + GenericList{WAPP_DBL_LIST_MAGIC, 0, sizeof(TYPE), nullptr, nullptr} +#define _dbl_list_node(TYPE, ITEM_PTR) ([&]() { \ + wapp_persist GenericNode node = { \ + NodeHeader{WAPP_DBL_NODE_MAGIC, sizeof(TYPE), nullptr, nullptr}, \ + ITEM_PTR, \ + }; \ + \ + return &node; \ +}()) +#else +#define wapp_dbl_list(TYPE) ( \ + (GenericList){.magic = WAPP_DBL_LIST_MAGIC, .item_size = sizeof(TYPE)} \ +) +#define _dbl_list_node(TYPE, ITEM_PTR) ( \ + &((GenericNode){.header = {.magic = WAPP_DBL_NODE_MAGIC, .item_size = sizeof(TYPE)}, \ + .item = ITEM_PTR}) \ +) +#endif // !WAPP_PLATFORM_CPP + +#define wapp_dbl_list_alloc(TYPE, ALLOCATOR) \ + (_dbl_list_alloc(ALLOCATOR, sizeof(TYPE))) +#define wapp_dbl_list_get(TYPE, LIST_PTR, ITEM_INDEX) \ + ((TYPE *)(_dbl_list_get(LIST_PTR, ITEM_INDEX, sizeof(TYPE))->item)) +#define wapp_dbl_list_get_node(TYPE, LIST_PTR, ITEM_INDEX) \ + (_dbl_list_get(LIST_PTR, ITEM_INDEX, sizeof(TYPE))) +#define wapp_dbl_list_get_node_item(TYPE, NODE_PTR) \ + ((TYPE *)( \ + (NODE_PTR == NULL) ? \ + NULL : \ + (NODE_PTR)->item \ + )) +#define wapp_dbl_list_push_front(TYPE, LIST_PTR, ITEM_PTR) \ + (_dbl_list_push_front(LIST_PTR, _dbl_list_node(TYPE, ITEM_PTR), sizeof(TYPE))) +#define wapp_dbl_list_push_back(TYPE, LIST_PTR, ITEM_PTR) \ + (_dbl_list_push_back(LIST_PTR, _dbl_list_node(TYPE, ITEM_PTR), sizeof(TYPE))) +#define wapp_dbl_list_insert(TYPE, LIST_PTR, ITEM_PTR, ITEM_INDEX) \ + (_dbl_list_insert(LIST_PTR, _dbl_list_node(TYPE, ITEM_PTR), \ + ITEM_INDEX, sizeof(TYPE))) +#define wapp_dbl_list_push_front_alloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR) \ + (_dbl_list_push_front(LIST_PTR, _dbl_list_node_alloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)), \ + sizeof(TYPE))) +#define wapp_dbl_list_push_back_alloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR) \ + (_dbl_list_push_back(LIST_PTR, _dbl_list_node_alloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)), \ + sizeof(TYPE))) +#define wapp_dbl_list_insert_alloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR, ITEM_INDEX) \ + (_dbl_list_insert(LIST_PTR, _dbl_list_node_alloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)), \ + ITEM_INDEX, sizeof(TYPE))) +#define wapp_dbl_list_pop_front(TYPE, LIST_PTR) \ + ((TYPE *)( \ + (LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \ + NULL : \ + _dbl_list_pop_front(LIST_PTR, sizeof(TYPE))->item \ + )) +#define wapp_dbl_list_pop_back(TYPE, LIST_PTR) \ + ((TYPE *)( \ + (LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \ + NULL : \ + _dbl_list_pop_back(LIST_PTR, sizeof(TYPE))->item \ + )) +#define wapp_dbl_list_remove(TYPE, LIST_PTR, ITEM_INDEX) \ + ((TYPE *)( \ + (LIST_PTR == NULL || (LIST_PTR)->node_count == 0 || ITEM_INDEX >= (LIST_PTR)->node_count) ? \ + NULL : \ + _dbl_list_remove(LIST_PTR, ITEM_INDEX, sizeof(TYPE))->item \ + )) +#define wapp_dbl_list_pop_front_node(TYPE, LIST_PTR) \ + ( \ + (LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \ + NULL : \ + _dbl_list_pop_front(LIST_PTR, sizeof(TYPE)) \ + ) +#define wapp_dbl_list_pop_back_node(TYPE, LIST_PTR) \ + ( \ + (LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \ + NULL : \ + _dbl_list_pop_back(LIST_PTR, sizeof(TYPE)) \ + ) +#define wapp_dbl_list_remove_node(TYPE, LIST_PTR, ITEM_INDEX) \ + ( \ + (LIST_PTR == NULL || (LIST_PTR)->node_count == 0 || ITEM_INDEX >= (LIST_PTR)->node_count) ? \ + NULL : \ + _dbl_list_remove(LIST_PTR, ITEM_INDEX, sizeof(TYPE)) \ + ) +#define wapp_dbl_list_empty(TYPE, LIST_PTR) \ + (_dbl_list_empty(LIST_PTR, sizeof(TYPE))) + +GenericList *_dbl_list_alloc(const Allocator *allocator, u64 item_size); +GenericNode *_dbl_list_node_alloc(const Allocator *allocator, void *item, u64 item_size); +GenericNode *_dbl_list_get(const GenericList *list, u64 index, u64 item_size); +void _dbl_list_push_front(GenericList *list, GenericNode *node, u64 item_size); +void _dbl_list_push_back(GenericList *list, GenericNode *node, u64 item_size); +void _dbl_list_insert(GenericList *list, GenericNode *node, u64 index, u64 item_size); +GenericNode *_dbl_list_pop_front(GenericList *list, u64 item_size); +GenericNode *_dbl_list_pop_back(GenericList *list, u64 item_size); +GenericNode *_dbl_list_remove(GenericList *list, u64 index, u64 item_size); +void _dbl_list_empty(GenericList *list, u64 item_size); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !DBL_LIST_H diff --git a/wapp/base/mem/allocator/mem_allocator.c b/wapp/base/mem/allocator/mem_allocator.c new file mode 100644 index 0000000..d9b0a50 --- /dev/null +++ b/wapp/base/mem/allocator/mem_allocator.c @@ -0,0 +1,35 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "mem_allocator.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/assert/assert.h" +#include + +void *wapp_mem_allocator_alloc(const Allocator *allocator, u64 size) { + wapp_debug_assert(allocator != NULL && (allocator->alloc) != NULL, "`allocator` and `allocator->alloc` should not be NULL"); + return allocator->alloc(size, allocator->obj); +} + +void *wapp_mem_allocator_alloc_aligned(const Allocator *allocator, u64 size, u64 alignment) { + wapp_debug_assert(allocator != NULL && (allocator->alloc_aligned) != NULL, "`allocator` and `allocator->alloc_aligned` should not be NULL"); + return allocator->alloc_aligned(size, alignment, allocator->obj); +} + +void *wapp_mem_allocator_realloc(const Allocator *allocator, void *ptr, u64 old_size, u64 new_size) { + wapp_debug_assert(allocator != NULL && (allocator->realloc) != NULL, "`allocator` and `allocator->realloc` should not be NULL"); + return allocator->realloc(ptr, old_size, new_size, allocator->obj); +} + +void *wapp_mem_allocator_realloc_aligned(const Allocator *allocator, void *ptr, u64 old_size, + u64 new_size, u64 alignment) { + wapp_debug_assert(allocator != NULL && (allocator->realloc_aligned) != NULL, "`allocator` and `allocator->realloc_aligned` should not be NULL"); + return allocator->realloc_aligned(ptr, old_size, new_size, alignment, allocator->obj); +} + +void wapp_mem_allocator_free(const Allocator *allocator, void **ptr, u64 size) { + if (!allocator || !(allocator->free)) { + return; + } + + allocator->free(ptr, size, allocator->obj); +} diff --git a/wapp/base/mem/allocator/mem_allocator.h b/wapp/base/mem/allocator/mem_allocator.h new file mode 100644 index 0000000..add3ca4 --- /dev/null +++ b/wapp/base/mem/allocator/mem_allocator.h @@ -0,0 +1,50 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_ALLOCATOR_H +#define MEM_ALLOCATOR_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" +#include + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef void *(MemAllocFunc)(u64 size, void *alloc_obj); +typedef void *(MemAllocAlignedFunc)(u64 size, u64 alignment, void *alloc_obj); +typedef void *(MemReallocFunc)(void *ptr, u64 old_size, u64 new_size, void *alloc_obj); +typedef void *(MemReallocAlignedFunc)(void *ptr, u64 old_size, u64 new_size, u64 alignment, void *alloc_obj); +typedef void (MemFreeFunc)(void **ptr, u64 size, void *alloc_obj); + +typedef struct Allocator Allocator; +struct Allocator { + void *obj; + MemAllocFunc *alloc; + MemAllocAlignedFunc *alloc_aligned; + MemReallocFunc *realloc; + MemReallocAlignedFunc *realloc_aligned; + MemFreeFunc *free; +}; + +#ifdef WAPP_PLATFORM_CPP +#define wapp_mem_allocator_invalid(ALLOCATOR) ([&]() { \ + Allocator alloc{}; \ + return memcmp(ALLOCATOR, &alloc, sizeof(Allocator)) == 0; \ +}()) +#else +#define wapp_mem_allocator_invalid(ALLOCATOR) (memcmp(ALLOCATOR, &((Allocator){0}), sizeof(Allocator)) == 0) +#endif // !WAPP_PLATFORM_CPP + +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 old_size, u64 new_size); +void *wapp_mem_allocator_realloc_aligned(const Allocator *allocator, void *ptr, u64 old_size, + u64 new_size, u64 alignment); +void wapp_mem_allocator_free(const Allocator *allocator, void **ptr, u64 size); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_ALLOCATOR_H diff --git a/wapp/base/mem/utils/mem_utils.c b/wapp/base/mem/utils/mem_utils.c new file mode 100644 index 0000000..9328c8f --- /dev/null +++ b/wapp/base/mem/utils/mem_utils.c @@ -0,0 +1,25 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "mem_utils.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/assert/assert.h" +#include "../../../common/misc/misc_utils.h" +#include + +void *wapp_mem_util_align_forward(void *ptr, u64 alignment) { + wapp_debug_assert(ptr != NULL, "`ptr` should not be NULL"); + wapp_runtime_assert(wapp_is_power_of_two(alignment), "`alignment` value is not a power of two"); + + 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; +} diff --git a/wapp/base/mem/utils/mem_utils.h b/wapp/base/mem/utils/mem_utils.h new file mode 100644 index 0000000..ca7f781 --- /dev/null +++ b/wapp/base/mem/utils/mem_utils.h @@ -0,0 +1,19 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_UTILS_H +#define MEM_UTILS_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +void *wapp_mem_util_align_forward(void *ptr, u64 alignment); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_UTILS_H diff --git a/wapp/base/queue/queue.c b/wapp/base/queue/queue.c new file mode 100644 index 0000000..b6cb0bb --- /dev/null +++ b/wapp/base/queue/queue.c @@ -0,0 +1,108 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "queue.h" +#include "../array/array.h" +#include "../../common/assert/assert.h" +#include "../../common/misc/misc_utils.h" +#include + +void _queue_push(GenericQueue *queue, void *item, u64 item_size) { + wapp_debug_assert(queue != NULL, "`queue` should not be NULL"); + wapp_runtime_assert(item_size == wapp_array_item_size(queue->items), "Invalid type"); + + u64 capacity = wapp_array_capacity(queue->items); + if (queue->count >= capacity) { return; } + + u64 index = (queue->back)++; + _array_set(queue->items, index, item, item_size); + ++(queue->count); + + if (queue->back >= capacity) { + queue->back = 0; + } +} + +GenericQueue *_queue_push_alloc(const Allocator *allocator, GenericQueue *queue, void *item, u64 item_size) { + wapp_debug_assert(allocator != NULL && queue != NULL && item != NULL, + "`allocator`, `queue` and `item` should not be NULL"); + wapp_runtime_assert(item_size == wapp_array_item_size(queue->items), "Invalid type"); + + GenericQueue *output = queue; + + u64 capacity = wapp_array_capacity(queue->items); + + // NOTE (Abdelrahman): Extracted into variable to fix MSVC error + b8 queue_full = queue->count >= capacity; + if (queue_full) { + u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(capacity * 2); + u64 array_size = _array_calc_alloc_size(new_capacity, item_size); + u64 alloc_size = sizeof(GenericQueue) + array_size; + void *buffer = wapp_mem_allocator_alloc(allocator, alloc_size); + if (!buffer) { + goto RETURN_QUEUE_PUSH_ALLOC; + } + + memset((void *)buffer, 0, alloc_size); + + output = (GenericQueue *)buffer; + output->items = _array_from_preallocated_buffer((void *)(output + 1), array_size, ARRAY_INIT_FILLED, item_size); + + // NOTE (Abdelrahman): When the queue is full, the front and back indices should + // always be the same + u64 front_count = capacity - queue->front; + u64 back_count = queue->back; + void *copy_boundary = (void *)((uptr)(queue->items) + (queue->front * item_size)); + + memcpy(output->items, copy_boundary, front_count * item_size); + + /** + * NOTE (Abdelrahman): Since this is a ring buffer, the elements at the beginning of the array + * aren't always the ones at the front of the queue. When that's the case, the memcpy above + * will only copy a subset of the elements. This is why we need to copy the remaining ones. + * + * Example: Take a queue that looks like this with a capacity of 5 elements + * + * 0 1 | 2 3 4 + * ---------------|----------------------- + * | * | * | * | * | * | + * ---------------|----------------------- + * | + * queue_front = 2 + * queue_back = 2 + * + * In this case, the first memcpy will only copy elements 2-4. The memcpy below will be + * responsible for copying elements 0-1. + */ + b8 items_left_to_copy = back_count > 0; + if (items_left_to_copy) { + void *back_copy_dst = (void *)((uptr)(output->items) + (front_count * item_size)); + memcpy(back_copy_dst, queue->items, back_count * item_size); + } + + output->front = 0; + output->back = front_count + back_count; + output->count = queue->count; + } + + _queue_push(output, item, item_size); + +RETURN_QUEUE_PUSH_ALLOC: + return output; +} + +void *_queue_pop(GenericQueue *queue, u64 item_size) { + wapp_debug_assert(queue != NULL, "`queue` should not be NULL"); + wapp_runtime_assert(item_size == wapp_array_item_size(queue->items), "Invalid type"); + + if (queue->count == 0) { return NULL; } + + u64 index = (queue->front)++; + --(queue->count); + + u64 capacity = wapp_array_capacity(queue->items); + if (queue->front >= capacity) { + queue->front = 0; + } + + return _array_get(queue->items, index, item_size); +} diff --git a/wapp/base/queue/queue.h b/wapp/base/queue/queue.h new file mode 100644 index 0000000..6e84ef2 --- /dev/null +++ b/wapp/base/queue/queue.h @@ -0,0 +1,100 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef QUEUE_H +#define QUEUE_H + +#include "../array/array.h" +#include "../mem/allocator/mem_allocator.h" +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef struct { + GenericArray items; + u64 front; + u64 back; + u64 count; +} GenericQueue; + +// NOTE (Abdelrahman): GenericQueue typedefs for readability +typedef GenericQueue VoidPtrQueue; +typedef GenericQueue C8Queue; +typedef GenericQueue C16Queue; +typedef GenericQueue C32Queue; +typedef GenericQueue U8Queue; +typedef GenericQueue U16Queue; +typedef GenericQueue U32Queue; +typedef GenericQueue U64Queue; +typedef GenericQueue B8Queue; +typedef GenericQueue I8Queue; +typedef GenericQueue I16Queue; +typedef GenericQueue I32Queue; +typedef GenericQueue I64Queue; +typedef GenericQueue F32Queue; +typedef GenericQueue F64Queue; +typedef GenericQueue F128Queue; +typedef GenericQueue UptrQueue; +typedef GenericQueue IptrQueue; +typedef GenericQueue Str8Queue; + +#ifdef WAPP_PLATFORM_CPP +#define wapp_queue(TYPE, CAPACITY) ([&]() { \ + wapp_persist GenericArray arr = wapp_array_with_capacity(TYPE, CAPACITY, ARRAY_INIT_FILLED); \ + wapp_persist GenericQueue queue = { \ + arr, \ + 0, \ + 0, \ + 0, \ + }; \ + \ + return queue; \ +}()) +#define wapp_queue_alloc(TYPE, ALLOCATOR_PTR, CAPACITY) ([&]() { \ + wapp_persist GenericQueue queue = { \ + wapp_array_alloc_capacity(TYPE, ALLOCATOR_PTR, CAPACITY, ARRAY_INIT_FILLED), \ + 0, \ + 0, \ + 0, \ + }; \ + \ + return queue; \ +}()) +#else +#define wapp_queue(TYPE, CAPACITY) ((GenericQueue){ \ + .items = wapp_array_with_capacity(TYPE, CAPACITY, ARRAY_INIT_FILLED), \ + .front = 0, \ + .back = 0, \ + .count = 0, \ +}) +#define wapp_queue_alloc(TYPE, ALLOCATOR_PTR, CAPACITY) ((GenericQueue){ \ + .items = wapp_array_alloc_capacity(TYPE, ALLOCATOR_PTR, CAPACITY, ARRAY_INIT_FILLED), \ + .front = 0, \ + .back = 0, \ + .count = 0, \ +}) +#endif // !WAPP_PLATFORM_CPP + +#define wapp_queue_capacity(QUEUE_PTR) (wapp_array_capacity((QUEUE_PTR)->items)) +#define wapp_queue_item_size(QUEUE_PTR) (wapp_array_item_size((QUEUE_PTR)->items)) +#define wapp_queue_push(TYPE, QUEUE_PTR, VALUE_PTR) ( \ + _queue_push(QUEUE_PTR, VALUE_PTR, sizeof(TYPE)) \ +) +#define wapp_queue_push_alloc(TYPE, ALLOCATOR_PTR, QUEUE_PTR, VALUE_PTR) ( \ + _queue_push_alloc(ALLOCATOR_PTR, QUEUE_PTR, VALUE_PTR, sizeof(TYPE)) \ +) +#define wapp_queue_pop(TYPE, QUEUE_PTR) ( \ + (TYPE *)_queue_pop(QUEUE_PTR, sizeof(TYPE)) \ +) + +void _queue_push(GenericQueue *queue, void *item, u64 item_size); +GenericQueue *_queue_push_alloc(const Allocator *allocator, GenericQueue *queue, void *item, u64 item_size); +void *_queue_pop(GenericQueue *queue, u64 item_size); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !QUEUE_H diff --git a/wapp/base/strings/str8/str8.c b/wapp/base/strings/str8/str8.c new file mode 100644 index 0000000..d9b6694 --- /dev/null +++ b/wapp/base/strings/str8/str8.c @@ -0,0 +1,480 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "str8.h" +#include "../../array/array.h" +#include "../../mem/allocator/mem_allocator.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/assert/assert.h" +#include +#include +#include +#include +#include + +#define STR8_BUF_ALLOC_SIZE(CAPACITY) (sizeof(Str8) + sizeof(c8) * CAPACITY) + +Str8 *wapp_str8_alloc_buf(const Allocator *allocator, u64 capacity) { + wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL"); + + Str8 *str = wapp_mem_allocator_alloc(allocator, STR8_BUF_ALLOC_SIZE(capacity)); + if (!str) { + goto RETURN_STR8; + } + + str->buf = (u8 *)str + sizeof(Str8); + str->size = 0; + str->capacity = capacity; + +RETURN_STR8: + return str; +} + +Str8 *wapp_str8_alloc_and_fill_buf(const Allocator *allocator, u64 capacity) { + Str8 *out = wapp_str8_alloc_buf(allocator, capacity); + if (out) { + memset(out->buf, 0, capacity); + out->size = capacity; + } + return out; +} + +Str8 *wapp_str8_alloc_cstr(const Allocator *allocator, const char *str) { + wapp_debug_assert(allocator != NULL && str != NULL, "`allocator` and `str` should not be NULL"); + + u64 length = strlen(str); + Str8 *output = wapp_str8_alloc_buf(allocator, length * 2); + if (!output) { + goto RETURN_ALLOC_CSTR; + } + + output->size = length; + memcpy(output->buf, str, length); + +RETURN_ALLOC_CSTR: + return output; +} + +Str8 *wapp_str8_alloc_str8(const Allocator *allocator, Str8RO *str) { + wapp_debug_assert(allocator != NULL && str != NULL, "`allocator` and `str` should not be NULL"); + + Str8 *output = wapp_str8_alloc_buf(allocator, str->capacity); + if (!output) { + goto RETURN_ALLOC_STR8; + } + + output->size = str->size; + memcpy(output->buf, str->buf, str->size); + +RETURN_ALLOC_STR8: + return output; +} + +Str8 *wapp_str8_alloc_substr(const Allocator *allocator, Str8RO *str, u64 start, u64 end) { + wapp_debug_assert(allocator != NULL && str != NULL, "`allocator` and `str` should not be NULL"); + + Str8 *output = NULL; + + if (start >= str->size || start >= end) { + goto RETURN_ALLOC_SUBSTR; + } + + if (end > str->size) { + end = str->size; + } + + output = wapp_str8_alloc_buf(allocator, str->capacity); + if (!output) { + goto RETURN_ALLOC_SUBSTR; + } + + output->size = end - start; + memcpy(output->buf, str->buf + start, output->size); + +RETURN_ALLOC_SUBSTR: + return output; +} + +void wapp_str8_dealloc_buf(const Allocator *allocator, Str8 **str) { + wapp_debug_assert(allocator != NULL && str != NULL && (*str) != NULL, "Either `allocator` is NULL or `str` is an invalid double pointer"); + wapp_mem_allocator_free(allocator, (void **)str, STR8_BUF_ALLOC_SIZE((*str)->capacity)); +} + +c8 wapp_str8_get(const Str8 *str, u64 index) { + if (index >= str->size) { + return '\0'; + } + + return str->buf[index]; +} + +void wapp_str8_set(Str8 *str, u64 index, c8 c) { + if (index >= str->size) { + return; + } + + str->buf[index] = c; +} + +void wapp_str8_push_back(Str8 *str, c8 c) { + if (!(str->size < str->capacity)) { + return; + } + + u64 index = (str->size)++; + wapp_str8_set(str, index, c); +} + +b8 wapp_str8_equal(Str8RO *s1, Str8RO *s2) { + if (s1->size != s2->size) { + return false; + } + + return wapp_str8_equal_to_count(s1, s2, s1->size); +} + +b8 wapp_str8_equal_to_count(Str8RO* s1, Str8RO* s2, u64 count) { + if (!s1 || !s2) { + return false; + } + + return memcmp(s1->buf, s2->buf, count) == 0; +} + +Str8 wapp_str8_slice(Str8RO *str, u64 start, u64 end) { + if (start >= str->size || start >= end) { + start = str->size; + end = str->size; + } + + if (end > str->size) { + end = str->size; + } + + return (Str8RO){ + .capacity = end - start, + .size = end - start, + .buf = str->buf + start, + }; +} + +Str8 *wapp_str8_alloc_concat(const Allocator *allocator, Str8 *dst, Str8RO *src) { + wapp_debug_assert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL"); + + Str8 *output = NULL; + u64 remaining = dst->capacity - dst->size; + if (src->size <= remaining) { + output = dst; + goto SOURCE_STRING_STR8_CONCAT; + } + + u64 capacity = dst->capacity + src->size; + + output = wapp_str8_alloc_buf(allocator, capacity); + if (!output) { + goto RETURN_STR8_CONCAT; + } + + wapp_str8_concat_capped(output, dst); + +SOURCE_STRING_STR8_CONCAT: + wapp_str8_concat_capped(output, src); + +RETURN_STR8_CONCAT: + return output; +} + +void wapp_str8_concat_capped(Str8 *dst, Str8RO *src) { + wapp_debug_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); + + u64 remaining = dst->capacity - dst->size; + u64 to_copy = remaining < src->size ? remaining : src->size; + + memcpy(dst->buf + dst->size, src->buf, to_copy); + dst->size += to_copy; +} + +void wapp_str8_copy_cstr_capped(Str8 *dst, const char *src) { + wapp_debug_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); + + u64 length = strlen(src); + u64 to_copy = length <= dst->capacity ? length : dst->capacity; + + memset(dst->buf, 0, dst->size); + memcpy(dst->buf, src, to_copy); + dst->size = to_copy; +} + +void wapp_str8_copy_str8_capped(Str8 *dst, Str8RO *src) { + wapp_debug_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); + + u64 to_copy = src->size <= dst->capacity ? src->size : dst->capacity; + + memset(dst->buf, 0, dst->size); + memcpy(dst->buf, src->buf, to_copy); + dst->size = to_copy; +} + +void wapp_str8_copy_to_cstr(char *dst, Str8RO *src, u64 dst_capacity) { + wapp_debug_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); + + u64 to_copy = src->size < dst_capacity ? src->size : dst_capacity - 1; + + memset(dst, 0, dst_capacity); + memcpy(dst, src->buf, to_copy); +} + +void wapp_str8_format(Str8 *dst, const char *format, ...) { + wapp_debug_assert(dst != NULL && format != NULL, "`dst` and `format` should not be NULL"); + + va_list args1; + va_list args2; + + va_start(args1, format); + va_copy(args2, args1); + + u64 total_size = vsnprintf(NULL, 0, format, args1); + dst->size = total_size <= dst->capacity ? total_size : dst->capacity; + + vsnprintf((char *)(dst->buf), dst->capacity, format, args2); + + va_end(args1); + va_end(args2); +} + +void wapp_str8_to_lower(Str8 *dst, Str8RO *src) { + wapp_debug_assert(src != NULL && dst != NULL, "`dst` and `src` should not be NULL"); + wapp_debug_assert(dst->capacity >= src->capacity, "`dst` does not have enough capacity"); + + dst->size = src->size; + + // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of + // MSVC Spectre mitigation warnings + u64 index = 0; + b8 running = true; + while (running) { + wapp_str8_set(dst, index, (u8)tolower(wapp_str8_get(src, index))); + ++index; + running = index < src->size; + } +} + +void wapp_str8_to_upper(Str8 *dst, Str8RO *src) { + wapp_debug_assert(src != NULL && dst != NULL, "`dst` and `src` should not be NULL"); + wapp_debug_assert(dst->capacity >= src->capacity, "`dst` does not have enough capacity"); + + dst->size = src->size; + + // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of + // MSVC Spectre mitigation warnings + u64 index = 0; + b8 running = true; + while (running) { + wapp_str8_set(dst, index, (u8)toupper(wapp_str8_get(src, index))); + ++index; + running = index < src->size; + } +} + +void wapp_str8_from_bytes(Str8 *dst, const U8Array src) { + wapp_debug_assert(src != NULL && dst != NULL, "`dst` and `src` should not be NULL"); + + u64 size = wapp_array_count(src) * wapp_array_item_size(src); + + wapp_debug_assert(dst->capacity >= size, "`dst` does not have enough capacity"); + + dst->size = size; + memcpy(dst->buf, src, size); +} + +i64 wapp_str8_find(Str8RO *str, Str8RO substr) { + if (!str || substr.size > str->size) { + return -1; + } + + // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of + // MSVC Spectre mitigation warnings + u64 char_index = 0; + b8 running = char_index < str->size; + while (running) { + const c8 *sub = str->buf + char_index; + if (memcmp(sub, substr.buf, substr.size) == 0) { + return char_index; + } + + ++char_index; + running = char_index < str->size; + } + + return -1; +} + +i64 wapp_str8_rfind(Str8RO *str, Str8RO substr) { + if (!str || substr.size > str->size) { + return -1; + } + + // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of + // MSVC Spectre mitigation warnings + i64 char_index = str->size - substr.size; + b8 running = char_index >= 0; + while (running) { + const c8 *sub = str->buf + char_index; + if (memcmp(sub, substr.buf, substr.size) == 0) { + return char_index; + } + + --char_index; + running = char_index >= 0; + } + + return -1; +} + +Str8List *wapp_str8_split_with_max(const Allocator *allocator, Str8RO *str, Str8RO *delimiter, i64 max_splits) { + wapp_debug_assert(allocator != NULL && str != NULL && delimiter != NULL, "`allocator`, `str` and `delimiter` should not be NULL"); + + Str8List *output = wapp_dbl_list_alloc(Str8, allocator); + + if (delimiter->size > str->size) { + Str8 *full = wapp_str8_alloc_str8(allocator, str); + if (full) { + wapp_dbl_list_push_back_alloc(Str8, allocator, output, full); + } + + goto RETURN_STR8_SPLIT; + } + + i64 start = 0; + i64 end = 0; + i64 splits = 0; + Str8 *rest = wapp_str8_alloc_str8(allocator, str); + Str8 *before_str; + + while ((end = wapp_str8_find(rest, *delimiter)) != -1) { + if (max_splits > 0 && splits >= max_splits) { + break; + } + + before_str = wapp_str8_alloc_substr(allocator, str, start, start + end); + if (before_str) { + wapp_dbl_list_push_back_alloc(Str8, allocator, output, before_str); + } + + wapp_mem_allocator_free(allocator, (void **)&rest, sizeof(Str8)); + rest = wapp_str8_alloc_substr(allocator, str, start + end + delimiter->size, str->size); + start += end + delimiter->size; + + ++splits; + } + + // Ensure the last part of the string after the delimiter is added to the list + rest = wapp_str8_alloc_substr(allocator, str, start, str->size); + if (rest) { + wapp_dbl_list_push_back_alloc(Str8, allocator, output, rest); + } + +RETURN_STR8_SPLIT: + return output; +} + +Str8List *wapp_str8_rsplit_with_max(const Allocator *allocator, Str8RO *str, Str8RO *delimiter, i64 max_splits) { + wapp_debug_assert(allocator != NULL && str != NULL && delimiter != NULL, "`allocator`, `str` and `delimiter` should not be NULL"); + + Str8List *output = wapp_dbl_list_alloc(Str8, allocator); + + if (delimiter->size > str->size) { + Str8 *full = wapp_str8_alloc_str8(allocator, str); + if (full) { + wapp_dbl_list_push_back_alloc(Str8, allocator, output, full); + } + + goto RETURN_STR8_SPLIT; + } + + i64 end = 0; + i64 splits = 0; + Str8 *rest = wapp_str8_alloc_str8(allocator, str); + Str8 *after_str; + + while ((end = wapp_str8_rfind(rest, *delimiter)) != -1) { + if (max_splits > 0 && splits >= max_splits) { + break; + } + + after_str = wapp_str8_alloc_substr(allocator, rest, end + delimiter->size, str->size); + if (after_str) { + wapp_dbl_list_push_front_alloc(Str8, allocator, output, after_str); + } + + wapp_mem_allocator_free(allocator, (void **)&rest, sizeof(Str8)); + rest = wapp_str8_alloc_substr(allocator, rest, 0, end); + + ++splits; + } + + rest = wapp_str8_alloc_substr(allocator, str, 0, rest->size); + if (rest) { + wapp_dbl_list_push_front_alloc(Str8, allocator, output, rest); + } + +RETURN_STR8_SPLIT: + return output; +} + +Str8 *wapp_str8_join(const Allocator *allocator, const Str8List *list, Str8RO *delimiter) { + wapp_debug_assert(allocator != NULL && list != NULL && delimiter != NULL, "`allocator`, `list` and `delimiter` should not be NULL"); + + u64 capacity = wapp_str8_list_total_size(list) + (delimiter->size * (list->node_count - 1)); + Str8 *output = wapp_str8_alloc_buf(allocator, capacity * 2); + + // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of + // MSVC Spectre mitigation warnings + Str8 *node; + u64 node_index = 0; + b8 running = node_index < list->node_count; + while (running) { + node = wapp_dbl_list_get(Str8, list, node_index); + if (!node) { + break; + } + + wapp_str8_concat_capped(output, node); + + // NOTE (Abdelrahman): Comparison extracted to variable to silence + // MSVC Spectre mitigation warnings + b8 not_last = node_index + 1 < list->node_count; + if (not_last) { + wapp_str8_concat_capped(output, delimiter); + } + + ++node_index; + running = node_index < list->node_count; + } + + return output; +} + +u64 wapp_str8_list_total_size(const Str8List *list) { + if (!list) { + return 0; + } + + // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of + // MSVC Spectre mitigation warnings + Str8 *node; + u64 node_index = 0; + u64 output = 0; + b8 running = node_index < list->node_count; + while (running) { + node = wapp_dbl_list_get(Str8, list, node_index); + if (!node) { + break; + } + + output += node->size; + ++node_index; + running = node_index < list->node_count; + } + + return output; +} diff --git a/wapp/base/strings/str8/str8.h b/wapp/base/strings/str8/str8.h new file mode 100644 index 0000000..1ae83e6 --- /dev/null +++ b/wapp/base/strings/str8/str8.h @@ -0,0 +1,130 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef STR8_H +#define STR8_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/assert/assert.h" +#include "../../../common/platform/platform.h" +#include "../../array/array.h" +#include "../../dbl_list/dbl_list.h" +#include "../../mem/allocator/mem_allocator.h" +#include + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef struct Str8 Str8; +struct Str8 { + u64 capacity; + u64 size; + c8 *buf; +}; + +typedef const Str8 Str8RO; + +/** + * Utilities to be used with printf functions + */ +#define WAPP_STR8_SPEC "%.*s" +#define wapp_str8_varg(STRING) (int)((STRING).size), (STRING).buf + +/** + * Str8 stack buffers + */ + +#ifdef WAPP_PLATFORM_CPP +// Uses a lambda to achieve the same behaviour achieved by the C macro +#define wapp_str8_buf(CAPACITY) ([&](){ \ + wapp_persist c8 buf[CAPACITY] = {}; \ + memset(buf, 0, CAPACITY); \ + return Str8{CAPACITY, 0, buf}; \ +}()) + +// Uses a lambda to achieve the same behaviour achieved by the C macro +#define wapp_str8_lit(STRING) ([&]() { \ + wapp_persist c8 buf[sizeof(STRING) * 2] = {}; \ + memcpy(buf, STRING, sizeof(STRING)); \ + return Str8{(sizeof(STRING) - 1) * 2, sizeof(STRING) - 1, buf}; \ +}()) + +#define wapp_str8_lit_ro(STRING) Str8RO{sizeof(STRING) - 1, sizeof(STRING) - 1, (c8 *)STRING} +#define wapp_str8_lit_ro_initialiser_list(STRING) {sizeof(STRING) - 1, sizeof(STRING) - 1, (c8 *)STRING} +#else +#define wapp_str8_buf(CAPACITY) ((Str8){.capacity = CAPACITY, .size = 0, .buf = (c8[CAPACITY]){0}}) + +// Utilises the fact that memcpy returns pointer to dest buffer and that getting +// address of compound literals is valid in C to create a string on the stack +#define wapp_str8_lit(STRING) ((Str8){.capacity = (sizeof(STRING) - 1) * 2, \ + .size = sizeof(STRING) - 1, \ + .buf = memcpy(&((c8 [sizeof(STRING) * 2]){0}), \ + STRING, \ + sizeof(STRING))}) +#define wapp_str8_lit_ro(STRING) ((Str8RO){.capacity = sizeof(STRING) - 1, \ + .size = sizeof(STRING) - 1, \ + .buf = (c8 *)STRING}) +// To be used only when initialising a static storage variable in compilers that don't support +// initialisers with the syntax of wapp_str8_lit_ro (e.g. gcc). Should only be used when necessary +// and only be assigned to a Str8RO variable to avoid any attempt at modifying the string +#define wapp_str8_lit_ro_initialiser_list(STRING) {.capacity = sizeof(STRING) - 1, \ + .size = sizeof(STRING) - 1, \ + .buf = (c8 *)STRING} +#endif // !WAPP_PLATFORM_CPP + +/** + * Str8 allocated buffers + */ +Str8 *wapp_str8_alloc_buf(const Allocator *allocator, u64 capacity); +Str8 *wapp_str8_alloc_and_fill_buf(const Allocator *allocator, u64 capacity); +Str8 *wapp_str8_alloc_cstr(const Allocator *allocator, const char *str); +Str8 *wapp_str8_alloc_str8(const Allocator *allocator, Str8RO *str); +Str8 *wapp_str8_alloc_substr(const Allocator *allocator, Str8RO *str, u64 start, u64 end); +Str8 *wapp_str8_alloc_concat(const Allocator *allocator, Str8 *dst, Str8RO *src); +// Only needed for allocators like malloc where each allocation has to be freed on its own. +// No need to use it for allocators like Arena. +void wapp_str8_dealloc_buf(const Allocator *allocator, Str8 **str); + +/** + * Str8 utilities + */ +c8 wapp_str8_get(Str8RO *str, u64 index); +void wapp_str8_set(Str8 *str, u64 index, c8 c); +void wapp_str8_push_back(Str8 *str, c8 c); +b8 wapp_str8_equal(Str8RO *s1, Str8RO *s2); +b8 wapp_str8_equal_to_count(Str8RO* s1, Str8RO* s2, u64 count); +Str8 wapp_str8_slice(Str8RO *str, u64 start, u64 end); +void wapp_str8_concat_capped(Str8 *dst, Str8RO *src); +void wapp_str8_copy_cstr_capped(Str8 *dst, const char *src); +void wapp_str8_copy_str8_capped(Str8 *dst, Str8RO *src); +void wapp_str8_copy_to_cstr(char *dst, Str8RO *src, u64 dst_capacity); +void wapp_str8_format(Str8 *dst, const char *format, ...); +void wapp_str8_to_lower(Str8 *dst, Str8RO *src); +void wapp_str8_to_upper(Str8 *dst, Str8RO *src); +void wapp_str8_from_bytes(Str8 *dst, const U8Array src); + +/** + * Str8 find functions + */ +i64 wapp_str8_find(Str8RO *str, Str8RO substr); +i64 wapp_str8_rfind(Str8RO *str, Str8RO substr); + +/** + * Str8 split and join + */ +#define wapp_str8_split(ALLOCATOR, STR, DELIMITER) wapp_str8_split_with_max(ALLOCATOR, STR, DELIMITER, -1) +#define wapp_str8_rsplit(ALLOCATOR, STR, DELIMITER) wapp_str8_rsplit_with_max(ALLOCATOR, STR, DELIMITER, -1) +Str8List *wapp_str8_split_with_max(const Allocator *allocator, Str8RO *str, Str8RO *delimiter, i64 max_splits); +Str8List *wapp_str8_rsplit_with_max(const Allocator *allocator, Str8RO *str, Str8RO *delimiter, i64 max_splits); +Str8 *wapp_str8_join(const Allocator *allocator, const Str8List *list, Str8RO *delimiter); + +/** + * Str8 list utilities + */ +u64 wapp_str8_list_total_size(const Str8List *list); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !STR8_H diff --git a/wapp/base/wapp_base.c b/wapp/base/wapp_base.c new file mode 100644 index 0000000..8f22291 --- /dev/null +++ b/wapp/base/wapp_base.c @@ -0,0 +1,14 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_BASE_C +#define WAPP_BASE_C + +#include "wapp_base.h" +#include "array/array.c" +#include "dbl_list/dbl_list.c" +#include "queue/queue.c" +#include "mem/allocator/mem_allocator.c" +#include "mem/utils/mem_utils.c" +#include "strings/str8/str8.c" + +#endif // !WAPP_BASE_C diff --git a/wapp/base/wapp_base.h b/wapp/base/wapp_base.h new file mode 100644 index 0000000..4c48c1d --- /dev/null +++ b/wapp/base/wapp_base.h @@ -0,0 +1,14 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_BASE_H +#define WAPP_BASE_H + +#include "array/array.h" +#include "dbl_list/dbl_list.h" +#include "queue/queue.h" +#include "mem/allocator/mem_allocator.h" +#include "mem/utils/mem_utils.h" +#include "strings/str8/str8.h" +#include "../common/wapp_common.h" + +#endif // !WAPP_BASE_H diff --git a/wapp/common/aliases/aliases.h b/wapp/common/aliases/aliases.h new file mode 100644 index 0000000..b51b135 --- /dev/null +++ b/wapp/common/aliases/aliases.h @@ -0,0 +1,67 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef ALIASES_H +#define ALIASES_H + +#include "../platform/platform.h" +#include + +#if defined(WAPP_PLATFORM_C) && WAPP_PLATFORM_C_VERSION >= WAPP_PLATFORM_C11_VERSION && !defined(WAPP_PLATFORM_APPLE) + #include + + #if WAPP_PLATFORM_C_VERSION >= WAPP_PLATFORM_C23_VERSION + typedef char8_t c8; + #else + typedef uint8_t c8; + #endif // !WAPP_PLATFORM_C23_VERSION + + typedef char16_t c16; + typedef char32_t c32; +#else + typedef uint8_t c8; + typedef uint16_t c16; + typedef uint32_t c32; +#endif // !WAPP_PLATFORM_C + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef uint8_t b8; + +#ifndef WAPP_PLATFORM_CPP + +#ifndef false +#define false (b8)0 +#endif // !false + +#ifndef true +#define true (b8)1 +#endif // !true + +#endif // !WAPP_PLATFORM_CPP + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef float f32; +typedef double f64; +typedef long double f128; + +typedef uintptr_t uptr; +typedef intptr_t iptr; + +#define wapp_extern extern +#define wapp_intern static +#define wapp_persist static + +#ifdef WAPP_PLATFORM_CPP +#define wapp_class_mem static +#define BEGIN_C_LINKAGE wapp_extern "C" { +#define END_C_LINKAGE } +#endif // WAPP_PLATFORM_CPP + +#endif // !ALIASES_H diff --git a/wapp/common/assert/assert.h b/wapp/common/assert/assert.h new file mode 100644 index 0000000..e7ffb3c --- /dev/null +++ b/wapp/common/assert/assert.h @@ -0,0 +1,61 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_ASSERT_H +#define WAPP_ASSERT_H + +#include "../aliases/aliases.h" +#include "../platform/platform.h" +#include +#include +#include + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#define wapp_static_assert(EXPR, MSG) wapp_extern char ASSERTION_FAILED[EXPR ? 1 : -1] + +#ifndef WAPP_NO_RUNTIME_ASSERT + #define wapp_runtime_assert(EXPR, MSG) __wapp_runtime_assert(EXPR, MSG) +#else + #define wapp_runtime_assert(EXPR, MSG) +#endif + +#ifdef WAPP_DEBUG_ASSERT + #define wapp_debug_assert(EXPR, MSG) wapp_runtime_assert(EXPR, MSG) +#else + #define wapp_debug_assert(EXPR, MSG) +#endif + +#ifdef WAPP_PLATFORM_WINDOWS +#define __wapp_runtime_assert(EXPR, MSG) do { \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + if (!(EXPR)) { \ + __pragma(warning(pop)) \ + __runtime_assert_failed(EXPR, MSG); \ + } \ +} while(false) +#else +#define __wapp_runtime_assert(EXPR, MSG) do { \ + if (!(EXPR)) { \ + __runtime_assert_failed(EXPR, MSG); \ + } \ +} while(false) +#endif // !WAPP_PLATFORM_WINDOWS + +#define __runtime_assert_failed(EXPR, MSG) do { \ + fprintf( \ + stderr, \ + "%s:%d (In function `%s`): Assertion failed (%" PRIu32 ")\nDiagnostic: %s\n\n", \ + __FILE__, __LINE__, __func__, \ + EXPR, MSG \ + ); \ + abort(); \ +} while(false) + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !WAPP_ASSERT_H diff --git a/wapp/common/misc/misc_utils.h b/wapp/common/misc/misc_utils.h new file mode 100644 index 0000000..6608fe8 --- /dev/null +++ b/wapp/common/misc/misc_utils.h @@ -0,0 +1,63 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MISC_UTILS_H +#define MISC_UTILS_H + +#include "../aliases/aliases.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#define KiB(SIZE) (((u64)SIZE) << 10) +#define MiB(SIZE) (((u64)SIZE) << 20) +#define GiB(SIZE) (((u64)SIZE) << 30) +#define TiB(SIZE) (((u64)SIZE) << 40) +#define PiB(SIZE) (((u64)SIZE) << 50) +#define EiB(SIZE) (((u64)SIZE) << 60) + +#define KB(SIZE) (((u64)SIZE) * 1000llu) +#define MB(SIZE) (KB(SIZE) * 1000llu) +#define GB(SIZE) (MB(SIZE) * 1000llu) +#define TB(SIZE) (GB(SIZE) * 1000llu) +#define PB(SIZE) (TB(SIZE) * 1000llu) +#define EB(SIZE) (PB(SIZE) * 1000llu) + +#define wapp_misc_utils_reserve_padding(SIZE) u8 reserved_padding[sizeof(void *) - ((SIZE) % sizeof(void *))] + +#define U64_RSHIFT_OR_1(X) (((u64)X) | (((u64)X) >> 1)) +#define U64_RSHIFT_OR_2(X) (((u64)X) | (((u64)X) >> 2)) +#define U64_RSHIFT_OR_4(X) (((u64)X) | (((u64)X) >> 4)) +#define U64_RSHIFT_OR_8(X) (((u64)X) | (((u64)X) >> 8)) +#define U64_RSHIFT_OR_16(X) (((u64)X) | (((u64)X) >> 16)) +#define U64_RSHIFT_OR_32(X) (((u64)X) | (((u64)X) >> 32)) +#define wapp_misc_utils_u64_round_up_pow2(X) ( \ + ( \ + U64_RSHIFT_OR_32( \ + U64_RSHIFT_OR_16( \ + U64_RSHIFT_OR_8( \ + U64_RSHIFT_OR_4( \ + U64_RSHIFT_OR_2( \ + U64_RSHIFT_OR_1(X - 1) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) + 1 \ +) + +#define wapp_is_power_of_two(NUM) ((NUM & (NUM - 1)) == 0) +#define wapp_pointer_offset(PTR, OFFSET) ((void *)((uptr)(PTR) + (OFFSET))) + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE + +#include + +#define wapp_misc_utils_va_args_count(T, ...) (std::tuple_size::value) +#else +#define wapp_misc_utils_va_args_count(T, ...) (sizeof((T[]){__VA_ARGS__})/sizeof(T)) +#endif // !WAPP_PLATFORM_CPP + +#endif // !MISC_UTILS_H diff --git a/wapp/common/platform/platform.h b/wapp/common/platform/platform.h new file mode 100644 index 0000000..907b696 --- /dev/null +++ b/wapp/common/platform/platform.h @@ -0,0 +1,114 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef PLATFORM_H +#define PLATFORM_H + +#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 + #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 + +#ifdef __cplusplus + #define WAPP_PLATFORM_CPP + #define WAPP_PLATFORM_CPP_VERSION __cplusplus + #define WAPP_PLATFORM_CPP98_VERSION 199711L + #define WAPP_PLATFORM_CPP11_VERSION 201103L + #define WAPP_PLATFORM_CPP14_VERSION 201402L + #define WAPP_PLATFORM_CPP17_VERSION 201703L + #define WAPP_PLATFORM_CPP20_VERSION 202002L + #define WAPP_PLATFORM_CPP23_VERSION 202302L + + #if WAPP_PLATFORM_CPP_VERSION == WAPP_PLATFORM_CPP98_VERSION + #define WAPP_PLATFORM_CPP98 + #elif WAPP_PLATFORM_CPP_VERSION == WAPP_PLATFORM_CPP11_VERSION + #define WAPP_PLATFORM_CPP11 + #elif WAPP_PLATFORM_CPP_VERSION == WAPP_PLATFORM_CPP14_VERSION + #define WAPP_PLATFORM_CPP14 + #elif WAPP_PLATFORM_CPP_VERSION == WAPP_PLATFORM_CPP17_VERSION + #define WAPP_PLATFORM_CPP17 + #elif WAPP_PLATFORM_CPP_VERSION == WAPP_PLATFORM_CPP20_VERSION + #define WAPP_PLATFORM_CPP20 + #elif WAPP_PLATFORM_CPP_VERSION == WAPP_PLATFORM_CPP23_VERSION + #define WAPP_PLATFORM_CPP23 + #else + #error "Unrecognised C++ version" + #endif +#else + #define WAPP_PLATFORM_C + + #if defined(__STDC_VERSION__) + #define WAPP_PLATFORM_C_VERSION __STDC_VERSION__ + #define WAPP_PLATFORM_C99_VERSION 199901L + #define WAPP_PLATFORM_C11_VERSION 201112L + #define WAPP_PLATFORM_C17_VERSION 201710L + #define WAPP_PLATFORM_C23_VERSION 202311L + + #if WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C99_VERSION + #define WAPP_PLATFORM_C99 + #elif WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C11_VERSION + #define WAPP_PLATFORM_C11 + #elif WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C17_VERSION + #define WAPP_PLATFORM_C17 + #elif WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C23_VERSION + #define WAPP_PLATFORM_C23 + #else + #error "Unrecognised C version" + #endif + #else + #define WAPP_PLATFORM_C89 + #endif +#endif // !__cplusplus + +#endif // !PLATFORM_H diff --git a/wapp/common/wapp_common.h b/wapp/common/wapp_common.h new file mode 100644 index 0000000..a126b0a --- /dev/null +++ b/wapp/common/wapp_common.h @@ -0,0 +1,11 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_COMMON_H +#define WAPP_COMMON_H + +#include "aliases/aliases.h" +#include "assert/assert.h" +#include "misc/misc_utils.h" +#include "platform/platform.h" + +#endif // !WAPP_COMMON_H diff --git a/wapp/log/log.c b/wapp/log/log.c new file mode 100644 index 0000000..57420c2 --- /dev/null +++ b/wapp/log/log.c @@ -0,0 +1,102 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "log.h" +#include "../common/aliases/aliases.h" +#include "../common/assert/assert.h" +#include "../os/file/file.h" +#include "../common/misc/misc_utils.h" + +#define LOG_LEVEL_STR_LENGTH 8 +#define LOG_PREFIX_BUF_LENGTH 16 + +typedef struct { + WFile *outlog; + WFile *errlog; + LogLevel level; + + wapp_misc_utils_reserve_padding(2 * sizeof(WFile *) + sizeof(LogLevel)); +} LogConfig; + +wapp_intern LogConfig LOG_CONFIG = { + .level = WAPP_LOG_DEBUG, +}; +wapp_intern Str8RO LOG_LEVEL_STRINGS[COUNT_LOG_LEVEL] = { + [WAPP_LOG_FATAL] = wapp_str8_lit_ro_initialiser_list("[ FATAL ] "), + [WAPP_LOG_CRITICAL] = wapp_str8_lit_ro_initialiser_list("[ CRITICAL ] "), + [WAPP_LOG_ERROR] = wapp_str8_lit_ro_initialiser_list("[ ERROR ] "), + [WAPP_LOG_WARNING] = wapp_str8_lit_ro_initialiser_list("[ WARNING ] "), + [WAPP_LOG_INFO] = wapp_str8_lit_ro_initialiser_list("[ INFO ] "), + [WAPP_LOG_DEBUG] = wapp_str8_lit_ro_initialiser_list("[ DEBUG ] "), +}; + +wapp_intern void _write_log_line(WFile *fp, const Logger *logger, Str8 msg, LogLevel level); + +void wapp_log_set_level(LogLevel level) { + LOG_CONFIG.level = level; +} + +void wapp_log_configure(WFile *outlog, WFile *errlog, LogLevel level) { + LOG_CONFIG.outlog = outlog; + LOG_CONFIG.errlog = errlog; + LOG_CONFIG.level = level; +} + +Logger wapp_log_make_logger(Str8 name) { + return (Logger){ .name = name }; +} + +void wapp_log_debug(const Logger *logger, Str8 msg) { + wapp_debug_assert(logger != NULL, "`logger` should not be NULL"); + if (LOG_CONFIG.level < WAPP_LOG_DEBUG) { return; } + + WFile *fp = LOG_CONFIG.outlog != NULL ? LOG_CONFIG.outlog : wapp_file_stdout(); + _write_log_line(fp, logger, msg, WAPP_LOG_DEBUG); +} + +void wapp_log_info(const Logger *logger, Str8 msg) { + wapp_debug_assert(logger != NULL, "`logger` should not be NULL"); + if (LOG_CONFIG.level < WAPP_LOG_INFO) { return; } + + WFile *fp = LOG_CONFIG.outlog != NULL ? LOG_CONFIG.outlog : wapp_file_stdout(); + _write_log_line(fp, logger, msg, WAPP_LOG_INFO); +} + +void wapp_log_warning(const Logger *logger, Str8 msg) { + wapp_debug_assert(logger != NULL, "`logger` should not be NULL"); + if (LOG_CONFIG.level < WAPP_LOG_WARNING) { return; } + + WFile *fp = LOG_CONFIG.outlog != NULL ? LOG_CONFIG.outlog : wapp_file_stdout(); + _write_log_line(fp, logger, msg, WAPP_LOG_WARNING); +} + +void wapp_log_error(const Logger *logger, Str8 msg) { + wapp_debug_assert(logger != NULL, "`logger` should not be NULL"); + if (LOG_CONFIG.level < WAPP_LOG_ERROR) { return; } + + WFile *fp = LOG_CONFIG.errlog != NULL ? LOG_CONFIG.errlog : wapp_file_stderr(); + _write_log_line(fp, logger, msg, WAPP_LOG_ERROR); +} + +void wapp_log_critical(const Logger *logger, Str8 msg) { + wapp_debug_assert(logger != NULL, "`logger` should not be NULL"); + if (LOG_CONFIG.level < WAPP_LOG_CRITICAL) { return; } + + WFile *fp = LOG_CONFIG.errlog != NULL ? LOG_CONFIG.errlog : wapp_file_stderr(); + _write_log_line(fp, logger, msg, WAPP_LOG_CRITICAL); +} + +void wapp_log_fatal(const Logger *logger, Str8 msg) { + wapp_debug_assert(logger != NULL, "`logger` should not be NULL"); + if (LOG_CONFIG.level < WAPP_LOG_FATAL) { return; } + + WFile *fp = LOG_CONFIG.errlog != NULL ? LOG_CONFIG.errlog : wapp_file_stderr(); + _write_log_line(fp, logger, msg, WAPP_LOG_FATAL); +} + +wapp_intern void _write_log_line(WFile *fp, const Logger *logger, Str8 msg, LogLevel level) { + wapp_file_write((void *)LOG_LEVEL_STRINGS[level].buf, fp, LOG_LEVEL_STRINGS[level].size); + wapp_file_write((void *)logger->name.buf, fp, logger->name.size); + wapp_file_write((void *)": ", fp, 2); + wapp_file_write((void *)msg.buf, fp, msg.size); + wapp_file_write((void *)"\n", fp, 1); +} diff --git a/wapp/log/log.h b/wapp/log/log.h new file mode 100644 index 0000000..f6605d1 --- /dev/null +++ b/wapp/log/log.h @@ -0,0 +1,34 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef LOG_H +#define LOG_H + +#include "../os/file/file.h" +#include "../base/strings/str8/str8.h" + +typedef enum { + WAPP_LOG_FATAL, + WAPP_LOG_CRITICAL, + WAPP_LOG_ERROR, + WAPP_LOG_WARNING, + WAPP_LOG_INFO, + WAPP_LOG_DEBUG, + + COUNT_LOG_LEVEL, +} LogLevel; + +typedef struct { + Str8 name; +} Logger; + +void wapp_log_set_level(LogLevel level); +void wapp_log_configure(WFile *outlog, WFile *errlog, LogLevel level); +Logger wapp_log_make_logger(Str8 name); +void wapp_log_debug(const Logger *logger, Str8 msg); +void wapp_log_info(const Logger *logger, Str8 msg); +void wapp_log_warning(const Logger *logger, Str8 msg); +void wapp_log_error(const Logger *logger, Str8 msg); +void wapp_log_critical(const Logger *logger, Str8 msg); +void wapp_log_fatal(const Logger *logger, Str8 msg); + +#endif // !LOG_H diff --git a/wapp/log/wapp_log.c b/wapp/log/wapp_log.c new file mode 100644 index 0000000..64376ed --- /dev/null +++ b/wapp/log/wapp_log.c @@ -0,0 +1,10 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_LOG_C +#define WAPP_LOG_C + +#include "log.c" +#include "../base/wapp_base.c" +#include "../os/wapp_os.c" + +#endif // !WAPP_LOG_C diff --git a/wapp/log/wapp_log.h b/wapp/log/wapp_log.h new file mode 100644 index 0000000..a8dcdcd --- /dev/null +++ b/wapp/log/wapp_log.h @@ -0,0 +1,11 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_LOG_H +#define WAPP_LOG_H + +#include "log.h" +#include "../common/wapp_common.h" +#include "../base/wapp_base.h" +#include "../os/wapp_os.h" + +#endif // !WAPP_LOG_H diff --git a/wapp/os/allocators/arena/mem_arena.c b/wapp/os/allocators/arena/mem_arena.c new file mode 100644 index 0000000..314dcb0 --- /dev/null +++ b/wapp/os/allocators/arena/mem_arena.c @@ -0,0 +1,192 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "mem_arena.h" +#include "../../mem/mem_os.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/assert/assert.h" +#include "../../../common/misc/misc_utils.h" +#include "../../../base/mem/utils/mem_utils.h" +#include + +#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 KiB(16) // Allocate minimum of 4 pages + +typedef enum { + ARENA_STORAGE_TYPE_ALLOCATED, + ARENA_STORAGE_TYPE_BUFFER, +} ArenaStorageType; + +struct Arena { + u8 *buf; + u8 *offset; + u8 *prev_offset; + u64 capacity; + ArenaStorageType type; + b8 committed; + + wapp_misc_utils_reserve_padding(sizeof(u8 *) * 3 + sizeof(u64) + sizeof(ArenaStorageType) + sizeof(b8)); +}; + +b8 wapp_mem_arena_init_buffer(Arena **arena, u8 *buffer, u64 buffer_size) { + if (!arena || *arena || buffer_size < sizeof(Arena)) { + return false; + } + + *arena = (Arena *)buffer; + Arena *arena_ptr = *arena; + + arena_ptr->buf = (u8 *)(arena_ptr + 1); + arena_ptr->offset = arena_ptr->buf; + arena_ptr->prev_offset = NULL; + arena_ptr->capacity = buffer_size - sizeof(Arena); + arena_ptr->type = ARENA_STORAGE_TYPE_BUFFER; + arena_ptr->committed = true; + + return true; +} + +b8 wapp_mem_arena_init_allocated_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, b8 zero_buffer) { + if (!arena || *arena || base_capacity == 0) { + return false; + } + + u64 size = sizeof(Arena) + (base_capacity >= ARENA_MINIMUM_CAPACITY ? base_capacity : ARENA_MINIMUM_CAPACITY); + u64 alloc_size = wapp_misc_utils_u64_round_up_pow2(size); + u8 *allocated = (u8 *)wapp_os_mem_alloc(NULL, alloc_size, WAPP_MEM_ACCESS_READ_WRITE, flags, + zero_buffer ? WAPP_MEM_INIT_INITIALISED : WAPP_MEM_INIT_UNINITIALISED); + if (!allocated) { + return false; + } + + b8 committed = (flags & WAPP_MEM_ALLOC_COMMIT) == WAPP_MEM_ALLOC_COMMIT; + +#ifdef WAPP_PLATFORM_WINDOWS + if (!committed) { + wapp_os_mem_alloc(allocated, sizeof(Arena), WAPP_MEM_ACCESS_READ_WRITE, WAPP_MEM_ALLOC_COMMIT, + WAPP_MEM_INIT_INITIALISED); + } +#endif // ifdef WAPP_PLATFORM_WINDOWS + + if (!wapp_mem_arena_init_buffer(arena, allocated, alloc_size)) { + wapp_mem_arena_destroy(arena); + return false; + } + + Arena *arena_ptr = *arena; + arena_ptr->type = ARENA_STORAGE_TYPE_ALLOCATED; + arena_ptr->committed = committed; + + 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) { + wapp_debug_assert(arena != NULL, "`arena` should not be 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; + +#ifdef WAPP_PLATFORM_WINDOWS + if (arena->type == ARENA_STORAGE_TYPE_ALLOCATED && !(arena->committed)) { + wapp_os_mem_alloc(alloc_start, (uptr)(arena->offset) - (uptr)(alloc_start), + WAPP_MEM_ACCESS_READ_WRITE, WAPP_MEM_ALLOC_COMMIT, + WAPP_MEM_INIT_UNINITIALISED); + } +#endif // ifdef WAPP_PLATFORM_WINDOWS + + memset(output, 0, size); + + return (void *)output; +} + +void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size) { + wapp_debug_assert(arena != NULL, "`arena` should not be NULL"); + + if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset || + arena->offset + new_size >= arena->buf + arena->capacity) { + return NULL; + } + + void *new_ptr = wapp_mem_arena_alloc(arena, new_size); + if (!new_ptr) { + return NULL; + } + + u64 copy_size = new_size <= old_size ? new_size : old_size; + memcpy(new_ptr, ptr, copy_size); + + return new_ptr; +} + +void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment) { + wapp_debug_assert(arena != NULL, "`arena` should not be NULL"); + + if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset || + arena->offset + new_size >= arena->buf + arena->capacity) { + return NULL; + } + + void *new_ptr = wapp_mem_arena_alloc_aligned(arena, new_size, alignment); + if (!new_ptr) { + return NULL; + } + + u64 copy_size = new_size <= old_size ? new_size : old_size; + memcpy(new_ptr, ptr, copy_size); + + return new_ptr; +} + +void wapp_mem_arena_temp_begin(Arena *arena) { + wapp_debug_assert(arena != NULL, "`arena` should not be NULL"); + + if (arena->prev_offset != NULL) { + return; + } + + arena->prev_offset = arena->offset; +} + +void wapp_mem_arena_temp_end(Arena *arena) { + wapp_debug_assert(arena != NULL, "`arena` should not be NULL"); + + if (arena->prev_offset == NULL) { + return; + } + + arena->offset = arena->prev_offset; + arena->prev_offset = NULL; +} + +void wapp_mem_arena_clear(Arena *arena) { + wapp_debug_assert(arena != NULL, "`arena` should not be NULL"); + + memset(arena->buf, 0, arena->offset - arena->buf); + arena->offset = arena->buf; +} + +void wapp_mem_arena_destroy(Arena **arena) { + wapp_debug_assert(arena != NULL && (*arena) != NULL, "`arena` double pointer is not valid"); + + Arena *arena_ptr = *arena; + + if (arena_ptr->type == ARENA_STORAGE_TYPE_ALLOCATED) { + wapp_os_mem_free(*arena, sizeof(Arena) + arena_ptr->capacity); + } + + *arena = NULL; +} diff --git a/wapp/os/allocators/arena/mem_arena.h b/wapp/os/allocators/arena/mem_arena.h new file mode 100644 index 0000000..3859f39 --- /dev/null +++ b/wapp/os/allocators/arena/mem_arena.h @@ -0,0 +1,45 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_ARENA_H +#define MEM_ARENA_H + +#include "../../mem/mem_os.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef struct Arena Arena; + +#define wapp_mem_arena_init_allocated(arena_dptr, base_capacity) \ + (wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, false)) +#define wapp_mem_arena_init_allocated_commit(arena_dptr, base_capacity) \ + (wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, false)) +#define wapp_mem_arena_init_allocated_zero(arena_dptr, base_capacity) \ + (wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, true)) +#define wapp_mem_arena_init_allocated_commit_and_zero(arena_dptr, base_capacity) \ + (wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, true)) + +/** + * Arena initialisation function. `wapp_mem_arena_init_allocated_custom` provides the most + * control over how the Arena is initialised. Wrapper macros are provided for + * easier use. + */ +b8 wapp_mem_arena_init_allocated_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, b8 zero_buffer); +b8 wapp_mem_arena_init_buffer(Arena **arena, u8 *buffer, u64 buffer_size); +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 old_size, u64 new_size); +void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment); +void wapp_mem_arena_temp_begin(Arena *arena); +void wapp_mem_arena_temp_end(Arena *arena); +void wapp_mem_arena_clear(Arena *arena); +void wapp_mem_arena_destroy(Arena **arena); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_ARENA_H diff --git a/wapp/os/allocators/arena/mem_arena_allocator.c b/wapp/os/allocators/arena/mem_arena_allocator.c new file mode 100644 index 0000000..fa020db --- /dev/null +++ b/wapp/os/allocators/arena/mem_arena_allocator.c @@ -0,0 +1,87 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "mem_arena_allocator.h" +#include "mem_arena.h" +#include "../../mem/mem_os.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/assert/assert.h" + +wapp_intern void initialise_arena_allocator(Allocator *allocator); +wapp_intern void *mem_arena_alloc(u64 size, void *alloc_obj); +wapp_intern void *mem_arena_alloc_aligned(u64 size, u64 alignment, void *alloc_obj); +wapp_intern void *mem_arena_realloc(void *ptr, u64 old_size, u64 new_size, void *alloc_obj); +wapp_intern void *mem_arena_realloc_aligned(void *ptr, u64 old_size, u64 new_size, u64 alignment, + void *alloc_obj); + +Allocator wapp_mem_arena_allocator_init_with_buffer(u8 *buffer, u64 buffer_size) { + Allocator allocator = {0}; + b8 initialised = wapp_mem_arena_init_buffer((Arena **)(&allocator.obj), buffer, buffer_size); + if (!initialised) { + return allocator; + } + + initialise_arena_allocator(&allocator); + + return allocator; +} + +Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags flags, b8 zero_buffer) { + Allocator allocator = {0}; + b8 initialised = wapp_mem_arena_init_allocated_custom((Arena **)(&allocator.obj), base_capacity, flags, zero_buffer); + if (!initialised) { + return allocator; + } + + initialise_arena_allocator(&allocator); + + return allocator; +} + +void wapp_mem_arena_allocator_temp_begin(const Allocator *allocator) { + wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL"); + wapp_mem_arena_temp_begin((Arena *)(allocator->obj)); +} + +void wapp_mem_arena_allocator_temp_end(const Allocator *allocator) { + wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL"); + wapp_mem_arena_temp_end((Arena *)(allocator->obj)); +} + +void wapp_mem_arena_allocator_clear(Allocator *allocator) { + wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL"); + wapp_mem_arena_clear((Arena *)(allocator->obj)); +} + +void wapp_mem_arena_allocator_destroy(Allocator *allocator) { + wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL"); + wapp_mem_arena_destroy((Arena **)(&(allocator->obj))); + *allocator = (Allocator){0}; +} + +wapp_intern void initialise_arena_allocator(Allocator *allocator) { + allocator->alloc = mem_arena_alloc; + allocator->alloc_aligned = mem_arena_alloc_aligned; + allocator->realloc = mem_arena_realloc; + allocator->realloc_aligned = mem_arena_realloc_aligned; +} + +wapp_intern void *mem_arena_alloc(u64 size, void *alloc_obj) { + Arena *arena = (Arena *)alloc_obj; + return wapp_mem_arena_alloc(arena, size); +} + +wapp_intern 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); +} + +wapp_intern void *mem_arena_realloc(void *ptr, u64 old_size, u64 new_size, void *alloc_obj) { + Arena *arena = (Arena *)alloc_obj; + return wapp_mem_arena_realloc(arena, ptr, old_size, new_size); +} + +wapp_intern void *mem_arena_realloc_aligned(void *ptr, u64 old_size, u64 new_size, u64 alignment, + void *alloc_obj) { + Arena *arena = (Arena *)alloc_obj; + return wapp_mem_arena_realloc_aligned(arena, ptr, old_size, new_size, alignment); +} diff --git a/wapp/os/allocators/arena/mem_arena_allocator.h b/wapp/os/allocators/arena/mem_arena_allocator.h new file mode 100644 index 0000000..bd88379 --- /dev/null +++ b/wapp/os/allocators/arena/mem_arena_allocator.h @@ -0,0 +1,46 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_ARENA_ALLOCATOR_H +#define MEM_ARENA_ALLOCATOR_H + +#include "../../mem/mem_os.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" +#include "../../../base/mem/allocator/mem_allocator.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#define wapp_mem_arena_allocator_init(base_capacity) \ + (wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE, false)) +#define wapp_mem_arena_allocator_init_commit(base_capacity) \ + (wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, false)) +#define wapp_mem_arena_allocator_init_zero(base_capacity) \ + (wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE, true)) +#define wapp_mem_arena_allocator_init_commit_and_zero(base_capacity) \ + (wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, true)) + +/** + * Wraps an Arena in an Allocator object. It attempts to initialise the Arena + * and, if successful, defines the operations supported by it to be used by the + * Allocator. + * + * An Arena allocator only supports normal allocation and aligned allocation. + * Reallocation, aligned reallocation and freeing aren't implemented. + * + * The `wapp_mem_arena_allocator_init_custom` provides the most control over how + * the Arena is initialised. Wrapper macros are provided for easier use. + */ +Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags flags, b8 zero_buffer); +Allocator wapp_mem_arena_allocator_init_with_buffer(u8 *buffer, u64 buffer_size); +void wapp_mem_arena_allocator_temp_begin(const Allocator *allocator); +void wapp_mem_arena_allocator_temp_end(const Allocator *allocator); +void wapp_mem_arena_allocator_clear(Allocator *allocator); +void wapp_mem_arena_allocator_destroy(Allocator *allocator); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_ARENA_ALLOCATOR_H diff --git a/wapp/os/cpath/cpath.c b/wapp/os/cpath/cpath.c new file mode 100644 index 0000000..5449518 --- /dev/null +++ b/wapp/os/cpath/cpath.c @@ -0,0 +1,136 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "cpath.h" +#include "../allocators/arena/mem_arena_allocator.h" +#include "../../common/aliases/aliases.h" +#include "../../common/misc/misc_utils.h" +#include "../../base/dbl_list/dbl_list.h" +#include "../../base/mem/allocator/mem_allocator.h" +#include "../../base/strings/str8/str8.h" +#include +#include +#include + +u32 wapp_cpath_join_path(Str8 *dst, const Str8List *parts) { + if (!dst || !parts) { + return CPATH_JOIN_INVALID_ARGS; + } + + if (parts->node_count == 0) { + return CPATH_JOIN_EMPTY_PARTS; + } + + Str8 separator = wapp_str8_buf(4); + wapp_str8_push_back(&separator, WAPP_PATH_SEP); + + u64 required_capacity = parts->node_count * separator.size + wapp_str8_list_total_size(parts); + if (dst->capacity < required_capacity) { + return CPATH_JOIN_INSUFFICIENT_DST_CAPACITY; + } + + // Handle first node + Str8 *first_node = wapp_dbl_list_get(Str8, parts, 0); + wapp_str8_copy_str8_capped(dst, first_node); + + // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of + // MSVC Spectre mitigation warnings + Str8 *node = first_node; + u64 node_index = 1; + b8 running = node_index < parts->node_count; + while (running) { + node = wapp_dbl_list_get(Str8, parts, node_index); + if (node->size == 0) { + goto CPATH_JOIN_LOOP_END; + } + + if (dst->size > 0) { + char dst_last = wapp_str8_get(dst, dst->size - 1); + char node_start = wapp_str8_get(node, 0); + b8 add_path_sep = dst_last != WAPP_PATH_SEP && node_start != WAPP_PATH_SEP; + + if (add_path_sep) { + wapp_str8_concat_capped(dst, &separator); + } + } + + wapp_str8_concat_capped(dst, node); + +CPATH_JOIN_LOOP_END: + ++node_index; + running = node_index < parts->node_count; + } + + return CPATH_JOIN_SUCCESS; +} + +Str8 *dirup(const Allocator *allocator, Str8RO *path, u64 levels) { + Str8 *output = NULL; + if (!allocator || !path) { + goto RETURN_DIRUP; + } + + b8 absolute = wapp_str8_get(path, 0) == WAPP_PATH_SEP; + Str8 separator = wapp_str8_buf(4); + wapp_str8_push_back(&separator, WAPP_PATH_SEP); + + if (path->size == 0) { + output = wapp_str8_alloc_buf(allocator, 16); + if (!output) { + goto RETURN_DIRUP; + } + + wapp_str8_push_back(output, absolute ? WAPP_PATH_SEP : '.'); + goto RETURN_DIRUP; + } + + if (levels < 1) { + output = wapp_str8_alloc_str8(allocator, path); + goto RETURN_DIRUP; + } + + Allocator tmp_arena = wapp_mem_arena_allocator_init(MiB(8)); + if (wapp_mem_allocator_invalid(&tmp_arena)) { + goto RETURN_DIRUP; + } + + Str8List *parts = wapp_str8_split(&tmp_arena, path, &separator); + if (!parts) { + goto RETURN_DIRUP; + } + + if (levels >= parts->node_count) { + output = wapp_str8_alloc_buf(allocator, 16); + if (!output) { + goto LIST_CLEANUP_DIRUP; + } + + wapp_str8_push_back(output, absolute ? WAPP_PATH_SEP : '.'); + } else { + for (u64 i = 0; i < levels; ++i) { + wapp_dbl_list_pop_back(Str8, parts); + } + + u64 alignment = sizeof(void *) * 2; + u64 alloc_size = wapp_str8_list_total_size(parts) + parts->node_count * separator.size; + u64 modulo = alloc_size & (alignment - 1); + alloc_size += alignment - modulo; + + output = wapp_str8_alloc_buf(allocator, alloc_size); + if (output) { + if (absolute) { + wapp_str8_push_back(output, WAPP_PATH_SEP); + } + + Str8 *joined = wapp_str8_join(&tmp_arena, parts, &separator); + if (joined) { + wapp_str8_concat_capped(output, joined); + } + } + } + +LIST_CLEANUP_DIRUP: + wapp_mem_arena_allocator_destroy(&tmp_arena); + +RETURN_DIRUP: + return output; +} diff --git a/wapp/os/cpath/cpath.h b/wapp/os/cpath/cpath.h new file mode 100644 index 0000000..d481822 --- /dev/null +++ b/wapp/os/cpath/cpath.h @@ -0,0 +1,45 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef CPATH_H +#define CPATH_H + +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" +#include "../../base/mem/allocator/mem_allocator.h" +#include "../../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#ifdef WAPP_PLATFORM_POSIX +#include +#define WAPP_PATH_SEP '/' +#define WAPP_PATH_MAX PATH_MAX +#elif defined(WAPP_PLATFORM_WINDOWS) +#define WIN32_LEAN_AND_MEAN +#include +#define WAPP_PATH_SEP '\\' +#define WAPP_PATH_MAX MAX_PATH +#else +#error "Unrecognised platform" +#endif + +#define wapp_cpath_dirname(ALLOCATOR, PATH) dirup(ALLOCATOR, PATH, 1) +#define wapp_cpath_dirup(ALLOCATOR, PATH, COUNT) dirup(ALLOCATOR, PATH, COUNT) + +enum { + CPATH_JOIN_SUCCESS = 0, + CPATH_JOIN_INVALID_ARGS, + CPATH_JOIN_EMPTY_PARTS, + CPATH_JOIN_INSUFFICIENT_DST_CAPACITY, +}; + +u32 wapp_cpath_join_path(Str8 *dst, const Str8List *parts); +Str8 *dirup(const Allocator *allocator, Str8RO *path, u64 levels); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !CPATH_H diff --git a/wapp/os/file/file.c b/wapp/os/file/file.c new file mode 100644 index 0000000..555d8ab --- /dev/null +++ b/wapp/os/file/file.c @@ -0,0 +1,129 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "file.h" +#include "../cpath/cpath.h" +#include "../../common/assert/assert.h" +#include "../../common/aliases/aliases.h" +#include "../../base/array/array.h" +#include "../../base/strings/str8/str8.h" + +WFile *wapp_file_open(const Allocator *allocator, Str8RO *filepath, FileAccessMode mode) { + wapp_debug_assert(allocator != NULL && filepath != NULL, "`allocator` and `filepath` should not be NULL"); + wapp_debug_assert(filepath->size < WAPP_PATH_MAX, "`filepath` exceeds max path limit."); + return _file_open(allocator, filepath, mode); +} + +i64 wapp_file_get_current_position(WFile *file) { + wapp_debug_assert(file != NULL, "`file` should not be NULL."); + return _file_seek(file, 0, WAPP_SEEK_CURRENT); +} + +i64 wapp_file_seek(WFile *file, i64 offset, FileSeekOrigin origin) { + wapp_debug_assert(file != NULL, "`file` should not be NULL."); + return _file_seek(file, offset, origin); +} + +i64 wapp_file_get_length(WFile *file) { + wapp_debug_assert(file != NULL, "`file` should not be NULL."); + + i64 current = wapp_file_get_current_position(file); + + _file_seek(file, 0, WAPP_SEEK_END); + + i64 output = wapp_file_get_current_position(file); + + // Restore position + _file_seek(file, current, WAPP_SEEK_START); + + return output; +} + +u64 wapp_file_read(void *dst_buf, WFile *file, u64 byte_count) { + wapp_debug_assert(dst_buf != NULL && file != NULL, + "`dst_buf` and `file` should not be NULL."); + + i64 file_length = wapp_file_get_length(file); + if (file_length < 0) { + return 0; + } + + return _file_read(dst_buf, byte_count, file, file_length); +} + +i64 wapp_file_write(const void *src_buf, WFile *file, u64 byte_count) { + wapp_debug_assert(src_buf != NULL && file != NULL, + "`src_buf` and `file` should not be NULL."); + return _file_write(src_buf, file, byte_count); +} + +u64 wapp_file_read_array(GenericArray dst_buf, WFile *file, u64 item_count) { + wapp_debug_assert(dst_buf != NULL && file != NULL, + "`dst_buf` and `file` should not be NULL."); + + i64 _file_length = wapp_file_get_length(file); + if (_file_length < 0) { + return 0; + } + + u64 file_length = (u64)_file_length; + u64 item_size = wapp_array_item_size(dst_buf); + u64 dst_byte_capacity = wapp_array_capacity(dst_buf) * item_size; + u64 req_byte_count = item_count * item_size; + u64 copy_byte_count = 0; + + if (req_byte_count <= file_length && req_byte_count <= dst_byte_capacity) { + copy_byte_count = req_byte_count; + } else { + copy_byte_count = file_length <= dst_byte_capacity ? file_length : dst_byte_capacity; + } + + u64 byte_count = _file_read(dst_buf, copy_byte_count, file, file_length); + if (byte_count == 0) { + return 0; + } + + wapp_array_set_count(dst_buf, byte_count / item_size); + + return wapp_array_count(dst_buf); +} + +i64 wapp_file_write_array(const GenericArray src_buf, WFile *file, u64 item_count) { + wapp_debug_assert(src_buf != NULL && file != NULL, + "`src_buf` and `file` should not be NULL."); + + u64 item_size = wapp_array_item_size(src_buf); + u64 src_byte_count = wapp_array_count(src_buf) * item_size; + u64 req_byte_count = item_count * item_size; + u64 to_copy = req_byte_count <= src_byte_count ? req_byte_count : src_byte_count; + + i64 bytes_written = _file_write(src_buf, file, to_copy); + if (bytes_written < 0) { + return 0; + } + + return (u64)bytes_written / item_size; +} + +i32 wapp_file_flush(WFile *file) { + wapp_debug_assert(file != NULL, "`file` should not be NULL."); + return _file_flush(file); +} + +i32 wapp_file_close(WFile *file) { + wapp_debug_assert(file != NULL, "`file` should not be NULL."); + return _file_close(file); +} + +i32 wapp_file_rename(Str8RO *old_filepath, Str8RO *new_filepath) { + wapp_debug_assert(old_filepath != NULL && new_filepath != NULL, + "`old_filepath` and `new_filepath` should not be NULL"); + wapp_debug_assert(old_filepath->size < WAPP_PATH_MAX, "`old_filepath` exceeds max path limit."); + wapp_debug_assert(new_filepath->size < WAPP_PATH_MAX, "`new_filepath` exceeds max path limit."); + return _file_rename(old_filepath, new_filepath); +} + +i32 wapp_file_remove(Str8RO *filepath) { + wapp_debug_assert(filepath != NULL, "`filepath` should not be NULL"); + wapp_debug_assert(filepath->size < WAPP_PATH_MAX, "`filepath` exceeds max path limit."); + return _file_remove(filepath); +} diff --git a/wapp/os/file/file.h b/wapp/os/file/file.h new file mode 100644 index 0000000..48ee7e3 --- /dev/null +++ b/wapp/os/file/file.h @@ -0,0 +1,75 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef FILE_H +#define FILE_H + +#include "../../base/mem/allocator/mem_allocator.h" +#include "../../common/aliases/aliases.h" +#include "../../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef struct WFile WFile; + +typedef enum { + WAPP_ACCESS_READ, // Equivalent to r + WAPP_ACCESS_WRITE, // Equivalent to w + WAPP_ACCESS_APPEND, // Equivalent to a + WAPP_ACCESS_READ_EX, // Equivalent to r+ + WAPP_ACCESS_WRITE_EX, // Equivalent to w+ + WAPP_ACCESS_APPEND_EX, // Equivalent to a+ + WAPP_ACCESS_WRITE_FAIL_ON_EXIST, // Equivalent to wx + WAPP_ACCESS_WRITE_FAIL_ON_EXIST_EX, // Equivalent to wx+ + + FILE_ACCESS_MODE_COUNT, +} FileAccessMode; + +typedef enum { + WAPP_SEEK_START, + WAPP_SEEK_CURRENT, + WAPP_SEEK_END, + + FILE_SEEK_ORIGIN_COUNT, +} FileSeekOrigin; + +// Return value should not be cached as it's not guaranteed to remain the same. Always call +// wapp_file_stdin to get the standard input stream +wapp_extern WFile *wapp_file_stdin(void); + +// Return value should not be cached as it's not guaranteed to remain the same. Always call +// wapp_file_stdout to get the standard output stream +wapp_extern WFile *wapp_file_stdout(void); + +// Return value should not be cached as it's not guaranteed to remain the same. Always call +// wapp_file_stderr to get the standard error stream +wapp_extern WFile *wapp_file_stderr(void); + +WFile *wapp_file_open(const Allocator *allocator, Str8RO *filepath, FileAccessMode mode); +i64 wapp_file_get_current_position(WFile *file); +i64 wapp_file_seek(WFile *file, i64 offset, FileSeekOrigin origin); +i64 wapp_file_get_length(WFile *file); +u64 wapp_file_read(void *dst_buf, WFile *file, u64 byte_count); +i64 wapp_file_write(const void *src_buf, WFile *file, u64 byte_count); +u64 wapp_file_read_array(GenericArray dst_buf, WFile *file, u64 item_count); +i64 wapp_file_write_array(const GenericArray src_buf, WFile *file, u64 item_count); +i32 wapp_file_flush(WFile *file); +i32 wapp_file_close(WFile *file); +i32 wapp_file_rename(Str8RO *old_filepath, Str8RO *new_filepath); +i32 wapp_file_remove(Str8RO *filepath); + +wapp_extern WFile *_file_open(const Allocator *allocator, Str8RO *filepath, FileAccessMode mode); +wapp_extern i64 _file_seek(WFile *file, i64 offset, FileSeekOrigin origin); +wapp_extern u64 _file_read(void *dst_buf, u64 byte_count, WFile *file, u64 file_length); +wapp_extern i64 _file_write(const void *src_buf, WFile *file, u64 byte_count); +wapp_extern i32 _file_flush(WFile *file); +wapp_extern i32 _file_close(WFile *file); +wapp_extern i32 _file_rename(Str8RO *old_filepath, Str8RO *new_filepath); +wapp_extern i32 _file_remove(Str8RO *filepath); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !FILE_H diff --git a/wapp/os/file/posix/file_posix.c b/wapp/os/file/posix/file_posix.c new file mode 100644 index 0000000..345c144 --- /dev/null +++ b/wapp/os/file/posix/file_posix.c @@ -0,0 +1,134 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "file_posix.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_POSIX + +#include "../file.h" +#include "../../cpath/cpath.h" +#include "../../../common/aliases/aliases.h" +#include "../../../base/array/array.h" +#include "../../../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_APPLE +#define _FILE_OFFSET_BITS 64 +#define lseek64 lseek +#endif // !WAPP_PLATFORM_APPLE + +#define __USE_LARGEFILE64 +#include +#include +#include + +wapp_intern i32 file_flags[FILE_ACCESS_MODE_COUNT] = { + [WAPP_ACCESS_READ] = O_RDONLY, + [WAPP_ACCESS_WRITE] = O_WRONLY | O_CREAT, + [WAPP_ACCESS_APPEND] = O_WRONLY | O_APPEND | O_CREAT, + [WAPP_ACCESS_READ_EX] = O_RDWR, + [WAPP_ACCESS_WRITE_EX] = O_RDWR | O_CREAT, + [WAPP_ACCESS_APPEND_EX] = O_RDWR | O_APPEND | O_CREAT, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST] = O_WRONLY | O_CREAT | O_EXCL, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST_EX] = O_RDWR | O_CREAT | O_EXCL, +}; + +wapp_intern mode_t file_modes[FILE_ACCESS_MODE_COUNT] = { + [WAPP_ACCESS_READ] = 0, + [WAPP_ACCESS_WRITE] = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + [WAPP_ACCESS_APPEND] = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + [WAPP_ACCESS_READ_EX] = 0, + [WAPP_ACCESS_WRITE_EX] = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + [WAPP_ACCESS_APPEND_EX] = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST] = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST_EX] = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, +}; + +wapp_intern i32 file_seek_origins[FILE_SEEK_ORIGIN_COUNT] = { + [WAPP_SEEK_START] = SEEK_SET, + [WAPP_SEEK_CURRENT] = SEEK_CUR, + [WAPP_SEEK_END] = SEEK_END, +}; + +WFile *wapp_file_stdin(void) { + wapp_persist WFile _stdin = { .fd = STDIN_FILENO }; + return &_stdin; +} + +WFile *wapp_file_stdout(void) { + wapp_persist WFile _stdout = { .fd = STDOUT_FILENO }; + return &_stdout; +} + +WFile *wapp_file_stderr(void) { + wapp_persist WFile _stderr = { .fd = STDERR_FILENO }; + return &_stderr; +} + +WFile *_file_open(const Allocator *allocator, Str8RO *filepath, FileAccessMode mode) { + wapp_persist c8 tmp[WAPP_PATH_MAX] = {0}; + memset(tmp, 0, WAPP_PATH_MAX); + memcpy(tmp, filepath->buf, filepath->size); + + i32 fd = open((const char *)tmp, file_flags[mode], file_modes[mode]); + if (fd < 0) { + return NULL; + } + + WFile *output = wapp_mem_allocator_alloc(allocator, sizeof(WFile)); + if (output) { + output->fd = fd; + } + + return output; +} + +i64 _file_seek(WFile *file, i64 offset, FileSeekOrigin origin) { + return lseek64(file->fd, offset, file_seek_origins[origin]); +} + +u64 _file_read(void *dst_buf, u64 byte_count, WFile *file, u64 file_length) { + u64 copy_byte_count = file_length <= byte_count ? file_length : byte_count; + + i64 count = read(file->fd, dst_buf, copy_byte_count); + if (count < 0) { return 0; } + + return count; +} + +i64 _file_write(const void *src_buf, WFile *file, u64 byte_count) { + return write(file->fd, src_buf, byte_count); +} + +i32 _file_flush(WFile *file) { + return fsync(file->fd); +} + +i32 _file_close(WFile *file) { + return close(file->fd); +} + +i32 _file_rename(Str8RO *old_filepath, Str8RO *new_filepath) { + wapp_persist c8 old_tmp[WAPP_PATH_MAX] = {0}; + wapp_persist c8 new_tmp[WAPP_PATH_MAX] = {0}; + memset(old_tmp, 0, WAPP_PATH_MAX); + memcpy(old_tmp, old_filepath->buf, old_filepath->size); + memset(new_tmp, 0, WAPP_PATH_MAX); + memcpy(new_tmp, new_filepath->buf, new_filepath->size); + + i32 link_result = link((const char *)old_tmp, (const char *)new_tmp); + if (link_result == 0) { + _file_remove(old_filepath); + } + + return link_result; +} + +i32 _file_remove(Str8RO *filepath) { + wapp_persist c8 tmp[WAPP_PATH_MAX] = {0}; + memset(tmp, 0, WAPP_PATH_MAX); + memcpy(tmp, filepath->buf, filepath->size); + + return unlink((const char *)tmp); +} + +#endif // !WAPP_PLATFORM_POSIX diff --git a/wapp/os/file/posix/file_posix.h b/wapp/os/file/posix/file_posix.h new file mode 100644 index 0000000..46a38ed --- /dev/null +++ b/wapp/os/file/posix/file_posix.h @@ -0,0 +1,27 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef FILE_POSIX_H +#define FILE_POSIX_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#ifdef WAPP_PLATFORM_POSIX + +#define END_OF_LINE "\n" + +struct WFile { + i32 fd; +}; + +#endif // !WAPP_PLATFORM_POSIX + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !FILE_POSIX_H diff --git a/wapp/os/file/win/file_win.c b/wapp/os/file/win/file_win.c new file mode 100644 index 0000000..8f40778 --- /dev/null +++ b/wapp/os/file/win/file_win.c @@ -0,0 +1,177 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "file_win.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_WINDOWS + +#include "../file.h" +#include "../../cpath/cpath.h" +#include "../../../common/aliases/aliases.h" +#include "../../../base/array/array.h" +#include "../../../base/strings/str8/str8.h" +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +wapp_intern DWORD file_accesses[FILE_ACCESS_MODE_COUNT] = { + [WAPP_ACCESS_READ] = FILE_READ_DATA, + [WAPP_ACCESS_WRITE] = FILE_WRITE_DATA, + [WAPP_ACCESS_APPEND] = FILE_APPEND_DATA, + [WAPP_ACCESS_READ_EX] = FILE_READ_DATA | FILE_WRITE_DATA, + [WAPP_ACCESS_WRITE_EX] = FILE_READ_DATA | FILE_WRITE_DATA, + [WAPP_ACCESS_APPEND_EX] = FILE_READ_DATA | FILE_APPEND_DATA, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST] = FILE_WRITE_DATA, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST_EX] = FILE_READ_DATA | FILE_WRITE_DATA, +}; + +wapp_intern DWORD creation_dispositions[FILE_ACCESS_MODE_COUNT] = { + [WAPP_ACCESS_READ] = OPEN_EXISTING, + [WAPP_ACCESS_WRITE] = CREATE_ALWAYS, + [WAPP_ACCESS_APPEND] = OPEN_ALWAYS, + [WAPP_ACCESS_READ_EX] = OPEN_EXISTING, + [WAPP_ACCESS_WRITE_EX] = CREATE_ALWAYS, + [WAPP_ACCESS_APPEND_EX] = OPEN_ALWAYS, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST] = CREATE_NEW, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST_EX] = CREATE_NEW, +}; + +wapp_intern DWORD sharing_modes[FILE_ACCESS_MODE_COUNT] = { + [WAPP_ACCESS_READ] = FILE_SHARE_READ | FILE_SHARE_WRITE, + [WAPP_ACCESS_WRITE] = FILE_SHARE_READ, + [WAPP_ACCESS_APPEND] = FILE_SHARE_READ, + [WAPP_ACCESS_READ_EX] = FILE_SHARE_READ | FILE_SHARE_WRITE, + [WAPP_ACCESS_WRITE_EX] = FILE_SHARE_READ, + [WAPP_ACCESS_APPEND_EX] = FILE_SHARE_READ, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST] = FILE_SHARE_READ, + [WAPP_ACCESS_WRITE_FAIL_ON_EXIST_EX] = FILE_SHARE_READ, +}; + +wapp_intern DWORD file_seek_origins[FILE_SEEK_ORIGIN_COUNT] = { + [WAPP_SEEK_START] = FILE_BEGIN, + [WAPP_SEEK_CURRENT] = FILE_CURRENT, + [WAPP_SEEK_END] = FILE_END, +}; + +WFile *wapp_file_stdin(void) { + wapp_persist WFile _stdin = { .fh = INVALID_HANDLE_VALUE }; + _stdin.fh = GetStdHandle(STD_INPUT_HANDLE); + return &_stdin; +} + +WFile *wapp_file_stdout(void) { + wapp_persist WFile _stdout = { .fh = INVALID_HANDLE_VALUE }; + _stdout.fh = GetStdHandle(STD_OUTPUT_HANDLE); + return &_stdout; +} + +WFile *wapp_file_stderr(void) { + wapp_persist WFile _stderr = { .fh = INVALID_HANDLE_VALUE }; + _stderr.fh = GetStdHandle(STD_ERROR_HANDLE); + return &_stderr; +} + +WFile *_file_open(const Allocator *allocator, Str8RO *filepath, FileAccessMode mode) { + wapp_persist c8 tmp[WAPP_PATH_MAX] = {0}; + memset(tmp, 0, WAPP_PATH_MAX); + memcpy(tmp, filepath->buf, filepath->size); + + HANDLE fh = CreateFileA((LPCSTR)tmp, + file_accesses[mode], + sharing_modes[mode], + NULL, + creation_dispositions[mode], + FILE_ATTRIBUTE_NORMAL, + NULL); + if (fh == INVALID_HANDLE_VALUE) { + return NULL; + } + + WFile *output = wapp_mem_allocator_alloc(allocator, sizeof(WFile)); + if (output) { + output->fh = fh; + } + + return output; +} + +i64 _file_seek(WFile *file, i64 offset, FileSeekOrigin origin) { + LARGE_INTEGER distance = {0}; + LARGE_INTEGER output = {0}; + + distance.QuadPart = offset; + + if (!SetFilePointerEx(file->fh, distance, &output, file_seek_origins[origin])) { + return -1; + } + + return output.QuadPart; +} + +u64 _file_read(void* dst_buf, u64 byte_count, WFile* file, u64 file_length) { + u64 copy_byte_count = file_length <= byte_count ? file_length : byte_count; + wapp_debug_assert(copy_byte_count <= DWORD_MAX, "Attempting to read large number of bytes at once"); + + DWORD read_count = 0; + if (!ReadFile(file->fh, dst_buf, (DWORD)copy_byte_count, &read_count, NULL)) { + return 0; + } + + return (u64)read_count; +} + +i64 _file_write(const void *src_buf, WFile *file, u64 byte_count) { + wapp_debug_assert(byte_count <= DWORD_MAX, "Attempting to write large number of bytes at once"); + + DWORD write_count = 0; + if (!WriteFile(file->fh, src_buf, (DWORD)byte_count, &write_count, NULL)) { + return 0; + } + return (i64)write_count; +} + +i32 _file_flush(WFile *file) { + if (!FlushFileBuffers(file->fh)) { + return -1; + } + + return 0; +} + +i32 _file_close(WFile *file) { + if (!CloseHandle(file->fh)) { + return -1; + } + + return 0; +} + +i32 _file_rename(Str8RO *old_filepath, Str8RO *new_filepath) { + wapp_persist c8 old_tmp[WAPP_PATH_MAX] = {0}; + wapp_persist c8 new_tmp[WAPP_PATH_MAX] = {0}; + memset(old_tmp, 0, WAPP_PATH_MAX); + memcpy(old_tmp, old_filepath->buf, old_filepath->size); + memset(new_tmp, 0, WAPP_PATH_MAX); + memcpy(new_tmp, new_filepath->buf, new_filepath->size); + + if (!MoveFile((LPCSTR)old_tmp, (LPCSTR)new_tmp)) { + return -1; + } + + return 0; +} + +i32 _file_remove(Str8RO *filepath) { + wapp_persist c8 tmp[WAPP_PATH_MAX] = {0}; + memset(tmp, 0, WAPP_PATH_MAX); + memcpy(tmp, filepath->buf, filepath->size); + + if (!DeleteFile((LPCSTR)tmp)) { + return -1; + } + + return 0; +} + +#endif // !WAPP_PLATFORM_WINDOWS diff --git a/wapp/os/file/win/file_win.h b/wapp/os/file/win/file_win.h new file mode 100644 index 0000000..69d579a --- /dev/null +++ b/wapp/os/file/win/file_win.h @@ -0,0 +1,31 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef FILE_WIN_H +#define FILE_WIN_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#ifdef WAPP_PLATFORM_WINDOWS + +#define END_OF_LINE "\r\n" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +struct WFile { + HANDLE fh; +}; + +#endif // !WAPP_PLATFORM_WINDOWS + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !FILE_WIN_H diff --git a/wapp/os/mem/mem_os.c b/wapp/os/mem/mem_os.c new file mode 100644 index 0000000..f8ddab5 --- /dev/null +++ b/wapp/os/mem/mem_os.c @@ -0,0 +1,30 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "mem_os.h" +#include "mem_os_ops.h" +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" +#include +#include + +#if defined(WAPP_PLATFORM_WINDOWS) +#include "win/mem_os_win.h" +#elif defined(WAPP_PLATFORM_POSIX) +#include "posix/mem_os_posix.h" +#else +#error "Unrecognised platform" +#endif + +void *wapp_os_mem_alloc(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type) { + void *output = os_mem_allocate(addr, size, access, flags, type); + + if (type == WAPP_MEM_INIT_INITIALISED) { + memset(output, 0, size); + } + + return output; +} + +void wapp_os_mem_free(void *ptr, u64 size) { + os_mem_free(ptr, size); +} diff --git a/wapp/os/mem/mem_os.h b/wapp/os/mem/mem_os.h new file mode 100644 index 0000000..5681ef7 --- /dev/null +++ b/wapp/os/mem/mem_os.h @@ -0,0 +1,33 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_OS_H +#define MEM_OS_H + +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" + +#include "mem_os_ops.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#if defined(WAPP_PLATFORM_WINDOWS) +#include "win/mem_os_win.h" +#elif defined(WAPP_PLATFORM_POSIX) +#include "posix/mem_os_posix.h" +#else +#error "Unrecognised platform" +#endif + +void *wapp_os_mem_alloc(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type); +void wapp_os_mem_free(void *ptr, u64 size); + +wapp_extern void *os_mem_allocate(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type); +wapp_extern void os_mem_free(void *ptr, u64 size); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_OS_H diff --git a/wapp/os/mem/mem_os_ops.h b/wapp/os/mem/mem_os_ops.h new file mode 100644 index 0000000..357a1af --- /dev/null +++ b/wapp/os/mem/mem_os_ops.h @@ -0,0 +1,30 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_OS_OPS_H +#define MEM_OS_OPS_H + +#include "../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +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_init_type { + WAPP_MEM_INIT_UNINITIALISED, + WAPP_MEM_INIT_INITIALISED, +} MemInitType; + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_OS_OPS_H diff --git a/wapp/os/mem/posix/mem_os_posix.c b/wapp/os/mem/posix/mem_os_posix.c new file mode 100644 index 0000000..8143af2 --- /dev/null +++ b/wapp/os/mem/posix/mem_os_posix.c @@ -0,0 +1,36 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_POSIX + +#include "mem_os_posix.h" +#include "../mem_os_ops.h" +#include + +wapp_intern 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, +}; + +void *os_mem_allocate(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type) { + (void)type; + 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(addr, size, access_types[access], alloc_flags, -1, 0); +} + +void os_mem_free(void *ptr, u64 size) { + munmap(ptr, size); +} + +#endif // !WAPP_PLATFORM_POSIX diff --git a/wapp/os/mem/posix/mem_os_posix.h b/wapp/os/mem/posix/mem_os_posix.h new file mode 100644 index 0000000..967cc78 --- /dev/null +++ b/wapp/os/mem/posix/mem_os_posix.h @@ -0,0 +1,35 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_OS_POSIX_H +#define MEM_OS_POSIX_H + +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#ifdef WAPP_PLATFORM_POSIX + +#include + +typedef enum mem_alloc_flags { +#if 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; + +#endif // !WAPP_PLATFORM_POSIX + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_OS_POSIX_H diff --git a/wapp/os/mem/win/mem_os_win.c b/wapp/os/mem/win/mem_os_win.c new file mode 100644 index 0000000..abfbe3b --- /dev/null +++ b/wapp/os/mem/win/mem_os_win.c @@ -0,0 +1,37 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_WINDOWS + +#include "mem_os_win.h" +#include "../mem_os_ops.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +wapp_intern 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, +}; + +void *os_mem_allocate(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type) { + // Ensure memory is committed if it's meant to be initialised + if (type == WAPP_MEM_INIT_INITIALISED) { + flags |= WAPP_MEM_ALLOC_COMMIT; + } + + return VirtualAlloc(addr, (SIZE_T)size, flags, access_types[access]); +} + +void os_mem_free(void *ptr, u64 size) { + VirtualFree(ptr, size, MEM_RELEASE); +} + +#endif // !WAPP_PLATFORM_WINDOWS diff --git a/wapp/os/mem/win/mem_os_win.h b/wapp/os/mem/win/mem_os_win.h new file mode 100644 index 0000000..e0a63eb --- /dev/null +++ b/wapp/os/mem/win/mem_os_win.h @@ -0,0 +1,29 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef MEM_OS_WIN_H +#define MEM_OS_WIN_H + +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#ifdef WAPP_PLATFORM_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include +#include + +typedef enum mem_alloc_flags { + WAPP_MEM_ALLOC_RESERVE = MEM_RESERVE, + WAPP_MEM_ALLOC_COMMIT = MEM_COMMIT, +} MemAllocFlags; + +#endif // !WAPP_PLATFORM_WINDOWS + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !MEM_OS_WIN_H diff --git a/wapp/os/shell/commander/commander.c b/wapp/os/shell/commander/commander.c new file mode 100644 index 0000000..b48c91a --- /dev/null +++ b/wapp/os/shell/commander/commander.c @@ -0,0 +1,101 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "commander.h" +#include "commander_output.h" +#include "../utils/shell_utils.h" +#include "../../allocators/arena/mem_arena_allocator.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/misc/misc_utils.h" +#include "../../../base/dbl_list/dbl_list.h" +#include "../../../base/mem/allocator/mem_allocator.h" +#include "../../../base/strings/str8/str8.h" +#include +#include +#include +#include + +#define CMD_BUF_LEN 8192 +#define OUT_BUF_LEN 4096 + +wapp_intern CMDResult execute_command(Str8RO *cmd, CMDOutHandling out_handling, Str8 *out_buf); +wapp_intern CMDError get_command_output(FILE *fp, CMDOutHandling out_handling, Str8 *out_buf); + +CMDResult wapp_shell_commander_execute(CMDOutHandling out_handling, Str8 *out_buf, const Str8List *cmd) { + if (!cmd) { + return CMD_NO_EXIT(SHELL_ERR_INVALID_ARGS); + } + + Allocator arena = wapp_mem_arena_allocator_init(KiB(500)); + + Str8 *cmd_str = wapp_str8_join(&arena, cmd, &wapp_str8_lit_ro(" ")); + if (!cmd_str) { + wapp_mem_arena_allocator_destroy(&arena); + return CMD_NO_EXIT(SHELL_ERR_ALLOCATION_FAIL); + } + + // Redirect output + cmd_str = wapp_str8_alloc_concat(&arena, cmd_str, &wapp_str8_lit_ro(" 2>&1")); + + CMDResult output = execute_command(cmd_str, out_handling, out_buf); + + wapp_mem_arena_allocator_destroy(&arena); + + return output; +} + +wapp_intern CMDResult execute_command(Str8RO *cmd, CMDOutHandling out_handling, Str8 *out_buf) { + char cmd_buf[CMD_BUF_LEN] = {0}; + wapp_str8_copy_to_cstr(cmd_buf, cmd, CMD_BUF_LEN); + + FILE *fp = wapp_shell_utils_popen(cmd_buf, "r"); + if (!fp) { + return CMD_NO_EXIT(SHELL_ERR_PROC_START_FAIL); + } + + CMDResult output; + + CMDError err = get_command_output(fp, out_handling, out_buf); + if (err > SHELL_ERR_NO_ERROR) { + output = CMD_NO_EXIT(err); + goto EXECUTE_COMMAND_CLOSE; + } + + i32 st = EXIT_SUCCESS; + err = get_output_status(fp, &st); + if (err > SHELL_ERR_NO_ERROR) { + output = CMD_NO_EXIT(err); + goto EXECUTE_COMMAND_CLOSE; + } + + // Process is already closed in get_output_status + fp = NULL; + + output = (CMDResult){ + .exited = true, + .exit_code = st, + .error = SHELL_ERR_NO_ERROR, + }; + +EXECUTE_COMMAND_CLOSE: + if (fp) { + wapp_shell_utils_pclose(fp); + } + return output; +} + +wapp_intern CMDError get_command_output(FILE *fp, CMDOutHandling out_handling, Str8 *out_buf) { + Str8 out = wapp_str8_buf(OUT_BUF_LEN); + + out.size = fread((void *)out.buf, sizeof(c8), out.capacity, fp); + if (out_handling == SHELL_OUTPUT_CAPTURE && out_buf != NULL) { + if (out.size >= out_buf->capacity) { + return SHELL_ERR_OUT_BUF_FULL; + } + + wapp_str8_concat_capped(out_buf, &out); + } else if (out_handling == SHELL_OUTPUT_PRINT) { + printf(WAPP_STR8_SPEC, wapp_str8_varg(out)); + } + + return SHELL_ERR_NO_ERROR; +} diff --git a/wapp/os/shell/commander/commander.h b/wapp/os/shell/commander/commander.h new file mode 100644 index 0000000..b4c6f6f --- /dev/null +++ b/wapp/os/shell/commander/commander.h @@ -0,0 +1,29 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef COMMANDER_H +#define COMMANDER_H + +#include "commander_output.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" +#include "../../../base/strings/str8/str8.h" +#include +#include + +// TODO (Abdelrahman): This module needs rethinking + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#define CMD_NO_EXIT(ERR) ((CMDResult){.exited = false, .exit_code = EXIT_FAILURE, .error = ERR}) + +CMDResult wapp_shell_commander_execute(CMDOutHandling out_handling, Str8 *out_buf, const Str8List *cmd); + +wapp_extern CMDError get_output_status(FILE *fp, i32 *status_out); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !COMMANDER_H diff --git a/wapp/os/shell/commander/commander_output.h b/wapp/os/shell/commander/commander_output.h new file mode 100644 index 0000000..fe45993 --- /dev/null +++ b/wapp/os/shell/commander/commander_output.h @@ -0,0 +1,42 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef COMMANDER_OUTPUT_H +#define COMMANDER_OUTPUT_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" +#include "../../../common/misc/misc_utils.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef enum { + SHELL_OUTPUT_DISCARD, + SHELL_OUTPUT_PRINT, + SHELL_OUTPUT_CAPTURE, +} CMDOutHandling; + +typedef enum { + SHELL_ERR_NO_ERROR, + SHELL_ERR_INVALID_ARGS, + SHELL_ERR_ALLOCATION_FAIL, + SHELL_ERR_PROC_START_FAIL, + SHELL_ERR_OUT_BUF_FULL, + SHELL_ERR_PROC_EXIT_FAIL, +} CMDError; + +typedef struct CMDResult CMDResult; +struct CMDResult { + i32 exit_code; + CMDError error; + b8 exited; + + wapp_misc_utils_reserve_padding(sizeof(b8) + sizeof(i32) + sizeof(CMDError)); +}; + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !COMMANDER_OUTPUT_H diff --git a/wapp/os/shell/commander/posix/commander_posix.c b/wapp/os/shell/commander/posix/commander_posix.c new file mode 100644 index 0000000..a25afb0 --- /dev/null +++ b/wapp/os/shell/commander/posix/commander_posix.c @@ -0,0 +1,25 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "../../../../common/aliases/aliases.h" +#include "../../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_POSIX + +#include "../commander_output.h" +#include "../../utils/shell_utils.h" +#include +#include + +CMDError get_output_status(FILE *fp, i32 *status_out) { + *status_out = wapp_shell_utils_pclose(fp); + + if (!WIFEXITED(*status_out)) { + return SHELL_ERR_PROC_EXIT_FAIL; + } + + *status_out = WEXITSTATUS(*status_out); + + return SHELL_ERR_NO_ERROR; +} + +#endif // !WAPP_PLATFORM_POSIX diff --git a/wapp/os/shell/commander/win/commander_win.c b/wapp/os/shell/commander/win/commander_win.c new file mode 100644 index 0000000..8be5df8 --- /dev/null +++ b/wapp/os/shell/commander/win/commander_win.c @@ -0,0 +1,24 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "../../../../common/aliases/aliases.h" +#include "../../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_WINDOWS + +#include "../commander_output.h" +#include "../../utils/shell_utils.h" +#include + +CMDError get_output_status(FILE *fp, i32 *status_out) { + if (!feof(fp)) { + // Ensure process is closed on failure + wapp_shell_utils_pclose(fp); + return SHELL_ERR_PROC_EXIT_FAIL; + } + + *status_out = wapp_shell_utils_pclose(fp); + + return SHELL_ERR_NO_ERROR; +} + +#endif // !WAPP_PLATFORM_WINDOWS diff --git a/wapp/os/shell/termcolour/posix/termcolour_posix.c b/wapp/os/shell/termcolour/posix/termcolour_posix.c new file mode 100644 index 0000000..48f7964 --- /dev/null +++ b/wapp/os/shell/termcolour/posix/termcolour_posix.c @@ -0,0 +1,36 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "../../../../common/aliases/aliases.h" +#include "../../../../common/platform/platform.h" +#include "../../../../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_POSIX + +#include "../terminal_colours.h" +#include + +wapp_intern Str8RO colours[COUNT_TERM_COLOUR] = { + [WAPP_TERM_COLOUR_FG_BLACK] = wapp_str8_lit_ro_initialiser_list("\033[30m"), + [WAPP_TERM_COLOUR_FG_RED] = wapp_str8_lit_ro_initialiser_list("\033[31m"), + [WAPP_TERM_COLOUR_FG_GREEN] = wapp_str8_lit_ro_initialiser_list("\033[32m"), + [WAPP_TERM_COLOUR_FG_BLUE] = wapp_str8_lit_ro_initialiser_list("\033[34m"), + [WAPP_TERM_COLOUR_FG_CYAN] = wapp_str8_lit_ro_initialiser_list("\033[36m"), + [WAPP_TERM_COLOUR_FG_MAGENTA] = wapp_str8_lit_ro_initialiser_list("\033[35m"), + [WAPP_TERM_COLOUR_FG_YELLOW] = wapp_str8_lit_ro_initialiser_list("\033[33m"), + [WAPP_TERM_COLOUR_FG_WHITE] = wapp_str8_lit_ro_initialiser_list("\033[37m"), + [WAPP_TERM_COLOUR_FG_BR_BLACK] = wapp_str8_lit_ro_initialiser_list("\033[90m"), + [WAPP_TERM_COLOUR_FG_BR_RED] = wapp_str8_lit_ro_initialiser_list("\033[91m"), + [WAPP_TERM_COLOUR_FG_BR_GREEN] = wapp_str8_lit_ro_initialiser_list("\033[92m"), + [WAPP_TERM_COLOUR_FG_BR_BLUE] = wapp_str8_lit_ro_initialiser_list("\033[94m"), + [WAPP_TERM_COLOUR_FG_BR_CYAN] = wapp_str8_lit_ro_initialiser_list("\033[96m"), + [WAPP_TERM_COLOUR_FG_BR_MAGENTA] = wapp_str8_lit_ro_initialiser_list("\033[95m"), + [WAPP_TERM_COLOUR_FG_BR_YELLOW] = wapp_str8_lit_ro_initialiser_list("\033[93m"), + [WAPP_TERM_COLOUR_FG_BR_WHITE] = wapp_str8_lit_ro_initialiser_list("\033[97m"), + [WAPP_TERM_COLOUR_CLEAR] = wapp_str8_lit_ro_initialiser_list("\033[0m"), +}; + +void print_coloured_text(Str8RO *text, TerminalColour colour) { + printf(WAPP_STR8_SPEC WAPP_STR8_SPEC, wapp_str8_varg(colours[colour]), wapp_str8_varg((*text))); +} + +#endif // !WAPP_PLATFORM_POSIX diff --git a/wapp/os/shell/termcolour/termcolour.c b/wapp/os/shell/termcolour/termcolour.c new file mode 100644 index 0000000..9b5a566 --- /dev/null +++ b/wapp/os/shell/termcolour/termcolour.c @@ -0,0 +1,18 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "termcolour.h" +#include "terminal_colours.h" +#include "../../../base/strings/str8/str8.h" + +void wapp_shell_termcolour_print_text(Str8RO *text, TerminalColour colour) { + if (colour < WAPP_TERM_COLOUR_FG_BLACK || colour > WAPP_TERM_COLOUR_FG_BR_WHITE) { + return; + } + + print_coloured_text(text, colour); +} + +void wapp_shell_termcolour_clear_colour(void) { + Str8RO empty = wapp_str8_lit_ro(""); + print_coloured_text(&empty, WAPP_TERM_COLOUR_CLEAR); +} diff --git a/wapp/os/shell/termcolour/termcolour.h b/wapp/os/shell/termcolour/termcolour.h new file mode 100644 index 0000000..24076b1 --- /dev/null +++ b/wapp/os/shell/termcolour/termcolour.h @@ -0,0 +1,26 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef TERM_COLOUR_H +#define TERM_COLOUR_H + +#include "terminal_colours.h" +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" +#include "../../../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +// TODO (Abdelrahman): Look into moving away from stdio in the implementation + +void wapp_shell_termcolour_print_text(Str8RO *text, TerminalColour colour); +void wapp_shell_termcolour_clear_colour(void); + +wapp_extern void print_coloured_text(Str8RO *text, TerminalColour colour); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !TERM_COLOUR_H diff --git a/wapp/os/shell/termcolour/terminal_colours.h b/wapp/os/shell/termcolour/terminal_colours.h new file mode 100644 index 0000000..8e864e6 --- /dev/null +++ b/wapp/os/shell/termcolour/terminal_colours.h @@ -0,0 +1,39 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef TERMINAL_COLOURS_H +#define TERMINAL_COLOURS_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef enum { + WAPP_TERM_COLOUR_FG_BLACK, + WAPP_TERM_COLOUR_FG_RED, + WAPP_TERM_COLOUR_FG_GREEN, + WAPP_TERM_COLOUR_FG_BLUE, + WAPP_TERM_COLOUR_FG_CYAN, + WAPP_TERM_COLOUR_FG_MAGENTA, + WAPP_TERM_COLOUR_FG_YELLOW, + WAPP_TERM_COLOUR_FG_WHITE, + WAPP_TERM_COLOUR_FG_BR_BLACK, + WAPP_TERM_COLOUR_FG_BR_RED, + WAPP_TERM_COLOUR_FG_BR_GREEN, + WAPP_TERM_COLOUR_FG_BR_BLUE, + WAPP_TERM_COLOUR_FG_BR_CYAN, + WAPP_TERM_COLOUR_FG_BR_MAGENTA, + WAPP_TERM_COLOUR_FG_BR_YELLOW, + WAPP_TERM_COLOUR_FG_BR_WHITE, + WAPP_TERM_COLOUR_CLEAR, + + COUNT_TERM_COLOUR, +} TerminalColour; + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !TERMINAL_COLOURS_H diff --git a/wapp/os/shell/termcolour/win/termcolour_win.c b/wapp/os/shell/termcolour/win/termcolour_win.c new file mode 100644 index 0000000..f3c5609 --- /dev/null +++ b/wapp/os/shell/termcolour/win/termcolour_win.c @@ -0,0 +1,73 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "../../../../common/aliases/aliases.h" +#include "../../../../common/platform/platform.h" +#include "../../../../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_WINDOWS + +#include "../terminal_colours.h" +#include "../../../../common/misc/misc_utils.h" +#include + +#define WIN32_LEAN_AND_MEAN +#include + +typedef struct TermcolourData TermcolourData; +struct TermcolourData { + HANDLE handle; + WORD default_colour; + WORD current_colour; + + wapp_misc_utils_reserve_padding(sizeof(HANDLE) + sizeof(WORD) + sizeof(WORD)); +}; + +wapp_intern void init_data(TermcolourData *data); + +wapp_intern WORD colours[COUNT_TERM_COLOUR] = { + [WAPP_TERM_COLOUR_FG_BLACK] = 0, + [WAPP_TERM_COLOUR_FG_RED] = FOREGROUND_RED, + [WAPP_TERM_COLOUR_FG_GREEN] = FOREGROUND_GREEN, + [WAPP_TERM_COLOUR_FG_BLUE] = FOREGROUND_BLUE, + [WAPP_TERM_COLOUR_FG_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE, + [WAPP_TERM_COLOUR_FG_MAGENTA] = FOREGROUND_RED | FOREGROUND_BLUE, + [WAPP_TERM_COLOUR_FG_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN, + [WAPP_TERM_COLOUR_FG_WHITE] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, + [WAPP_TERM_COLOUR_FG_BR_BLACK] = FOREGROUND_INTENSITY, + [WAPP_TERM_COLOUR_FG_BR_RED] = FOREGROUND_RED | FOREGROUND_INTENSITY, + [WAPP_TERM_COLOUR_FG_BR_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY, + [WAPP_TERM_COLOUR_FG_BR_BLUE] = FOREGROUND_BLUE | FOREGROUND_INTENSITY, + [WAPP_TERM_COLOUR_FG_BR_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, + [WAPP_TERM_COLOUR_FG_BR_MAGENTA] = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY, + [WAPP_TERM_COLOUR_FG_BR_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, + [WAPP_TERM_COLOUR_FG_BR_WHITE] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, +}; + +void print_coloured_text(Str8RO *text, TerminalColour colour) { + wapp_persist TermcolourData data = {0}; + if (data.handle == 0) { + init_data(&data); + } + + if (colour == WAPP_TERM_COLOUR_CLEAR) { + data.current_colour = data.default_colour; + } else { + data.current_colour = colours[colour]; + } + + SetConsoleTextAttribute(data.handle, data.current_colour); + printf(WAPP_STR8_SPEC, wapp_str8_varg((*text))); +} + +wapp_intern void init_data(TermcolourData *data) { + // create handle + data->handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // get console colour information + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(data->handle, &csbi); + data->default_colour = csbi.wAttributes; + data->current_colour = data->default_colour; +} + +#endif // !WAPP_PLATFORM_WINDOWS diff --git a/wapp/os/shell/utils/shell_utils.h b/wapp/os/shell/utils/shell_utils.h new file mode 100644 index 0000000..a9d8195 --- /dev/null +++ b/wapp/os/shell/utils/shell_utils.h @@ -0,0 +1,26 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef SHELL_UTILS_H +#define SHELL_UTILS_H + +#include "../../../common/aliases/aliases.h" +#include "../../../common/platform/platform.h" +#include + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#ifdef WAPP_PLATFORM_WINDOWS + #define wapp_shell_utils_popen _popen + #define wapp_shell_utils_pclose _pclose +#else + #define wapp_shell_utils_popen popen + #define wapp_shell_utils_pclose pclose +#endif /* ifdef WAPP_PLATFORM_WINDOWS */ + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !SHELL_UTILS_H diff --git a/wapp/os/wapp_os.c b/wapp/os/wapp_os.c new file mode 100644 index 0000000..62a6bce --- /dev/null +++ b/wapp/os/wapp_os.c @@ -0,0 +1,24 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_OS_C +#define WAPP_OS_C + +#include "wapp_os.h" +#include "file/file.c" +#include "file/posix/file_posix.c" +#include "file/win/file_win.c" +#include "shell/termcolour/posix/termcolour_posix.c" +#include "shell/termcolour/win/termcolour_win.c" +#include "shell/termcolour/termcolour.c" +#include "shell/commander/posix/commander_posix.c" +#include "shell/commander/win/commander_win.c" +#include "shell/commander/commander.c" +#include "cpath/cpath.c" +#include "allocators/arena/mem_arena.c" +#include "allocators/arena/mem_arena_allocator.c" +#include "mem/posix/mem_os_posix.c" +#include "mem/win/mem_os_win.c" +#include "mem/mem_os.c" +#include "../base/wapp_base.c" + +#endif // !WAPP_OS_C diff --git a/wapp/os/wapp_os.h b/wapp/os/wapp_os.h new file mode 100644 index 0000000..39cb3b2 --- /dev/null +++ b/wapp/os/wapp_os.h @@ -0,0 +1,24 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_CORE_H +#define WAPP_CORE_H + +#include "file/file.h" +#include "file/posix/file_posix.h" +#include "file/win/file_win.h" +#include "shell/termcolour/termcolour.h" +#include "shell/termcolour/terminal_colours.h" +#include "shell/commander/commander.h" +#include "shell/commander/commander_output.h" +#include "shell/utils/shell_utils.h" +#include "cpath/cpath.h" +#include "allocators/arena/mem_arena.h" +#include "allocators/arena/mem_arena_allocator.h" +#include "mem/posix/mem_os_posix.h" +#include "mem/win/mem_os_win.h" +#include "mem/mem_os_ops.h" +#include "mem/mem_os.h" +#include "../common/wapp_common.h" +#include "../base/wapp_base.h" + +#endif // !WAPP_CORE_H diff --git a/wapp/prng/wapp_prng.c b/wapp/prng/wapp_prng.c new file mode 100644 index 0000000..e5081be --- /dev/null +++ b/wapp/prng/wapp_prng.c @@ -0,0 +1,8 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_PRNG_C +#define WAPP_PRNG_C + +#include "xorshift/xorshift.c" + +#endif // !WAPP_PRNG_C diff --git a/wapp/prng/wapp_prng.h b/wapp/prng/wapp_prng.h new file mode 100644 index 0000000..e64dfac --- /dev/null +++ b/wapp/prng/wapp_prng.h @@ -0,0 +1,9 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_PRNG_H +#define WAPP_PRNG_H + +#include "xorshift/xorshift.h" +#include "../common/wapp_common.h" + +#endif // !WAPP_PRNG_H diff --git a/wapp/prng/xorshift/xorshift.c b/wapp/prng/xorshift/xorshift.c new file mode 100644 index 0000000..d681924 --- /dev/null +++ b/wapp/prng/xorshift/xorshift.c @@ -0,0 +1,135 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "xorshift.h" +#include "../../common/aliases/aliases.h" +#include "../../common/assert/assert.h" +#include "../../common/platform/platform.h" +#include +#include + +typedef struct SplitMix64State SplitMix64State; +struct SplitMix64State { + u64 seed; +}; + +wapp_intern u64 rol64(u64 x, u64 bits); +wapp_intern u64 split_mix_64(SplitMix64State *state); +wapp_intern void seed_os_generator(void); +wapp_intern u64 generate_random_number(void); + +XOR256State wapp_prng_xorshift_init_state(void) { + wapp_persist b8 seeded = false; + if (!seeded) { + seeded = true; + seed_os_generator(); + } + + SplitMix64State sm64 = {.seed = generate_random_number()}; + + return (XOR256State){ + .x = split_mix_64(&sm64), + .y = split_mix_64(&sm64), + .z = split_mix_64(&sm64), + .w = split_mix_64(&sm64), + }; +} + +u64 wapp_prng_xorshift_256(XOR256State *state) { + u64 t = state->x ^ (state->x << 11); + + state->x = state->y; + state->y = state->z; + state->z = state->w; + state->w = (state->w ^ (state->w >> 19)) ^ (t ^ (t >> 8)); + + return state->w; +} + +u64 wapp_prng_xorshift_256ss(XOR256State *state) { + const u64 result = rol64(state->z * 5, 7) * 9; + const u64 t = state->z << 17; + + state->y ^= state->w; + state->x ^= state->z; + state->z ^= state->y; + state->w ^= state->x; + + state->y ^= t; + state->x = rol64(state->x, 45); + + return result; +} + +u64 wapp_prng_xorshift_256p(XOR256State *state) { + const u64 result = state->w + state->x; + const u64 t = state->z << 17; + + state->y ^= state->w; + state->x ^= state->z; + state->z ^= state->y; + state->w ^= state->x; + + state->y ^= t; + state->x = rol64(state->x, 45); + + return result; +} + +wapp_intern u64 rol64(u64 x, u64 bits) { + return (x << bits) | (x >> (64 - bits)); +} + +wapp_intern u64 split_mix_64(SplitMix64State *state) { + state->seed += 0x9E3779B97f4A7C15; + + u64 result = state->seed; + result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; + result = (result ^ (result >> 27)) * 0x94D049BB133111EB; + + return result ^ (result >> 31); +} + +#if defined(WAPP_PLATFORM_C) && WAPP_PLATFORM_C_VERSION >= WAPP_PLATFORM_C11_VERSION +#ifdef WAPP_PLATFORM_POSIX +wapp_intern void seed_os_generator(void) { + struct timespec ts = {0}; + int result = clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + wapp_runtime_assert(result == 0, "Invalid seed value"); + + srand48(ts.tv_nsec); +} + +wapp_intern u64 generate_random_number(void) { + return lrand48(); +} +#else +wapp_intern void seed_os_generator(void) { + struct timespec ts = {0}; + int result = timespec_get(&ts, TIME_UTC); + wapp_runtime_assert(result != 0, "Invalid seed value"); + + srand(ts.tv_nsec); +} + +wapp_intern u64 generate_random_number(void) { + i32 n1 = rand(); + i32 n2 = rand(); + + return (((u64)n1) << 32 | (u64)n2); +} +#endif // !WAPP_PLATFORM_POSIX +#else +wapp_intern void seed_os_generator(void) { + time_t result = time(NULL); + wapp_runtime_assert(result != (time_t)(-1), "Invalid seed value"); + + srand(result); +} + +wapp_intern u64 generate_random_number(void) { + i32 n1 = rand(); + i32 n2 = rand(); + + return (((u64)n1) << 32 | (u64)n2); +} +#endif // !WAPP_PLATFORM_C diff --git a/wapp/prng/xorshift/xorshift.h b/wapp/prng/xorshift/xorshift.h new file mode 100644 index 0000000..7a66d05 --- /dev/null +++ b/wapp/prng/xorshift/xorshift.h @@ -0,0 +1,30 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef XORSHIFT_H +#define XORSHIFT_H + +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +typedef struct XOR256State XOR256State; +struct XOR256State { + u64 x; + u64 y; + u64 z; + u64 w; +}; + +XOR256State wapp_prng_xorshift_init_state(void); +u64 wapp_prng_xorshift_256(XOR256State *state); +u64 wapp_prng_xorshift_256ss(XOR256State *state); +u64 wapp_prng_xorshift_256p(XOR256State *state); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !XORSHIFT_H diff --git a/wapp/testing/tester/tester.c b/wapp/testing/tester/tester.c new file mode 100644 index 0000000..fc8773b --- /dev/null +++ b/wapp/testing/tester/tester.c @@ -0,0 +1,55 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "tester.h" +#include "../../common/aliases/aliases.h" +#include "../../os/shell/termcolour/termcolour.h" +#include "../../base/strings/str8/str8.h" +#include +#include +#include + +wapp_intern void handle_test_result(TestFuncResult result); + +void run_tests(TestFunc *func1, ...) { + printf("\n"); + + handle_test_result(func1()); + + va_list args; + va_start(args, func1); + + TestFunc *func = va_arg(args, TestFunc *); + + while (func) { + TestFuncResult result = func(); + handle_test_result(result); + + func = va_arg(args, TestFunc *); + } + + va_end(args); + + printf("\n"); +} + +wapp_intern void handle_test_result(TestFuncResult result) { + TerminalColour colour; + Str8 result_text = wapp_str8_buf(64); + + if (result.passed) { + colour = WAPP_TERM_COLOUR_FG_BR_GREEN; + wapp_str8_copy_cstr_capped(&result_text, "PASSED"); + } else { + colour = WAPP_TERM_COLOUR_FG_BR_RED; + wapp_str8_copy_cstr_capped(&result_text, "FAILED"); + } + + printf("["); + wapp_shell_termcolour_print_text(&result_text, colour); + wapp_shell_termcolour_clear_colour(); + printf("] " WAPP_STR8_SPEC "\n", wapp_str8_varg(result.name)); + + if (!result.passed) { + exit(EXIT_FAILURE); + } +} diff --git a/wapp/testing/tester/tester.h b/wapp/testing/tester/tester.h new file mode 100644 index 0000000..74b592c --- /dev/null +++ b/wapp/testing/tester/tester.h @@ -0,0 +1,36 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef TESTER_H +#define TESTER_H + +#include "../../common/misc/misc_utils.h" +#include "../../common/platform/platform.h" +#include "../../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE + +#define wapp_tester_result(PASSED) (TestFuncResult{wapp_str8_lit_ro(__func__), PASSED, {}}) +#else +#define wapp_tester_result(PASSED) ((TestFuncResult){.name = wapp_str8_lit_ro(__func__), .passed = PASSED}) +#endif // !WAPP_PLATFORM_CPP + +#define wapp_tester_run_tests(...) run_tests(__VA_ARGS__, NULL) + +typedef struct TestFuncResult TestFuncResult; +struct TestFuncResult { + Str8 name; + b8 passed; + + wapp_misc_utils_reserve_padding(sizeof(Str8) + sizeof(b8)); +}; + +typedef TestFuncResult(TestFunc)(void); + +void run_tests(TestFunc *func1, ...); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !TESTER_H diff --git a/wapp/testing/wapp_testing.c b/wapp/testing/wapp_testing.c new file mode 100644 index 0000000..2898eea --- /dev/null +++ b/wapp/testing/wapp_testing.c @@ -0,0 +1,10 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_TESTING_C +#define WAPP_TESTING_C + +#include "wapp_testing.h" +#include "tester/tester.c" +#include "../os/wapp_os.c" + +#endif // !WAPP_TESTING_C diff --git a/wapp/testing/wapp_testing.h b/wapp/testing/wapp_testing.h new file mode 100644 index 0000000..1c1877f --- /dev/null +++ b/wapp/testing/wapp_testing.h @@ -0,0 +1,10 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_TESTING_H +#define WAPP_TESTING_H + +#include "tester/tester.h" +#include "../common/wapp_common.h" +#include "../os/wapp_os.h" + +#endif // !WAPP_TESTING_H diff --git a/wapp/uuid/uuid.c b/wapp/uuid/uuid.c new file mode 100644 index 0000000..06993fb --- /dev/null +++ b/wapp/uuid/uuid.c @@ -0,0 +1,58 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "uuid.h" +#include "../common/aliases/aliases.h" +#include "../common/assert/assert.h" +#include "../base/strings/str8/str8.h" +#include "../prng/xorshift/xorshift.h" +#include + +#define UUID_STR_FORMAT ("%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.12" PRIx64) + +typedef struct UUID4 UUID4; +struct UUID4 { + u64 high; + u64 low; +}; + +wapp_intern UUID4 generate_uuid4(void); +wapp_intern void uuid4_to_uuid(const UUID4* uuid4, WUUID *uuid); + +WUUID *wapp_uuid_init_uuid4(WUUID *uuid) { + wapp_debug_assert(uuid != NULL, "`uuid` should not be NULL"); + + UUID4 uuid4 = generate_uuid4(); + uuid4_to_uuid(&uuid4, uuid); + + return uuid; +} + +wapp_intern UUID4 generate_uuid4(void) { + wapp_persist XOR256State state = {0}; + wapp_persist b8 initialised = false; + + if (!initialised) { + initialised = true; + state = wapp_prng_xorshift_init_state(); + } + + UUID4 uuid = (UUID4){ + .high = wapp_prng_xorshift_256(&state), + .low = wapp_prng_xorshift_256(&state), + }; + + uuid.high = (uuid.high & 0xffffffffffff0fff) | 0x0000000000004000; + uuid.low = (uuid.low & 0x3fffffffffffffff) | 0x8000000000000000; + + return uuid; +} + +wapp_intern void uuid4_to_uuid(const UUID4* uuid4, WUUID *uuid) { + u64 group1 = uuid4->high >> 32; + u64 group2 = (uuid4->high << 32) >> 48; + u64 group3 = (uuid4->high << 48) >> 48; + u64 group4 = uuid4->low >> 48; + u64 group5 = (uuid4->low << 16) >> 16; + + wapp_str8_format(&(uuid->uuid), UUID_STR_FORMAT, group1, group2, group3, group4, group5); +} diff --git a/wapp/uuid/uuid.h b/wapp/uuid/uuid.h new file mode 100644 index 0000000..09fdcab --- /dev/null +++ b/wapp/uuid/uuid.h @@ -0,0 +1,50 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef UUID_H +#define UUID_H + +#include "../common/aliases/aliases.h" +#include "../common/platform/platform.h" +#include "../base/strings/str8/str8.h" + +#ifdef WAPP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#define UUID_BUF_LENGTH 48 +#define WAPP_UUID_SPEC WAPP_STR8_SPEC +#define wapp_uuid_varg(WUUID) wapp_str8_varg((WUUID).uuid) + +typedef struct WUUID WUUID; +struct WUUID { + Str8 uuid; +}; + +// TODO (Abdelrahman): Update UUID implementation to work properly with C++ and tests for validation + +#ifdef WAPP_PLATFORM_CPP +#define wapp_uuid_gen_uuid4() ([&](){ \ + wapp_persist WUUID uuid = wapp_uuid_create(); \ + return *(wapp_uuid_init_uuid4(&uuid)); \ +}()) +#else +#define wapp_uuid_gen_uuid4() *(wapp_uuid_init_uuid4(&wapp_uuid_create())) +#endif + +/* Low level UUID API */ + +#ifdef WAPP_PLATFORM_CPP +#define wapp_uuid_create() ([&](){ return WUUID{wapp_str8_buf(UUID_BUF_LENGTH)}; }()) +#else +#define wapp_uuid_create() ((WUUID){.uuid = wapp_str8_buf(UUID_BUF_LENGTH)}) +#endif + +// Just returns the same pointer that was passed in with the UUID initialised. +// Fails when passed a NULL pointer. +WUUID *wapp_uuid_init_uuid4(WUUID *uuid); + +#ifdef WAPP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WAPP_PLATFORM_CPP + +#endif // !UUID_H diff --git a/wapp/uuid/wapp_uuid.c b/wapp/uuid/wapp_uuid.c new file mode 100644 index 0000000..c2a9b00 --- /dev/null +++ b/wapp/uuid/wapp_uuid.c @@ -0,0 +1,10 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_UUID_C +#define WAPP_UUID_C + +#include "uuid.c" +#include "../base/wapp_base.c" +#include "../prng/wapp_prng.c" + +#endif // !WAPP_UUID_C diff --git a/wapp/uuid/wapp_uuid.h b/wapp/uuid/wapp_uuid.h new file mode 100644 index 0000000..4878499 --- /dev/null +++ b/wapp/uuid/wapp_uuid.h @@ -0,0 +1,11 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_UUID_H +#define WAPP_UUID_H + +#include "uuid.h" +#include "../common/wapp_common.h" +#include "../base/wapp_base.h" +#include "../prng/wapp_prng.h" + +#endif // !WAPP_UUID_H diff --git a/wapp/wapp.c b/wapp/wapp.c new file mode 100644 index 0000000..9dc22ab --- /dev/null +++ b/wapp/wapp.c @@ -0,0 +1,14 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_C +#define WAPP_C + +#include "wapp.h" +#include "base/wapp_base.c" +#include "os/wapp_os.c" +#include "log/wapp_log.c" +#include "prng/wapp_prng.c" +#include "uuid/wapp_uuid.c" +#include "testing/wapp_testing.c" + +#endif // !WAPP_C diff --git a/wapp/wapp.h b/wapp/wapp.h new file mode 100644 index 0000000..14957fe --- /dev/null +++ b/wapp/wapp.h @@ -0,0 +1,14 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef WAPP_H +#define WAPP_H + +#include "common/wapp_common.h" +#include "base/wapp_base.h" +#include "os/wapp_os.h" +#include "log/wapp_log.h" +#include "prng/wapp_prng.h" +#include "uuid/wapp_uuid.h" +#include "testing/wapp_testing.h" + +#endif // !WAPP_H