From 6cb22fd7f2c20be2cf268d6bcd236252d7847763 Mon Sep 17 00:00:00 2001 From: Mitja Felicijan Date: Wed, 21 Jan 2026 17:53:40 +0100 Subject: Engage! --- nonstd.h | 445 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 nonstd.h (limited to 'nonstd.h') diff --git a/nonstd.h b/nonstd.h new file mode 100644 index 0000000..939c030 --- /dev/null +++ b/nonstd.h @@ -0,0 +1,445 @@ +#ifndef NONSTD_H +#define NONSTD_H + +#include +#include +#include +#include +#include + +#ifndef NONSTD_DEF +#ifdef NONSTD_STATIC +#define NONSTD_DEF static +#else +#define NONSTD_DEF extern +#endif +#endif + +typedef int8_t i8; +typedef uint8_t u8; +typedef int16_t i16; +typedef uint16_t u16; +typedef int32_t i32; +typedef uint32_t u32; +typedef int64_t i64; +typedef uint64_t u64; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef intptr_t isize; +typedef uintptr_t usize; +typedef char c8; + +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +#define static_foreach(type, var, array) \ + for (size_t _i_##var = 0, _n_##var = countof(array); \ + _i_##var < _n_##var && ((var) = (array)[_i_##var], 1); ++_i_##var) + +#define ALLOC(type, n) ((type *)malloc((n) * sizeof(type))) +#define REALLOC(ptr, type, n) ((type *)realloc((ptr), (n) * sizeof(type))) +#define FREE(ptr) \ + do { \ + free(ptr); \ + ptr = NULL; \ + } while (0) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define CLAMP(x, lo, hi) (MIN((hi), MAX((lo), (x)))) + +#define UNUSED(x) (void)(x) + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define STATIC_ASSERT(expr, msg) _Static_assert((expr), msg) +#else +#define STATIC_ASSERT(expr, msg) \ + typedef char static_assertion_##msg[(expr) ? 1 : -1] +#endif + +// String view - read-only, non-owning reference to a string +typedef struct { + const char *data; + size_t length; +} stringv; + +NONSTD_DEF stringv sv_from_cstr(const char *s); +NONSTD_DEF stringv sv_from_parts(const char *data, size_t length); +NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end); +NONSTD_DEF int sv_equals(stringv a, stringv b); +NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix); +NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix); + +// String builder - owning, mutable, dynamically growing string buffer +typedef struct { + char *data; + size_t length; + size_t capacity; +} stringb; + +NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap); +NONSTD_DEF void sb_free(stringb *sb); +NONSTD_DEF void sb_ensure(stringb *sb, size_t additional); +NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s); +NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv); +NONSTD_DEF void sb_append_char(stringb *sb, char c); +NONSTD_DEF stringv sb_as_sv(const stringb *sb); + +// Slice - generic non-owning view into an array +// Usage: +// SLICE_DEF(int); // Define slice_int type +// slice(int) view = ...; // Use it +#define SLICE_DEF(T) \ + typedef struct { \ + T *data; \ + size_t length; \ + } slice_##T + +#define slice(T) slice_##T + +#define make_slice(T, ptr, len) ((slice(T)){.data = (ptr), .length = (len)}) + +#define array_as_slice(T, arr) \ + ((slice(T)){.data = (arr).data, .length = (arr).length}) + +// Dynamic array - generic type-safe growable array using macros +// Usage: array(int) numbers; array_init(numbers); +#define array(T) \ + struct { \ + T *data; \ + size_t length; \ + size_t capacity; \ + } + +#define array_init(arr) \ + do { \ + (arr).capacity = 0; \ + (arr).data = NULL; \ + (arr).length = 0; \ + } while (0) + +#define array_init_cap(arr, initial_cap) \ + do { \ + (arr).capacity = (initial_cap) ? (initial_cap) : 16; \ + (arr).data = ALLOC(__typeof__(*(arr).data), (arr).capacity); \ + (arr).length = 0; \ + } while (0) + +#define array_free(arr) \ + do { \ + FREE((arr).data); \ + (arr).length = 0; \ + (arr).capacity = 0; \ + } while (0) + +#define array_ensure(arr, additional) \ + do { \ + size_t _needed = (arr).length + (additional); \ + if (_needed > (arr).capacity) { \ + size_t _new_cap = (arr).capacity ? (arr).capacity : 16; \ + while (_new_cap < _needed) { \ + _new_cap *= 2; \ + } \ + (arr).data = \ + REALLOC((arr).data, __typeof__(*(arr).data), _new_cap); \ + (arr).capacity = _new_cap; \ + } \ + } while (0) + +#define array_push(arr, value) \ + do { \ + array_ensure((arr), 1); \ + (arr).data[(arr).length++] = (value); \ + } while (0) + +#define array_pop(arr) ((arr).length > 0 ? (arr).data[--(arr).length] : 0) + +#define array_get(arr, index) ((arr).data[index]) + +#define array_set(arr, index, value) \ + do { \ + if ((index) < (arr).length) { \ + (arr).data[index] = (value); \ + } \ + } while (0) + +#define array_insert(arr, index, value) \ + do { \ + if ((index) <= (arr).length) { \ + array_ensure((arr), 1); \ + for (size_t _i = (arr).length; _i > (index); --_i) { \ + (arr).data[_i] = (arr).data[_i - 1]; \ + } \ + (arr).data[index] = (value); \ + (arr).length++; \ + } \ + } while (0) + +#define array_remove(arr, index) \ + do { \ + if ((index) < (arr).length) { \ + for (size_t _i = (index); _i < (arr).length - 1; ++_i) { \ + (arr).data[_i] = (arr).data[_i + 1]; \ + } \ + (arr).length--; \ + } \ + } while (0) + +#define array_clear(arr) \ + do { \ + (arr).length = 0; \ + } while (0) + +#define array_reserve(arr, new_capacity) \ + do { \ + if ((new_capacity) > (arr).capacity) { \ + (arr).data = \ + REALLOC((arr).data, __typeof__(*(arr).data), (new_capacity)); \ + (arr).capacity = (new_capacity); \ + } \ + } while (0) + +#define array_foreach(arr, var) \ + for (size_t _i_##var = 0; \ + _i_##var < (arr).length && ((var) = (arr).data[_i_##var], 1); \ + ++_i_##var) + +#define array_foreach_i(arr, var, index) \ + for (size_t index = 0; \ + index < (arr).length && ((var) = (arr).data[index], 1); ++index) + +// Arena - block-based memory allocator +// Usage: Arena a = arena_make(); void* p = arena_alloc(&a, 100); +// arena_free(&a); +typedef struct { + char *ptr; + char *end; + array(char *) blocks; +} Arena; + +#define ARENA_DEFAULT_BLOCK_SIZE (4096) + +NONSTD_DEF Arena arena_make(void); +NONSTD_DEF void arena_grow(Arena *a, size_t min_size); +NONSTD_DEF void *arena_alloc(Arena *a, size_t size); +NONSTD_DEF void arena_free(Arena *a); + +// File I/O helpers +NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size); +NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size); +NONSTD_DEF stringv read_entire_file_sv(const char *filepath); +NONSTD_DEF stringb read_entire_file_sb(const char *filepath); +NONSTD_DEF int write_file_sv(const char *filepath, stringv sv); +NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb); + +#endif // NONSTD_H + +#ifdef NONSTD_IMPLEMENTATION + +NONSTD_DEF stringv sv_from_cstr(const char *s) { + return (stringv){.data = s, .length = s ? strlen(s) : 0}; +} + +NONSTD_DEF stringv sv_from_parts(const char *data, size_t length) { + return (stringv){.data = data, .length = length}; +} + +NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end) { + if (start > sv.length) { + start = sv.length; + } + if (end > sv.length) { + end = sv.length; + } + if (start > end) { + start = end; + } + return (stringv){.data = sv.data + start, .length = end - start}; +} + +NONSTD_DEF int sv_equals(stringv a, stringv b) { + return a.length == b.length && (a.length == 0 || memcmp(a.data, b.data, a.length) == 0); +} + +NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix) { + return sv.length >= prefix.length && memcmp(sv.data, prefix.data, prefix.length) == 0; +} + +NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix) { + return sv.length >= suffix.length && memcmp(sv.data + sv.length - suffix.length, suffix.data, suffix.length) == 0; +} + +NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap) { + sb->capacity = initial_cap ? initial_cap : 16; + sb->data = ALLOC(char, sb->capacity); + sb->length = 0; + if (sb->data) { + sb->data[0] = '\0'; + } +} + +NONSTD_DEF void sb_free(stringb *sb) { + FREE(sb->data); + sb->length = 0; + sb->capacity = 0; +} + +NONSTD_DEF void sb_ensure(stringb *sb, size_t additional) { + size_t needed = sb->length + additional + 1; + if (needed > sb->capacity) { + while (sb->capacity < needed) { + sb->capacity *= 2; + } + sb->data = REALLOC(sb->data, char, sb->capacity); + } +} + +NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s) { + if (!s) { + return; + } + size_t slength = strlen(s); + sb_ensure(sb, slength); + memcpy(sb->data + sb->length, s, slength); + sb->length += slength; + sb->data[sb->length] = '\0'; +} + +NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv) { + if (!sv.data || sv.length == 0) { + return; + } + sb_ensure(sb, sv.length); + memcpy(sb->data + sb->length, sv.data, sv.length); + sb->length += sv.length; + sb->data[sb->length] = '\0'; +} + +NONSTD_DEF void sb_append_char(stringb *sb, char c) { + sb_ensure(sb, 1); + sb->data[sb->length++] = c; + sb->data[sb->length] = '\0'; +} + +NONSTD_DEF stringv sb_as_sv(const stringb *sb) { + return (stringv){.data = sb->data, .length = sb->length}; +} + +NONSTD_DEF Arena arena_make(void) { + Arena a = {0}; + array_init(a.blocks); + return a; +} + +NONSTD_DEF void arena_grow(Arena *a, size_t min_size) { + size_t size = MAX(ARENA_DEFAULT_BLOCK_SIZE, min_size); + char *block = ALLOC(char, size); + a->ptr = block; + a->end = block + size; + array_push(a->blocks, block); +} + +NONSTD_DEF void *arena_alloc(Arena *a, size_t size) { + // Align to 8 bytes basically + size_t align = sizeof(void *); + uintptr_t current = (uintptr_t)a->ptr; + uintptr_t aligned = (current + align - 1) & ~(align - 1); + uintptr_t available = (uintptr_t)a->end - aligned; + + if (available < size) { + arena_grow(a, size); + current = (uintptr_t)a->ptr; + aligned = (current + align - 1) & ~(align - 1); + } + + a->ptr = (char *)(aligned + size); + return (void *)aligned; +} + +NONSTD_DEF void arena_free(Arena *a) { + char *block; + array_foreach(a->blocks, block) { FREE(block); } + array_free(a->blocks); + a->ptr = NULL; + a->end = NULL; +} + +NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size) { + FILE *f = fopen(filepath, "rb"); + if (!f) { + return NULL; + } + + fseek(f, 0, SEEK_END); + long length = ftell(f); + fseek(f, 0, SEEK_SET); + + if (length < 0) { + fclose(f); + return NULL; + } + + size_t size = (size_t)length; + char *buffer = ALLOC(char, size + 1); + if (!buffer) { + fclose(f); + return NULL; + } + + size_t read = fread(buffer, 1, size, f); + fclose(f); + + if (read != size) { + FREE(buffer); + return NULL; + } + + buffer[size] = '\0'; + if (out_size) { + *out_size = size; + } + + return buffer; +} + +NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size) { + FILE *f = fopen(filepath, "wb"); + if (!f) { + return 0; + } + + size_t written = fwrite(data, 1, size, f); + fclose(f); + + return written == size; +} + +NONSTD_DEF stringv read_entire_file_sv(const char *filepath) { + size_t size = 0; + char *data = read_entire_file(filepath, &size); + if (!data) { + return (stringv){0}; + } + return (stringv){.data = data, .length = size}; +} + +NONSTD_DEF stringb read_entire_file_sb(const char *filepath) { + size_t size = 0; + char *data = read_entire_file(filepath, &size); + stringb sb = {0}; + if (data) { + sb.data = data; + sb.length = size; + sb.capacity = size + 1; + } + return sb; +} + +NONSTD_DEF int write_file_sv(const char *filepath, stringv sv) { + return write_entire_file(filepath, sv.data, sv.length); +} + +NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb) { + return write_entire_file(filepath, sb->data, sb->length); +} + +#endif // NONSTD_IMPLEMENTATION \ No newline at end of file -- cgit v1.2.3