#ifdef NONSTD_IMPLEMENTATION #ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #endif #ifndef NONSTD_H #define NONSTD_H #include #include #include #include #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 *)safe_malloc(sizeof(type), (n))) #define REALLOC(ptr, type, n) ((type *)safe_realloc((ptr), sizeof(type), (n))) #define FREE(ptr) \ do { \ free(ptr); \ ptr = NULL; \ } while (0) NONSTD_DEF void *safe_malloc(size_t item_size, size_t count); NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count); #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); slice(int) view = ...; #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) { \ if (_new_cap > SIZE_MAX / 2) { \ _new_cap = SIZE_MAX; \ break; \ } \ _new_cap *= 2; \ } \ if (_new_cap < _needed) { /* Overflow or OOM */ \ break; \ } \ void *_new_data = \ safe_realloc((arr).data, sizeof(*(arr).data), _new_cap); \ if (_new_data) { \ (arr).data = _new_data; \ (arr).capacity = _new_cap; \ } \ } \ } while (0) #define array_push(arr, value) \ do { \ array_ensure((arr), 1); \ if ((arr).length < (arr).capacity) { \ (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); \ if ((arr).length < (arr).capacity) { \ 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) { \ void *_new_data = \ safe_realloc((arr).data, sizeof(*(arr).data), (new_capacity)); \ if (_new_data) { \ (arr).data = _new_data; \ (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_idx(arr, var, index) \ for (size_t index = 0; \ index < (arr).length && ((var) = (arr).data[index], 1); ++index) // Arena - block-based memory allocator 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); // Image - simple RGB image structure typedef struct { u8 r, g, b; } Color; typedef struct { u32 width; u32 height; Color *pixels; } Canvas; #define COLOR_RGB(r, g, b) ((Color){(u8)(r), (u8)(g), (u8)(b)}) #define COLOR_HEX(hex) ((Color){(u8)(((hex) >> 16) & 0xFF), (u8)(((hex) >> 8) & 0xFF), (u8)((hex) & 0xFF)}) #define COLOR_BLACK COLOR_RGB(0, 0, 0) #define COLOR_WHITE COLOR_RGB(255, 255, 255) #define COLOR_RED COLOR_RGB(255, 0, 0) #define COLOR_GREEN COLOR_RGB(0, 255, 0) #define COLOR_BLUE COLOR_RGB(0, 0, 255) #define COLOR_YELLOW COLOR_RGB(255, 255, 0) #define COLOR_MAGENTA COLOR_RGB(255, 0, 255) #define COLOR_CYAN COLOR_RGB(0, 255, 255) NONSTD_DEF Canvas ppm_init(u32 width, u32 height); NONSTD_DEF void ppm_free(Canvas *img); NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color); NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y); NONSTD_DEF int ppm_save(const Canvas *img, const char *filename); NONSTD_DEF Canvas ppm_read(const char *filename); NONSTD_DEF void ppm_fill(Canvas *canvas, Color color); NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color); NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color); NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 x, i32 y, i32 r, Color color); NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color); // 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 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); // Logging typedef enum { LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, } LogLevel; NONSTD_DEF void set_log_level(LogLevel level); NONSTD_DEF LogLevel get_log_level_from_env(void); NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...); #define LOG_INFO_MSG(...) log_message(stdout, LOG_INFO, __VA_ARGS__) #define LOG_DEBUG_MSG(...) log_message(stdout, LOG_DEBUG, __VA_ARGS__) #define LOG_WARN_MSG(...) log_message(stderr, LOG_WARN, __VA_ARGS__) #define LOG_ERROR_MSG(...) log_message(stderr, LOG_ERROR, __VA_ARGS__) #define COLOR_RESET "\033[0m" #define COLOR_INFO "\033[32m" #define COLOR_DEBUG "\033[36m" #define COLOR_WARNING "\033[33m" #define COLOR_ERROR "\033[31m" #endif // NONSTD_H #ifdef NONSTD_IMPLEMENTATION NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) { if (count != 0 && item_size > SIZE_MAX / count) { return NULL; } return malloc(item_size * count); } NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count) { if (count != 0 && item_size > SIZE_MAX / count) { return NULL; } return realloc(ptr, item_size * count); } // String View 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; } // String Builder Implementation 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; size_t new_cap = sb->capacity; if (needed > new_cap) { while (new_cap < needed) { if (new_cap > SIZE_MAX / 2) { new_cap = SIZE_MAX; break; } new_cap *= 2; } if (new_cap < needed) return; // Overflow char *new_data = safe_realloc(sb->data, sizeof(char), new_cap); if (new_data) { sb->data = new_data; sb->capacity = new_cap; } } } NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s) { if (!s) { return; } size_t slength = strlen(s); sb_ensure(sb, slength); if (sb->length + slength + 1 <= sb->capacity) { 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); if (sb->length + sv.length + 1 <= sb->capacity) { 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); if (sb->length + 2 <= sb->capacity) { 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; } // Arena Implementation 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 end = (uintptr_t)a->end; // Check for overflow (aligned wrapped around) or out of bounds (aligned >= end) // or not enough space ((end - aligned) < size) if (aligned < current || aligned >= end || (end - aligned) < size) { arena_grow(a, size); current = (uintptr_t)a->ptr; aligned = (current + align - 1) & ~(align - 1); end = (uintptr_t)a->end; } // Double check after grow (in case grow failed or size is just too huge) if (aligned < current || aligned >= end || (end - aligned) < size) { return NULL; } 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; } // File I/O Implementation 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 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); } // Logging Implementation static LogLevel max_level = LOG_INFO; static const char *level_strings[] = { "ERROR", "WARN", "INFO", "DEBUG", }; static const char *level_colors[] = { COLOR_ERROR, COLOR_WARNING, COLOR_INFO, COLOR_DEBUG, }; NONSTD_DEF void set_log_level(LogLevel level) { max_level = level; } NONSTD_DEF LogLevel get_log_level_from_env(void) { const char *env = getenv("LOG_LEVEL"); if (env) { int level = atoi(env); if (level >= 0 && level <= 3) { return (LogLevel)level; } } return max_level; } NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...) { if (max_level < level) return; struct timeval tv; gettimeofday(&tv, NULL); struct tm *tm_info = localtime(&tv.tv_sec); char time_str[24]; strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); const char *color = isatty(fileno(stream)) ? level_colors[level] : ""; const char *reset = isatty(fileno(stream)) ? COLOR_RESET : ""; const char *log_format = "%s[%s.%03d] [%-5s] "; fprintf(stream, log_format, color, time_str, (int)(tv.tv_usec / 1000), level_strings[level]); va_list args; va_start(args, format); vfprintf(stream, format, args); va_end(args); fprintf(stream, "%s\n", reset); fflush(stream); } // PPM Image Implementation NONSTD_DEF Canvas ppm_init(u32 width, u32 height) { Canvas img = {0}; img.width = width; img.height = height; img.pixels = ALLOC(Color, width * height); if (img.pixels) { memset(img.pixels, 0, sizeof(Color) * width * height); } return img; } NONSTD_DEF void ppm_free(Canvas *img) { if (img->pixels) { FREE(img->pixels); } img->width = 0; img->height = 0; } NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color) { if (x < img->width && y < img->height) { img->pixels[y * img->width + x] = color; } } NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y) { if (x < img->width && y < img->height) { return img->pixels[y * img->width + x]; } return (Color){0, 0, 0}; } NONSTD_DEF int ppm_save(const Canvas *img, const char *filename) { FILE *f = fopen(filename, "w"); if (!f) { return 0; } fprintf(f, "P3\n%u %u\n255\n", img->width, img->height); for (u32 y = 0; y < img->height; ++y) { for (u32 x = 0; x < img->width; ++x) { Color c = ppm_get_pixel(img, x, y); fprintf(f, "%d %d %d ", c.r, c.g, c.b); } fprintf(f, "\n"); } fclose(f); return 1; } NONSTD_DEF Canvas ppm_read(const char *filename) { Canvas img = {0}; FILE *f = fopen(filename, "r"); if (!f) { return img; } char magic[3]; if (fscanf(f, "%2s", magic) != 1 || strcmp(magic, "P3") != 0) { fclose(f); return img; } u32 w, h, max_val; if (fscanf(f, "%u %u %u", &w, &h, &max_val) != 3) { fclose(f); return img; } img = ppm_init(w, h); if (!img.pixels) { fclose(f); return img; } for (u32 i = 0; i < w * h; ++i) { int r, g, b; if (fscanf(f, "%d %d %d", &r, &g, &b) != 3) { ppm_free(&img); fclose(f); return (Canvas){0}; } img.pixels[i] = (Color){(u8)r, (u8)g, (u8)b}; } fclose(f); return img; } NONSTD_DEF void ppm_fill(Canvas *canvas, Color color) { for (u32 i = 0; i < canvas->width * canvas->height; ++i) { canvas->pixels[i] = color; } } NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color) { if (w == 0 || h == 0) return; for (u32 i = x; i < x + w; ++i) { ppm_set_pixel(canvas, i, y, color); ppm_set_pixel(canvas, i, y + h - 1, color); } for (u32 j = y; j < y + h; ++j) { ppm_set_pixel(canvas, x, j, color); ppm_set_pixel(canvas, x + w - 1, j, color); } } NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color) { i32 dx = abs(x1 - x0); i32 dy = -abs(y1 - y0); i32 sx = x0 < x1 ? 1 : -1; i32 sy = y0 < y1 ? 1 : -1; i32 err = dx + dy; while (1) { ppm_set_pixel(canvas, (u32)x0, (u32)y0, color); if (x0 == x1 && y0 == y1) { break; } i32 e2 = 2 * err; if (e2 >= dy) { err += dy; x0 += sx; } if (e2 <= dx) { err += dx; y0 += sy; } } } NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 xm, i32 ym, i32 r, Color color) { i32 x = -r, y = 0, err = 2 - 2 * r; do { ppm_set_pixel(canvas, (u32)(xm - x), (u32)(ym + y), color); ppm_set_pixel(canvas, (u32)(xm - y), (u32)(ym - x), color); ppm_set_pixel(canvas, (u32)(xm + x), (u32)(ym - y), color); ppm_set_pixel(canvas, (u32)(xm + y), (u32)(ym + x), color); r = err; if (r <= y) { err += ++y * 2 + 1; } if (r > x || err > y) { err += ++x * 2 + 1; } } while (x < 0); } NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color) { ppm_draw_line(canvas, x0, y0, x1, y1, color); ppm_draw_line(canvas, x1, y1, x2, y2, color); ppm_draw_line(canvas, x2, y2, x0, y0, color); } #endif // NONSTD_IMPLEMENTATION