diff --git a/base/Home.md b/base/Home.md index 62a0161..4549874 100644 --- a/base/Home.md +++ b/base/Home.md @@ -1 +1,374 @@ # base + +The `base` package provides core data structures and memory abstractions: dynamic arrays, doubly-linked lists, queues, strings, and a pluggable allocator interface. + +**Dependencies:** common + +--- + +## Allocator Interface + +A pluggable allocator interface that abstracts memory allocation strategies. + +```c +#include "wapp_base.h" +``` + +### Allocator Struct + +```c +typedef struct Allocator Allocator; +struct Allocator { + void *obj; + MemAllocFunc *alloc; + MemAllocAlignedFunc *alloc_aligned; + MemReallocFunc *realloc; + MemReallocAlignedFunc *realloc_aligned; + MemFreeFunc *free; +}; +``` + +### Functions + +| Function | Description | +|----------|-------------| +| `wapp_mem_allocator_alloc(allocator, size)` | Allocates memory | +| `wapp_mem_allocator_alloc_aligned(allocator, size, alignment)` | Allocated aligned memory | +| `wapp_mem_allocator_realloc(allocator, ptr, old_size, new_size)` | Reallocates memory | +| `wapp_mem_allocator_realloc_aligned(allocator, ptr, old_size, new_size, alignment)` | Reallocates aligned memory | +| `wapp_mem_allocator_free(allocator, ptr, size)` | Frees memory | +| `wapp_mem_allocator_invalid(allocator)` | Checks if an allocator is zero-initialised (invalid) | + +The `Allocator` struct holds function pointers and a context object. This allows different allocation strategies (malloc, arena, custom) to share the same interface. + +--- + +## Memory Utilities + +```c +#include "wapp_base.h" +``` + +| Function | Description | +|----------|-------------| +| `wapp_mem_util_align_forward(ptr, alignment)` | Aligns a pointer forward to the given alignment | + +--- + +## Array + +A type-generic dynamic array with stack-allocated and allocator-backed variants. + +```c +#include "wapp_base.h" +``` + +### Typedefs + +Convenience typedefs for common element types: + +`VoidPtrArray`, `C8Array`, `C16Array`, `C32Array`, `U8Array`, `U16Array`, `U32Array`, `U64Array`, `B8Array`, `I8Array`, `I16Array`, `I32Array`, `I64Array`, `F32Array`, `F64Array`, `F128Array`, `UptrArray`, `IptrArray`, `Str8Array` + +### Creation + +| Macro | Description | +|-------|-------------| +| `wapp_array(TYPE, ...)` | Creates a stack-allocated array with initial elements. Capacity rounds up to the next power of 2, with a minimum of 2x the element count. | +| `wapp_array_with_capacity(TYPE, CAPACITY, FLAGS)` | Creates a stack-allocated array with a specific capacity. `FLAGS` is a bitmask of `ArrayInitFlags`. | +| `wapp_array_alloc_capacity(TYPE, ALLOCATOR, CAPACITY, FLAGS)` | Creates an allocator-backed array with a specific capacity | + +### Element Operations + +| Macro | Description | +|-------|-------------| +| `wapp_array_count(ARRAY)` | Returns the current element count | +| `wapp_array_capacity(ARRAY)` | Returns the total capacity | +| `wapp_array_item_size(ARRAY)` | Returns the size of a single element | +| `wapp_array_set_count(ARRAY, COUNT)` | Sets the element count | +| `wapp_array_get(TYPE, ARRAY, INDEX)` | Returns a pointer to the element at `INDEX` | +| `wapp_array_set(TYPE, ARRAY, INDEX, VALUE_PTR)` | Sets the element at `INDEX` from `VALUE_PTR` | +| `wapp_array_pop(TYPE, ARRAY)` | Pops and returns the last element by value. Returns `0`/`{}` if empty. | + +### Mutation (Capped — no allocation) + +| Macro | Description | +|-------|-------------| +| `wapp_array_append_capped(TYPE, ARRAY, VALUE_PTR)` | Appends an element within current capacity. Does not grow. | +| `wapp_array_extend_capped(TYPE, DST, SRC)` | Extends `DST` with elements from `SRC` within capacity. | +| `wapp_array_copy_capped(TYPE, DST, SRC)` | Copies `SRC` elements into `DST` within capacity. | +| `wapp_array_clear(TYPE, ARRAY)` | Resets the element count to 0 | + +### Mutation (Allocator-backed — may reallocate) + +| Macro | Description | +|-------|-------------| +| `wapp_array_append_alloc(TYPE, ALLOCATOR, ARRAY, VALUE_PTR, FLAGS)` | Appends, growing via allocator if needed | +| `wapp_array_extend_alloc(TYPE, ALLOCATOR, DST, SRC, FLAGS)` | Extends, growing via allocator if needed | +| `wapp_array_copy_alloc(TYPE, ALLOCATOR, DST, SRC, FLAGS)` | Copies, growing via allocator if needed | + +### Other + +| Macro | Description | +|-------|-------------| +| `wapp_array_calc_alloc_size(TYPE, CAPACITY)` | Calculates the allocation size needed for a given capacity | +| `wapp_array_from_preallcated_buffer(TYPE, BUFFER, BUFFER_SIZE)` | Creates an array from a pre-allocated buffer | + +### Initialization Flags + +| Flag | Description | +|------|-------------| +| `ARRAY_INIT_NONE` | No initialization (elements contain garbage) | +| `ARRAY_INIT_FILLED` | Zero-fill all elements on creation | + +### Example + +```c +// Stack-allocated array with initial values +I32Array arr = wapp_array(i32, 10, 20, 30); +wapp_array_count(&arr); // 3 +wapp_array_capacity(&arr); // 8 (next power of 2) + +// Append (capped) +i32 val = 40; +wapp_array_append_capped(i32, &arr, &val); + +// Heap-allocated array with automatic growth via arena +Allocator arena = wapp_mem_arena_allocator_init(KiB(16)); +I32Array dyn = wapp_array_alloc_capacity(i32, &arena, 4, ARRAY_INIT_NONE); +wapp_array_append_alloc(i32, &arena, &dyn, &val, ARRAY_INIT_NONE); +``` + +--- + +## Doubly-Linked List + +A type-generic doubly-linked list with both stack-allocated (persistent) and allocator-backed nodes. + +```c +#include "wapp_base.h" +``` + +### Typedefs + +Convenience typedefs for common element types, both for lists and nodes: + +`VoidPtrList`, `C8List`, `C16List`, `C32List`, `U8List`, `U16List`, `U32List`, `U64List`, `B8List`, `I8List`, `I16List`, `I32List`, `I64List`, `F32List`, `F64List`, `F128List`, `UptrList`, `IptrList`, `Str8List` + +### Creation + +| Macro | Description | +|-------|-------------| +| `wapp_dbl_list(TYPE)` | Creates an empty stack-allocated doubly-linked list | +| `wapp_dbl_list_alloc(TYPE, ALLOCATOR)` | Allocates a list using the given allocator | + +### Element Access + +| Macro | Description | +|-------|-------------| +| `wapp_dbl_list_get(TYPE, LIST, INDEX)` | Returns a pointer to the element at `INDEX` (walks from nearest end) | +| `wapp_dbl_list_get_node(TYPE, LIST, INDEX)` | Returns the `GenericNode*` at `INDEX` | +| `wapp_dbl_list_get_node_item(TYPE, NODE)` | Returns a pointer to the item in a node | + +### Mutation (Stack-allocated persistent nodes) + +| Macro | Description | +|-------|-------------| +| `wapp_dbl_list_push_front(TYPE, LIST, ITEM_PTR)` | Inserts at the front | +| `wapp_dbl_list_push_back(TYPE, LIST, ITEM_PTR)` | Appends at the back | +| `wapp_dbl_list_insert(TYPE, LIST, ITEM_PTR, INDEX)` | Inserts at `INDEX` | +| `wapp_dbl_list_pop_front(TYPE, LIST)` | Removes and returns the front element (or `NULL`) | +| `wapp_dbl_list_pop_back(TYPE, LIST)` | Removes and returns the back element (or `NULL`) | +| `wapp_dbl_list_remove(TYPE, LIST, INDEX)` | Removes and returns the element at `INDEX` (or `NULL`) | +| `wapp_dbl_list_empty(TYPE, LIST)` | Removes all elements from the list | + +### Mutation (Allocator-backed nodes) + +| Macro | Description | +|-------|-------------| +| `wapp_dbl_list_push_front_alloc(TYPE, ALLOCATOR, LIST, ITEM_PTR)` | Inserts at front with allocated node | +| `wapp_dbl_list_push_back_alloc(TYPE, ALLOCATOR, LIST, ITEM_PTR)` | Appends at back with allocated node | +| `wapp_dbl_list_insert_alloc(TYPE, ALLOCATOR, LIST, ITEM_PTR, INDEX)` | Inserts at `INDEX` with allocated node | + +### Node-Level Operations + +| Macro | Description | +|-------|-------------| +| `wapp_dbl_list_pop_front_node(TYPE, LIST)` | Removes and returns the front `GenericNode*` (or `NULL`) | +| `wapp_dbl_list_pop_back_node(TYPE, LIST)` | Removes and returns the back `GenericNode*` (or `NULL`) | +| `wapp_dbl_list_remove_node(TYPE, LIST, INDEX)` | Removes and returns the `GenericNode*` at `INDEX` (or `NULL`) | + +### Example + +```c +Str8List list = wapp_dbl_list(Str8); + +Str8 s1 = wapp_str8_lit("hello"); +Str8 s2 = wapp_str8_lit("world"); + +wapp_dbl_list_push_back(Str8, &list, &s1); +wapp_dbl_list_push_back(Str8, &list, &s2); + +Str8 *item = wapp_dbl_list_get(Str8, &list, 0); // pointer to s1 +``` + +--- + +## Queue + +A type-generic circular queue (FIFO) backed by a dynamic array. + +```c +#include "wapp_base.h" +``` + +### Typedefs + +Convenience typedefs: `VoidPtrQueue`, `C8Queue`, `C16Queue`, `C32Queue`, `U8Queue`, `U16Queue`, `U32Queue`, `U64Queue`, `B8Queue`, `I8Queue`, `I32Queue`, `I64Queue`, `F32Queue`, `F64Queue`, `F128Queue`, `UptrQueue`, `IptrQueue`, `Str8Queue` + +### Creation + +| Macro | Description | +|-------|-------------| +| `wapp_queue(TYPE, CAPACITY)` | Creates a stack-allocated queue with the given capacity | +| `wapp_queue_alloc(TYPE, ALLOCATOR, CAPACITY)` | Creates an allocator-backed queue | + +### Operations + +| Macro | Description | +|-------|-------------| +| `wapp_queue_push(TYPE, QUEUE, VALUE_PTR)` | Pushes an item to the back. Fails if full. | +| `wapp_queue_push_alloc(TYPE, ALLOCATOR, QUEUE, VALUE_PTR)` | Pushes, growing the backing array via allocator if full | +| `wapp_queue_pop(TYPE, QUEUE)` | Pops and returns a pointer to the front item | +| `wapp_queue_capacity(QUEUE)` | Returns the current capacity | +| `wapp_queue_item_size(QUEUE)` | Returns the size of a single element | + +### Example + +```c +I32Queue queue = wapp_queue(i32, 8); + +i32 val = 42; +wapp_queue_push(i32, &queue, &val); + +i32 *result = wapp_queue_pop(i32, &queue); // pointer to 42 +``` + +--- + +## Str8 + +A length-tracked, mutable string type. + +```c +#include "wapp_base.h" +``` + +### Type + +```c +typedef struct Str8 Str8; +struct Str8 { + u64 capacity; + u64 size; + c8 *buf; +}; + +typedef const Str8 Str8RO; // read-only view +``` + +### Printf Helpers + +| Macro | Description | +|-------|-------------| +| `WAPP_STR8_SPEC` | Format specifier (`"%.*s"`) for printf | +| `wapp_str8_varg(STRING)` | Expands to `(int)(STRING).size, (STRING).buf` for use with `WAPP_STR8_SPEC` | + +```c +printf("Value: " WAPP_STR8_SPEC "\n", wapp_str8_varg(my_str)); +``` + +### Creation — Stack + +| Macro | Description | +|-------|-------------| +| `wapp_str8_buf(CAPACITY)` | Creates an empty string buffer (zeroed) | +| `wapp_str8_lit(STRING)` | Creates a mutable copy of a string literal (capacity = 2x length) | +| `wapp_str8_lit_ro(STRING)` | Creates a read-only view of a string literal | +| `wapp_str8_lit_ro_initialiser_list(STRING)` | For use in static initialisers when compound literals aren't supported | + +### Creation — Allocator + +| Function | Description | +|----------|-------------| +| `wapp_str8_alloc_buf(allocator, capacity)` | Allocates an empty string buffer via allocator | +| `wapp_str8_alloc_and_fill_buf(allocator, capacity)` | Allocates and zero-fills | +| `wapp_str8_alloc_cstr(allocator, cstr)` | Copies a C string into an allocated Str8 | +| `wapp_str8_alloc_str8(allocator, str)` | Copies a Str8 into an allocated Str8 | +| `wapp_str8_alloc_substr(allocator, str, start, end)` | Copies a substring range | +| `wapp_str8_alloc_concat(allocator, dst, src)` | Concatenates and returns a new allocated Str8 | +| `wapp_str8_dealloc_buf(allocator, str)` | Frees an allocated Str8 | + +### Element Access & Mutation + +| Function | Description | +|----------|-------------| +| `wapp_str8_get(str, index)` | Gets character at `index`. Returns `'\0'` if out of bounds. | +| `wapp_str8_set(str, index, c)` | Sets character at `index` | +| `wapp_str8_push_back(str, c)` | Appends a single character | + +### Comparison & Search + +| Function | Description | +|----------|-------------| +| `wapp_str8_equal(s1, s2)` | Full string equality | +| `wapp_str8_equal_to_count(s1, s2, count)` | Equality limited to the first `count` characters | +| `wapp_str8_find(str, substr)` | Forward search. Returns index or `-1`. | +| `wapp_str8_rfind(str, substr)` | Reverse search. Returns index or `-1`. | + +### Content Manipulation + +| Function | Description | +|----------|-------------| +| `wapp_str8_slice(str, start, end)` | Returns a read-only substring view | +| `wapp_str8_concat_capped(dst, src)` | Concatenates within `dst` capacity | +| `wapp_str8_copy_cstr_capped(dst, cstr)` | Copies a C string into a Str8 | +| `wapp_str8_copy_str8_capped(dst, src)` | Copies a Str8 into another Str8 | +| `wapp_str8_copy_to_cstr(dst, src, dst_capacity)` | Copies Str8 content to a C string buffer | +| `wapp_str8_format(dst, format, ...)` | printf-style formatting into a Str8 | +| `wapp_str8_to_lower(dst, src)` | Converts to lowercase | +| `wapp_str8_to_upper(dst, src)` | Converts to uppercase | +| `wapp_str8_from_bytes(dst, src)` | Converts a `U8Array` of bytes into a Str8 | + +### Split and Join + +| Function | Description | +|----------|-------------| +| `wapp_str8_split(allocator, str, delimiter)` | Splits forward into a `Str8List` (all parts) | +| `wapp_str8_split_with_max(allocator, str, delimiter, max_splits)` | Splits forward with a maximum number of splits | +| `wapp_str8_rsplit(allocator, str, delimiter)` | Splits in reverse | +| `wapp_str8_rsplit_with_max(allocator, str, delimiter, max_splits)` | Splits in reverse with a maximum | +| `wapp_str8_join(allocator, list, delimiter)` | Joins a `Str8List` with a delimiter | +| `wapp_str8_list_total_size(list)` | Sum of all string sizes in a list | + +### Example + +```c +Str8 s = wapp_str8_lit("Hello, World!"); +printf(WAPP_STR8_SPEC "\n", wapp_str8_varg(s)); // prints "Hello, World!" + +Str8 buf = wapp_str8_buf(256); +wapp_str8_copy_cstr_capped(&buf, "Hello"); +wapp_str8_push_back(&buf, '!'); + +// Formatting +wapp_str8_format(&buf, "Value: %d", 42); + +// Split +Allocator arena = wapp_mem_arena_allocator_init(KiB(16)); +Str8 data = wapp_str8_lit("a,b,c"); +Str8 delim = wapp_str8_lit_ro(","); +Str8List *parts = wapp_str8_split(&arena, &data, &delim); + +// Join +Str8 *joined = wapp_str8_join(&arena, parts, &delim); +``` diff --git a/common/Home.md b/common/Home.md index d0553e6..cf18578 100644 --- a/common/Home.md +++ b/common/Home.md @@ -1 +1,168 @@ # common + +The `common` package provides foundational type definitions, platform detection, assertion macros, and miscellaneous utilities used by all other packages. + +**Dependencies:** None + +--- + +## Aliases + +Convenient typedefs for primitive types and qualifiers. + +```c +#include "wapp_common.h" +``` + +### Integer Types + +| Type | Underlying | +|------|-----------| +| `u8`, `u16`, `u32`, `u64` | `uint8_t`, `uint16_t`, `uint32_t`, `uint64_t` | +| `i8`, `i16`, `i32`, `i64` | `int8_t`, `int16_t`, `int32_t`, `int64_t` | +| `b8` | `uint8_t` (boolean) | +| `uptr`, `iptr` | `uintptr_t`, `intptr_t` | + +### Character Types + +| Type | Description | +|------|-------------| +| `c8` | UTF-8 code unit (`uint8_t` or `char8_t`) | +| `c16` | UTF-16 code unit (`uint16_t` or `char16_t`) | +| `c32` | UTF-32 code unit (`uint32_t` or `char32_t`) | + +### Floating-Point Types + +| Type | Description | +|------|-------------| +| `f32` | 32-bit float | +| `f64` | 64-bit double | +| `f128` | 80+ bit long double | + +### Boolean Macros (C only) + +In C, `true` and `false` are defined as `(b8)1` and `(b8)0` respectively when unavailable. + +### Qualifier Macros + +| Macro | Expands To | Description | +|-------|-----------|-------------| +| `wapp_extern` | `extern` | External linkage | +| `wapp_intern` | `static` | Internal linkage | +| `wapp_persist` | `static` | Persistent storage duration | +| `wapp_class_mem` | `static` | Class-level static (C++ only) | + +### C/C++ Interop + +```c +BEGIN_C_LINKAGE // extern "C" { (C++ only) +END_C_LINKAGE // } (C++ only) +``` + +--- + +## Platform Detection + +The library auto-detects the target platform and defines the appropriate macros. + +### Platform Macros + +**OS Family:** +- `WAPP_PLATFORM_POSIX` – Any POSIX-compliant system +- `WAPP_PLATFORM_WINDOWS` – Any Windows system (including Cygwin) +- `WAPP_PLATFORM_UNIX` – Traditional Unix systems + +**Specific Platforms:** +- `WAPP_PLATFORM_LINUX` – Linux +- `WAPP_PLATFORM_MACOS` – macOS +- `WAPP_PLATFORM_IOS` – iOS +- `WAPP_PLATFORM_ANDROID` – Android +- `WAPP_PLATFORM_FREE_BSD` – FreeBSD +- `WAPP_PLATFORM_NET_BSD` – NetBSD +- `WAPP_PLATFORM_OPEN_BSD` – OpenBSD +- `WAPP_PLATFORM_DRAGON_FLY` – DragonFly BSD +- `WAPP_PLATFORM_GNU` – GNU/Hurd +- `WAPP_PLATFORM_CYGWIN` – Cygwin +- `WAPP_PLATFORM_WINDOWS64` – 64-bit Windows +- `WAPP_PLATFORM_WINDOWS32` – 32-bit Windows +- `WAPP_PLATFORM_APPLE` – Any Apple platform + +### Language Version Macros + +**C Language:** +- `WAPP_PLATFORM_C` – Defined when compiling as C +- `WAPP_PLATFORM_C89`, `WAPP_PLATFORM_C99`, `WAPP_PLATFORM_C11`, `WAPP_PLATFORM_C17`, `WAPP_PLATFORM_C23` + +**C++ Language:** +- `WAPP_PLATFORM_CPP` – Defined when compiling as C++ +- `WAPP_PLATFORM_CPP98`, `WAPP_PLATFORM_CPP11`, `WAPP_PLATFORM_CPP14`, `WAPP_PLATFORM_CPP17`, `WAPP_PLATFORM_CPP20`, `WAPP_PLATFORM_CPP23` + +--- + +## Assertions + +```c +#include "wapp_common.h" +``` + +### Macros + +| Macro | Description | +|-------|-------------| +| `wapp_static_assert(EXPR, MSG)` | Compile-time assertion using `extern char` trick | +| `wapp_runtime_assert(EXPR, MSG)` | Runtime assertion (disabled by `WAPP_NO_RUNTIME_ASSERT`) | +| `wapp_debug_assert(EXPR, MSG)` | Runtime assertion, only active when `WAPP_DEBUG_ASSERT` is defined | + +On failure, assertions print the file, line, function, expression value, and diagnostic message to `stderr`, then call `abort()`. + +### Build Configuration + +```c +// Enable debug assertions during development +#define WAPP_DEBUG_ASSERT + +// Disable all runtime assertions for release +#define WAPP_NO_RUNTIME_ASSERT +``` + +--- + +## Misc Utilities + +```c +#include "wapp_common.h" +``` + +### Size Literals + +Binary (IEC) prefixes: + +| Macro | Formula | +|-------|---------| +| `KiB(SIZE)` | `SIZE << 10` | +| `MiB(SIZE)` | `SIZE << 20` | +| `GiB(SIZE)` | `SIZE << 30` | +| `TiB(SIZE)` | `SIZE << 40` | +| `PiB(SIZE)` | `SIZE << 50` | +| `EiB(SIZE)` | `SIZE << 60` | + +Decimal (SI) prefixes: + +| Macro | Formula | +|-------|---------| +| `KB(SIZE)` | `SIZE * 1000` | +| `MB(SIZE)` | `KB(SIZE) * 1000` | +| `GB(SIZE)` | `MB(SIZE) * 1000` | +| `TB(SIZE)` | `GB(SIZE) * 1000` | +| `PB(SIZE)` | `TB(SIZE) * 1000` | +| `EB(SIZE)` | `PB(SIZE) * 1000` | + +### Utility Macros + +| Macro | Description | +|-------|-------------| +| `wapp_misc_utils_reserve_padding(SIZE)` | Inserts padding bytes to align a struct field to pointer size | +| `wapp_misc_utils_u64_round_up_pow2(X)` | Rounds `X` up to the nearest power of 2 | +| `wapp_is_power_of_two(NUM)` | Checks if `NUM` is a power of 2 | +| `wapp_pointer_offset(PTR, OFFSET)` | Adds a byte offset to a pointer | +| `wapp_misc_utils_va_args_count(T, ...)` | Returns the number of variadic arguments of type `T` | diff --git a/os/Home.md b/os/Home.md index be68d69..abe2b7e 100644 --- a/os/Home.md +++ b/os/Home.md @@ -1 +1,356 @@ # os + +The `os` package provides OS-level abstractions: file I/O, memory-mapped arena allocators, shell command execution, terminal colour output, and path manipulation. + +**Dependencies:** common, base + +--- + +## OS Memory + +Low-level virtual memory allocation. + +```c +#include "wapp_os.h" +``` + +### Types + +| Type | Description | +|------|-------------| +| `MemAccess` | Memory access permissions: `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` | +| `MemAllocFlags` | Allocation flags: `WAPP_MEM_ALLOC_RESERVE`, `WAPP_MEM_ALLOC_COMMIT` | +| `MemInitType` | Initialization: `WAPP_MEM_INIT_UNINITIALISED`, `WAPP_MEM_INIT_INITIALISED` | + +### Functions + +| Function | Description | +|----------|-------------| +| `wapp_os_mem_alloc(addr, size, access, flags, init_type)` | Allocates or reserves virtual memory | +| `wapp_os_mem_free(ptr, size)` | Frees virtual memory | + +### Platform Notes + +On POSIX, `wapp_os_mem_alloc` wraps `mmap` and `wapp_os_mem_free` wraps `munmap`. On Windows they wrap `VirtualAlloc` and `VirtualFree`. + +POSIX uses `MAP_POPULATE` (Linux/GNU) or `MAP_PREFAULT_READ` (FreeBSD) for the commit flag. Other POSIX platforms (BSD, Apple) set both flags to `0`, relying on lazy mapping. + +On Windows, `WAPP_MEM_ALLOC_RESERVE` maps to `MEM_RESERVE` and `WAPP_MEM_ALLOC_COMMIT` maps to `MEM_COMMIT`. + +--- + +## Arena Allocator + +A bump allocator backed by OS virtual memory (or a user-provided buffer). Provides fast, sequential allocations with temporary allocation scoping (memory stacking). + +```c +#include "wapp_os.h" +``` + +### Initialization + +| Function / Macro | Description | +|------------------|-------------| +| `wapp_mem_arena_init_allocated(arena, base_capacity)` | Initialises an arena with reserved OS memory | +| `wapp_mem_arena_init_allocated_commit(arena, base_capacity)` | Reserves and commits OS memory | +| `wapp_mem_arena_init_allocated_zero(arena, base_capacity)` | Reserves and zeroes OS memory | +| `wapp_mem_arena_init_allocated_commit_and_zero(arena, base_capacity)` | Reserves, commits, and zeroes | +| `wapp_mem_arena_init_allocated_custom(arena, base_capacity, flags, zero)` | Full control over initialization | +| `wapp_mem_arena_init_buffer(arena, buffer, buffer_size)` | Initialises an arena from a pre-existing buffer | + +### Allocation + +| Function | Description | +|----------|-------------| +| `wapp_mem_arena_alloc(arena, size)` | Bump-allocates memory | +| `wapp_mem_arena_alloc_aligned(arena, size, alignment)` | Bump-allocates aligned memory | +| `wapp_mem_arena_realloc(arena, ptr, old_size, new_size)` | Reallocates within the arena | +| `wapp_mem_arena_realloc_aligned(arena, ptr, old_size, new_size, alignment)` | Reallocates aligned | + +### Temporary Scopes & Lifecycle + +| Function | Description | +|----------|-------------| +| `wapp_mem_arena_temp_begin(arena)` | Saves the current position (start of temp scope) | +| `wapp_mem_arena_temp_end(arena)` | Rewinds to the saved position (ends temp scope) | +| `wapp_mem_arena_clear(arena)` | Zeroes all arena memory | +| `wapp_mem_arena_destroy(arena)` | Frees the arena and sets pointer to `NULL` | + +### Example + +```c +Arena *arena = NULL; +wapp_mem_arena_init_allocated(&arena, MiB(1)); + +i32 *numbers = wapp_mem_arena_alloc(arena, 100 * sizeof(i32)); + +// Temporary scope +wapp_mem_arena_temp_begin(arena); +// ... temporary allocations ... +wapp_mem_arena_temp_end(arena); // memory rewound + +wapp_mem_arena_destroy(&arena); // arena is now NULL +``` + +### Arena-backed Allocator + +Wraps an `Arena` in the `Allocator` interface (from base). + +```c +#include "wapp_os.h" +``` + +| Function / Macro | Description | +|------------------|-------------| +| `wapp_mem_arena_allocator_init(capacity)` | Creates an `Allocator` backed by a new arena | +| `wapp_mem_arena_allocator_init_commit(capacity)` | With committed memory | +| `wapp_mem_arena_allocator_init_zero(capacity)` | With zeroed memory | +| `wapp_mem_arena_allocator_init_commit_and_zero(capacity)` | With committed and zeroed memory | +| `wapp_mem_arena_allocator_init_custom(capacity, flags, zero)` | Full control | +| `wapp_mem_arena_allocator_init_with_buffer(buffer, size)` | From a pre-existing buffer | +| `wapp_mem_arena_allocator_temp_begin(allocator)` | Begin temp scope | +| `wapp_mem_arena_allocator_temp_end(allocator)` | End temp scope | +| `wapp_mem_arena_allocator_clear(allocator)` | Clear arena | +| `wapp_mem_arena_allocator_destroy(allocator)` | Destroy arena | + +**Note:** The arena allocator only supports `alloc` and `alloc_aligned`. `realloc`, `realloc_aligned`, and `free` are not implemented (individual frees are unsupported by arenas). + +```c +Allocator allocator = wapp_mem_arena_allocator_init(KiB(16)); +void *mem = wapp_mem_allocator_alloc(&allocator, 256); +wapp_mem_arena_allocator_destroy(&allocator); +``` + +--- + +## File I/O + +Cross-platform file operations. + +```c +#include "wapp_os.h" +``` + +### Types + +| Type | Description | +|------|-------------| +| `WFile` | Opaque file handle | +| `FileAccessMode` | Access mode enum | +| `FileSeekOrigin` | Seek origin enum | + +### File Access Modes + +| Mode | Description | +|------|-------------| +| `WAPP_ACCESS_READ` | Read-only (`r`) | +| `WAPP_ACCESS_WRITE` | Write-only, truncates (`w`) | +| `WAPP_ACCESS_APPEND` | Append (`a`) | +| `WAPP_ACCESS_READ_EX` | Read/write (`r+`) | +| `WAPP_ACCESS_WRITE_EX` | Read/write, truncates (`w+`) | +| `WAPP_ACCESS_APPEND_EX` | Read/append (`a+`) | +| `WAPP_ACCESS_WRITE_FAIL_ON_EXIST` | Write, fail if exists (`wx`) | +| `WAPP_ACCESS_WRITE_FAIL_ON_EXIST_EX` | Read/write, fail if exists (`wx+`) | + +### Seek Origins + +| Origin | Description | +|--------|-------------| +| `WAPP_SEEK_START` | Beginning of file | +| `WAPP_SEEK_CURRENT` | Current position | +| `WAPP_SEEK_END` | End of file | + +### Functions + +| Function | Description | +|----------|-------------| +| `wapp_file_open(allocator, filepath, mode)` | Opens a file | +| `wapp_file_close(file)` | Closes a file | +| `wapp_file_read(buffer, file, byte_count)` | Reads raw bytes. Returns bytes read. | +| `wapp_file_write(buffer, file, byte_count)` | Writes raw bytes. Returns bytes written or negative on error. | +| `wapp_file_read_array(dst_array, file, item_count)` | Reads array elements from file | +| `wapp_file_write_array(src_array, file, item_count)` | Writes array elements to file | +| `wapp_file_seek(file, offset, origin)` | Seeks to a position. Returns the new position or negative on error. | +| `wapp_file_get_current_position(file)` | Returns the current file cursor position or negative on error | +| `wapp_file_get_length(file)` | Returns the file size in bytes | +| `wapp_file_flush(file)` | Flushes file buffers | +| `wapp_file_rename(old_path, new_path)` | Renames a file | +| `wapp_file_remove(path)` | Deletes a file | + +### Platform Notes + +On POSIX, `WFile` stores an `i32 fd` (file descriptor) and all operations wrap POSIX system calls (`open`, `read`, `write`, `lseek`, `ftruncate`, `close`, `rename`, `unlink`). + +On Windows, `WFile` stores a `HANDLE` and operations wrap Win32 API calls (`CreateFile`, `ReadFile`, `WriteFile`, `SetFilePointerEx`, `GetFileSizeEx`, `FlushFileBuffers`, `CloseHandle`, `MoveFileEx`, `DeleteFile`). + +Newline convention: `END_OF_LINE` is `"\n"` on POSIX and `"\r\n"` on Windows. + +### Example + +```c +Allocator arena = wapp_mem_arena_allocator_init(KiB(16)); +Str8 filename = wapp_str8_lit("test.txt"); + +// Write +WFile *f = wapp_file_open(&arena, &filename, WAPP_ACCESS_WRITE_EX); +i32 data[] = {1, 2, 3}; +I32Array arr = wapp_array_from_preallcated_buffer(i32, data, sizeof(data)); +wapp_file_write_array(arr, f, 3); +wapp_file_close(f); + +// Read +f = wapp_file_open(&arena, &filename, WAPP_ACCESS_READ); +i64 len = wapp_file_get_length(f); +// ... read into buffer ... +wapp_file_close(f); +wapp_file_remove(&filename); +``` + +--- + +## Shell Commander + +Execute shell commands and capture or discard their output. + +```c +#include "wapp_os.h" +``` + +### Types + +```c +typedef enum { + SHELL_OUTPUT_DISCARD, // Run silently + SHELL_OUTPUT_PRINT, // Print to stdout + SHELL_OUTPUT_CAPTURE, // Capture into a Str8 buffer +} 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 { + i32 exit_code; + CMDError error; + b8 exited; // whether the process exited normally +} CMDResult; +``` + +### Functions + +| Function | Description | +|----------|-------------| +| `wapp_shell_commander_execute(out_handling, out_buf, cmd)` | Executes a command. When `out_handling` is `SHELL_OUTPUT_CAPTURE`, `out_buf` must point to a `Str8` buffer. | + +### Example + +```c +// Build command +Str8List cmd = wapp_dbl_list(Str8); +Str8 prog = wapp_str8_lit("echo"); +Str8 arg = wapp_str8_lit("hello world"); +wapp_dbl_list_push_back(Str8, &cmd, &prog); +wapp_dbl_list_push_back(Str8, &cmd, &arg); + +// Execute and discard output +CMDResult result = wapp_shell_commander_execute(SHELL_OUTPUT_DISCARD, NULL, &cmd); +// result.exited == true, result.exit_code == 0 + +// Execute and capture output +Str8 buf = wapp_str8_buf(64); +result = wapp_shell_commander_execute(SHELL_OUTPUT_CAPTURE, &buf, &cmd); +// buf contains "hello world\n" +``` + +--- + +## Terminal Colours + +Print coloured text to the terminal. + +```c +#include "wapp_os.h" +``` + +### Colour Values + +| Colour | Description | +|--------|-------------| +| `WAPP_TERM_COLOUR_FG_BLACK` through `WAPP_TERM_COLOUR_FG_WHITE` | Standard foreground colours | +| `WAPP_TERM_COLOUR_FG_BR_BLACK` through `WAPP_TERM_COLOUR_FG_BR_WHITE` | Bright foreground variants | +| `WAPP_TERM_COLOUR_CLEAR` | Reset to default | + +### Functions + +| Function | Description | +|----------|-------------| +| `wapp_shell_termcolour_print_text(text, colour)` | Prints text in the specified colour | +| `wapp_shell_termcolour_clear_colour()` | Resets terminal colours to default | + +--- + +## CPath + +Cross-platform path manipulation utilities. + +```c +#include "wapp_os.h" +``` + +### Constants + +| Constant | Value | +|----------|-------| +| `WAPP_PATH_SEP` | Platform path separator (`'/'` or `'\\'`) | +| `WAPP_PATH_MAX` | Maximum path length | + +### Functions + +| Function | Description | +|----------|-------------| +| `wapp_cpath_join_path(dst, parts)` | Joins a `Str8List` of path components into a `Str8`. Returns an error code: `CPATH_JOIN_SUCCESS` (0), `CPATH_JOIN_INVALID_ARGS`, `CPATH_JOIN_EMPTY_PARTS`, `CPATH_JOIN_INSUFFICIENT_DST_CAPACITY`. Handles separator insertion automatically. | +| `wapp_cpath_dirname(allocator, path)` | Returns the parent directory (equivalent to `dirup(path, 1)`) | +| `wapp_cpath_dirup(allocator, path, count)` | Returns the directory `count` levels up | + +### Example + +```c +Allocator arena = wapp_mem_arena_allocator_init(KiB(16)); + +// Join path components +Str8List parts = wapp_dbl_list(Str8); +Str8 root = wapp_str8_lit("/"); +Str8 home = wapp_str8_lit("home"); +Str8 user = wapp_str8_lit("user"); +wapp_dbl_list_push_back(Str8, &parts, &root); +wapp_dbl_list_push_back(Str8, &parts, &home); +wapp_dbl_list_push_back(Str8, &parts, &user); + +Str8 out = wapp_str8_buf(256); +wapp_cpath_join_path(&out, &parts); // out = "/home/user" + +// Parent directory +Str8 dir = wapp_str8_lit("/home/user/docs"); +Str8 *parent = wapp_cpath_dirname(&arena, &dir); // "/home/user" +Str8 *grandparent = wapp_cpath_dirup(&arena, &dir, 2); // "/home" +``` + +--- + +## Shell Utils + +Cross-platform wrappers for `popen`/`pclose`. + +```c +#include "wapp_os.h" +``` + +| Macro | POSIX | Windows | +|-------|-------|---------| +| `wapp_shell_utils_popen` | `popen` | `_popen` | +| `wapp_shell_utils_pclose` | `pclose` | `_pclose` | diff --git a/prng/Home.md b/prng/Home.md index 541ae18..edae012 100644 --- a/prng/Home.md +++ b/prng/Home.md @@ -1 +1,48 @@ # prng + +The `prng` package provides a **Xorshift256** pseudo-random number generator (PRNG). It uses a SplitMix64 seeding strategy initialised from the system clock. + +**Dependencies:** common + +--- + +## Xorshift256 + +A fast, non-cryptographic PRNG with a 256-bit state. + +```c +#include "wapp_prng.h" +``` + +### State + +```c +typedef struct XOR256State XOR256State; +struct XOR256State { + u64 x; + u64 y; + u64 z; + u64 w; +}; +``` + +### Functions + +| Function | Description | +|----------|-------------| +| `wapp_prng_xorshift_init_state()` | Seeds and returns a new `XOR256State`. The OS generator is seeded once globally from the system clock (CLOCK_MONOTONIC_RAW on POSIX, `timespec_get` or `time` otherwise). A SplitMix64 generator then expands this into the 4 x 64-bit state. | +| `wapp_prng_xorshift_256(state)` | Standard xorshift256 variant. Returns a `u64`. | +| `wapp_prng_xorshift_256ss(state)` | xorshift256 with "scrambled" output (`rol64(z * 5, 7) * 9`). Passes BigCrush. | +| `wapp_prng_xorshift_256p(state)` | xorshift256 with "+" output (`w + x`). Passes BigCrush. | + +### Example + +```c +XOR256State state = wapp_prng_xorshift_init_state(); + +u64 r1 = wapp_prng_xorshift_256(&state); +u64 r2 = wapp_prng_xorshift_256ss(&state); +u64 r3 = wapp_prng_xorshift_256p(&state); +``` + +The state is passed by pointer and mutated on each call. The `_ss` and `_p` variants provide better statistical quality and pass the BigCrush test suite, while the standard `_256` variant is faster but has known weaknesses. diff --git a/testing/Home.md b/testing/Home.md index 5697da2..496a566 100644 --- a/testing/Home.md +++ b/testing/Home.md @@ -1 +1,87 @@ # testing + +The `testing` package provides a lightweight test runner that uses terminal colours for pass/fail output. + +**Dependencies:** common, os, base + +--- + +## Tester + +A simple test framework. Each test is a function that returns a `TestFuncResult`. Tests are registered by passing their function pointers to `wapp_tester_run_tests`. + +```c +#include "wapp_testing.h" +``` + +### Types + +```c +typedef struct TestFuncResult TestFuncResult; +struct TestFuncResult { + Str8 name; + b8 passed; +}; + +// Test function signature: +typedef TestFuncResult(TestFunc)(void); +``` + +### Test Result Helper + +| Macro | Description | +|-------|-------------| +| `wapp_tester_result(PASSED)` | Creates a `TestFuncResult` with the current function's name via `__func__` | + +### Test Runner + +| Macro / Function | Description | +|------------------|-------------| +| `wapp_tester_run_tests(func1, func2, ..., funcN)` | Runs all listed test functions. Must be NULL-terminated (the varargs macro automatically appends `NULL`). | +| `run_tests(func1, ...)` | Underlying function (varargs, NULL-terminated) | + +### Behaviour + +- Each test runs sequentially in declaration order. +- Passing tests are shown in green (`[PASSED]`), failing tests in red (`[FAILED]`). +- Output uses terminal colours via `wapp_shell_termcolour_print_text`. +- On the first failure, the runner calls `exit(EXIT_FAILURE)` immediately. + +### Example + +```c +#include "wapp_testing.h" + +TestFuncResult test_addition(void) { + int result = 2 + 2; + return wapp_tester_result(result == 4); +} + +TestFuncResult test_subtraction(void) { + int result = 5 - 3; + return wapp_tester_result(result == 2); +} + +int main(void) { + wapp_tester_run_tests( + test_addition, + test_subtraction + ); + return 0; +} +``` + +Output: +``` +[PASSED] test_addition +[PASSED] test_subtraction +``` + +### Writing Tests + +Each test function must: +1. Have the signature `TestFuncResult test_name(void)` +2. Return `wapp_tester_result(condition)` where `condition` is `true` for pass, `false` for fail +3. Not take any parameters + +Tests that share state (e.g. a file handle or an arena) can use `wapp_intern` (file-level `static`) variables to persist state across sequentially run tests. diff --git a/uuid/Home.md b/uuid/Home.md index 5e2a970..8f1b6aa 100644 --- a/uuid/Home.md +++ b/uuid/Home.md @@ -1 +1,68 @@ # uuid + +The `uuid` package provides **UUID v4** generation as specified by RFC 4122. UUIDs are generated using the library's Xorshift256 PRNG and formatted as 36-character strings. + +**Dependencies:** common, base, prng + +--- + +## UUID v4 + +```c +#include "wapp_uuid.h" +``` + +### Type + +```c +typedef struct WUUID WUUID; +struct WUUID { + Str8 uuid; // The UUID string representation +}; +``` + +### Constants + +| Constant | Description | +|----------|-------------| +| `UUID_BUF_LENGTH` | `48` — recommended buffer size for UUID strings | +| `WAPP_UUID_SPEC` | `WAPP_STR8_SPEC` — for printf formatting | +| `wapp_uuid_varg(wuuid)` | Expands to `wapp_str8_varg((WUUID).uuid)` for printf | + +### Convenience Macro + +| Macro | Description | +|-------|-------------| +| `wapp_uuid_gen_uuid4()` | One-shot UUID v4 generation. Creates a buffer and returns a fully initialised `WUUID` in a single expression — no variables needed. | + +### Low-Level API + +| Function | Description | +|----------|-------------| +| `wapp_uuid_create()` | Creates an empty `WUUID` with a zeroed buffer of `UUID_BUF_LENGTH` capacity | +| `wapp_uuid_init_uuid4(uuid)` | Fills a `WUUID` with a randomly generated UUID v4. Returns the same pointer for chaining. | + +### Example + +```c +// One-shot generation (most common usage) +WUUID uuid = wapp_uuid_gen_uuid4(); +printf(WAPP_UUID_SPEC "\n", wapp_uuid_varg(uuid)); +// Example output: "f47ac10b-58cc-4372-a567-0e02b2c3d479" + +// Two-step (low-level) +WUUID u = wapp_uuid_create(); +wapp_uuid_init_uuid4(&u); +printf(WAPP_UUID_SPEC "\n", wapp_uuid_varg(u)); +``` + +### UUID Format + +The generated UUID follows RFC 4122: +``` +xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx +``` +Where: +- The `4` identifies version 4 (random) +- `y` is one of `8`, `9`, `a`, or `b` (the variant field) +- All other digits are randomly generated via `wapp_prng_xorshift_256`