From 68a526c3269d5a93de0e289056735de21bb60e4b Mon Sep 17 00:00:00 2001 From: Georgios Samaras Date: Sun, 1 Dec 2024 11:39:04 +0100 Subject: [PATCH] setup --- .gitignore | 2 + CMakeLists.txt | 14 ++++ aoc/aoc.c | 72 +++++++++++++++++ aoc/aoc.h | 66 ++++++++++++++++ aoc/arena.c | 85 ++++++++++++++++++++ aoc/arena.h | 40 ++++++++++ aoc/common.h | 173 +++++++++++++++++++++++++++++++++++++++++ aoc/str.h | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 659 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 aoc/aoc.c create mode 100644 aoc/aoc.h create mode 100644 aoc/arena.c create mode 100644 aoc/arena.h create mode 100644 aoc/common.h create mode 100644 aoc/str.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4fb4fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..052c647 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.10) +project(AdventOfCode-2024) + +add_library(aoc aoc/aoc.c aoc/arena.c) +target_include_directories(aoc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/aoc) + +file(GLOB DAY_DIRS day*) +message("DAY_DIRS: ${DAY_DIRS}") +foreach(DAY_DIR ${DAY_DIRS}) + get_filename_component(DAY ${DAY_DIR} NAME) + message("DAY: ${DAY}") + add_executable(${DAY} ${DAY_DIR}/main.c) + target_link_libraries(${DAY} aoc) +endforeach() diff --git a/aoc/aoc.c b/aoc/aoc.c new file mode 100644 index 0000000..4b14992 --- /dev/null +++ b/aoc/aoc.c @@ -0,0 +1,72 @@ +#define STR_IMPLEMENTATION +#include "str.h" +#include "aoc.h" +#include + +Arena *make_arena(ptrdiff_t size) { + void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + Arena *arena = arena_init(mem, size); + return arena; +} + +str read_file(Arena *arena, const char *filename) { + FILE *f = fopen(filename, "r"); + ASSERT(f); + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + char *data = ARENA_ALLOC_ARRAY(arena, char, size + 1); + size_t read_size = fread(data, 1, size, f); + ASSERT(read_size == size); + data[size] = '\0'; + fclose(f); + return make_str((uint8_t *) data, size); +} + +Tokens str_tokenize(Arena *arena, str s, str delim) { + // 1st pass: count number of tokens + + str stuff = s; + isize token_count = 0; + while (str_next_token(&stuff, delim).len > 0) { + token_count++; + } + + // 2nd pass: allocate memory and copy tokens + Tokens result = { + .len = token_count, + .tokens = ARENA_ALLOC_ARRAY(arena, str, token_count) + }; + ASSERT(result.tokens); + + stuff = s; + for (isize i = 0; i < token_count; ++i) { + result.tokens[i] = str_next_token(&stuff, delim); + } + + return result; +} + +Tokens read_lines(Arena *arena, const char *path) { + str file = read_file(arena, path); + Tokens lines = str_tokenize(arena, file, STR("\n")); + return lines; +} + + +char * +str_to_cstr(str s, Arena *a) { + char *result = ARENA_ALLOC_ARRAY(a, char, s.len + 1); + memcpy(result, s.data, s.len); + result[s.len] = '\0'; + return result; +} + +i64 +parse_i64(str s, Arena temp) { + char *cs = str_to_cstr(s, &temp); + char *endptr; + i64 result = strtol(cs, &endptr, 10); + ASSERT(*endptr == '\0'); + return result; +} diff --git a/aoc/aoc.h b/aoc/aoc.h new file mode 100644 index 0000000..cad676d --- /dev/null +++ b/aoc/aoc.h @@ -0,0 +1,66 @@ +#include "arena.h" +#include "str.h" + +#include + +Arena *make_arena(ptrdiff_t size); +str read_file(Arena *arena, const char *filename); + +typedef struct { + isize len; + str *tokens; +} Tokens; + +Tokens str_tokenize(Arena *arena, str s, str delim); +Tokens read_lines(Arena *arena, const char *path); + +char *str_to_cstr(str s, Arena *a); +i64 parse_i64(str s, Arena temp); + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#include +#define ASSERT(cond) if (!(cond)) { fprintf(stderr, "%s:%d (%s): Assertion failed: %s\n", __FILE__, __LINE__, __func__, #cond); __builtin_trap(); } + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int64_t i64; +typedef int32_t i32; +typedef int16_t i16; +typedef int8_t i8; + +typedef size_t usize; +typedef ptrdiff_t isize; + +typedef float f32; +typedef double f64; + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x)) + +#define Kilobytes(x) ((u64) (x) << 10) +#define Megabytes(x) ((u64) (x) << 20) +#define Gigabytes(x) ((u64) (x) << 30) + +#define push(slice, arena) \ + ((slice)->len >= ((slice)->cap) \ + ? grow(slice, sizeof(*(slice)->data), arena), \ + (slice)->data + (slice)->len++ \ + : (slice)->data + (slice)->len++) + +static inline void +grow(void *slice, isize elem_size, Arena *arena) { + struct { + void *data; + isize len; + isize cap; + } repl; + memcpy(&repl, slice, sizeof(repl)); + isize new_cap = MAX(16, 2 * repl.cap); + repl.data = arena_realloc_aligned(arena, repl.data, repl.cap * elem_size, new_cap * elem_size, elem_size); + repl.cap = new_cap; + memcpy(slice, &repl, sizeof(repl)); +} diff --git a/aoc/arena.c b/aoc/arena.c new file mode 100644 index 0000000..86cbe5c --- /dev/null +++ b/aoc/arena.c @@ -0,0 +1,85 @@ +#include "arena.h" +#include + +Arena * +arena_init(void *mem, ptrdiff_t size) { + Arena *arena = mem; + arena->mem = arena + 1; + arena->top = arena->mem; + arena->lim = arena->mem + size; + return arena; +} + +static void * +align_forward(void *ptr, ptrdiff_t align) { + return (void *)(((uintptr_t)ptr + align - 1) & ~(align - 1)); +} + +static void +arena_oom(Arena *arena, ptrdiff_t size, ptrdiff_t align) { + loge("Out of memory in arena %p: requested size %ld, align %ld", arena, size, align); + BP(); +} + +void * +arena_alloc_aligned(Arena *arena, ptrdiff_t size, ptrdiff_t align) { + uintptr_t start = (uintptr_t) align_forward(arena->top, align); + if (start + size > (uintptr_t) arena->lim) { + arena_oom(arena, size, align); + return NULL; + } + memset((void *)start, 0, size); + arena->top = (void *)(start + size); + return (void *)start; +} + +void * +arena_realloc_aligned(Arena *arena, void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, ptrdiff_t align) { + if (ptr) { + // check that pointer was allocated in this arena + ASSERT((uintptr_t) ptr >= (uintptr_t) arena->mem); + ASSERT((uintptr_t) ptr < (uintptr_t) arena->lim); + ASSERT((uintptr_t) ptr + old_size <= (uintptr_t) arena->top); + } + + void *result = NULL; + if (false && ptr && (uintptr_t) ptr + old_size == (uintptr_t) arena->top) { + // The block is at the top of the arena, so we can just attempt to extend it. + if ((uintptr_t) ptr + new_size <= (uintptr_t) arena->lim) { + // clear new memory + memset((char *) ptr + old_size, 0, new_size - old_size); + arena->top = ((char *) ptr + new_size); + result = ptr; + } + else { + arena_oom(arena, new_size, align); + } + } + else { + result = arena_alloc_aligned(arena, new_size, align); + if (result) { + if (ptr) { + memcpy(result, ptr, old_size); + } + } + else { + arena_oom(arena, new_size, align); + } + } + return result; +} + +void +arena_clear(Arena *arena) { + arena_clear_overwrite(arena); + arena->top = arena->mem; +} + +void arena_clear_overwrite_with(Arena *arena, u8 value) { + memset(arena->mem, value, (uintptr_t) arena->top - (uintptr_t) arena->mem); + arena->top = arena->mem; +} + +void arena_clear_overwrite(Arena *arena) { + arena_clear_overwrite_with(arena, 0xCC); +} diff --git a/aoc/arena.h b/aoc/arena.h new file mode 100644 index 0000000..cd5b786 --- /dev/null +++ b/aoc/arena.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include "common.h" + +typedef struct Arena { + void *mem, *top, *lim; +} Arena; + +Arena *arena_init(void *mem, ptrdiff_t size); +void *arena_alloc_aligned(Arena *arena, ptrdiff_t size, ptrdiff_t align); +void *arena_realloc_aligned(Arena *arena, void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, ptrdiff_t align); +void arena_clear(Arena *arena); +void arena_clear_overwrite(Arena *arena); +void arena_clear_overwrite_with(Arena *arena, u8 value); + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L /* C11 */ +#define ALIGNOF(t) _Alignof(t) +#elif __GNUC__ +#define ALIGNOF(t) __alignof(t) +#else +#error "No alignof" +#endif + +#define ARENA_ALLOC(a, t) ((t *)arena_alloc_aligned(a, sizeof(t), ALIGNOF(t))) +#define ARENA_ALLOC_ARRAY(a, t, n) ((t *)arena_alloc_aligned(a, sizeof(t) * n, ALIGNOF(t))) + +#define ARENA_REALLOC_ARRAY(a, t, p, old_n, new_n) ((t *)arena_realloc_aligned(a, p, sizeof(t) * old_n, sizeof(t) * new_n, ALIGNOF(t))) + + +static inline void * +arena_copy_array(Arena *arena, void *ptr, isize size, isize align) { + void *result = arena_alloc_aligned(arena, size, align); + if (result) { + memcpy(result, ptr, size); + } + return result; +} + +#define ARENA_COPY_ARRAY(a, t, p, n) ((t *)arena_copy_array(a, p, sizeof(t) * n, ALIGNOF(t))) diff --git a/aoc/common.h b/aoc/common.h new file mode 100644 index 0000000..c076154 --- /dev/null +++ b/aoc/common.h @@ -0,0 +1,173 @@ +#pragma once + +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#include +#define ASSERT(cond) if (!(cond)) { fprintf(stderr, "%s:%d (%s): Assertion failed: %s\n", __FILE__, __LINE__, __func__, #cond); __builtin_trap(); } + +#define NOT_IMPLEMENTED() ASSERT(0 && "Not implemented") +#define NOT_REACHABLE() ASSERT(0 && "Not reachable") +#define INCOMPLETE(cond) if (!(cond)) { fprintf(stderr, "%s:%d (%s): Incomplete: %s\n", __FILE__, __LINE__, __func__, #cond); __builtin_trap(); } +#define BP() asm("int3;nop;") + +#define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0) + +#define container_of(ptr, type, member) (type *)((char *)(ptr) - offsetof(type, member)) + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int64_t i64; +typedef int32_t i32; +typedef int16_t i16; +typedef int8_t i8; + +typedef size_t usize; +typedef ptrdiff_t isize; + +typedef float f32; +typedef double f64; + + +#define log(level, fmt, ...) fprintf(stderr, "[" level "] %s:%d (%s): " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__) + +#define LOG_LEVEL_TRACE 6 +#define LOG_LEVEL_DEBUG 5 +#define LOG_LEVEL_VERBOSE 4 +#define LOG_LEVEL_INFO 3 +#define LOG_LEVEL_WARNING 2 +#define LOG_LEVEL_ERROR 1 +#define LOG_LEVEL_FATAL 0 + +#define LOG_LEVEL LOG_LEVEL_DEBUG + +#if (LOG_LEVEL >= LOG_LEVEL_TRACE) +#define logt(fmt, ...) log("TRACE", fmt, ##__VA_ARGS__) +#else +#define logt(fmt, ...) +#endif + +#if (LOG_LEVEL >= LOG_LEVEL_DEBUG) +#define logd(fmt, ...) log("\033[1;34mDEBUG\033[0m", fmt, ##__VA_ARGS__) +#else +#define logd(fmt, ...) +#endif + +#if (LOG_LEVEL >= LOG_LEVEL_VERBOSE) +#define logv(fmt, ...) log("\033[1;32mVERBOSE\033[0m", fmt, ##__VA_ARGS__) +#else +#define logv(fmt, ...) +#endif + +#if (LOG_LEVEL >= LOG_LEVEL_INFO) +#define logi(fmt, ...) log("\033[1;32mINFO\033[0m", fmt, ##__VA_ARGS__) +#else +#define logi(fmt, ...) +#endif + +#if (LOG_LEVEL >= LOG_LEVEL_WARNING) +#define logw(fmt, ...) log("\033[1;33mWARNING\033[0m", fmt, ##__VA_ARGS__) +#else +#define logw(fmt, ...) +#endif + +#if (LOG_LEVEL >= LOG_LEVEL_ERROR) +#define loge(fmt, ...) log("\033[1;31mERROR\033[0m", fmt, ##__VA_ARGS__) +#else +#define loge(fmt, ...) +#endif + +#if (LOG_LEVEL >= LOG_LEVEL_FATAL) +#define logf(fmt, ...) do { log("\033[1;31mFATAL\033[0m", fmt, ##__VA_ARGS__); __builtin_trap(); } while (0) +#else +#define logf(fmt, ...) +#endif + + +#define Kilobytes(x) ((u64) (x) << 10) +#define Megabytes(x) ((u64) (x) << 20) +#define Gigabytes(x) ((u64) (x) << 30) + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x)) + +typedef union Vec2 { + struct { f32 x, y; }; + struct { f32 u, v; }; +} Vec2; + +typedef union Vec2i { + struct { i32 x, y; }; + struct { i32 u, v; }; +} Vec2i ; + +typedef struct Mat4 { + float e[4][4]; +} Mat4; + + +static inline Vec2 +vec2_add(Vec2 a, Vec2 b) { + Vec2 result = { + .x = a.x + b.x, + .y = a.y + b.y, + }; + return result; +} + +static inline Vec2 +vec2_sub(Vec2 a, Vec2 b) { + Vec2 result = { + .x = a.x - b.x, + .y = a.y - b.y, + }; + return result; +} + +static inline Vec2 +vec2(f32 x, f32 y) { + Vec2 result = { x, y }; + return result; +} + +static inline f32 +vec2_dot(Vec2 a, Vec2 b) { + return a.x * b.x + a.y * b.y; +} + +static inline f32 +vec2_norm2(Vec2 v) { + return vec2_dot(v, v); +} + +static inline f32 +vec2_norm(Vec2 v) { + return sqrtf(vec2_norm2(v)); +} + +static inline f32 +vec2_dist(Vec2 a, Vec2 b) { + return vec2_norm(vec2_sub(a, b)); +} + +typedef struct Rect2i { + int x, y, w, h; +} Rect2i; + +typedef struct Rect2f { + f32 x, y, w, h; +} Rect2f; + +static inline Rect2f +rect2f(f32 x, f32 y, f32 w, f32 h) { + Rect2f result = { x, y, w, h }; + return result; +} diff --git a/aoc/str.h b/aoc/str.h new file mode 100644 index 0000000..3ac5d2b --- /dev/null +++ b/aoc/str.h @@ -0,0 +1,207 @@ +#ifndef STR_H +#define STR_H + +#include +#include +#include + +typedef struct { + uint8_t *data; + size_t len; +} str; + +typedef struct { + bool found; + size_t offset; +} str_find_result; + +#define STR(s) (str) { .data = (uint8_t *) s, .len = sizeof(s) - 1 } + +str make_str(uint8_t *data, size_t len); +str cstr(const char *cstr); +str str_next_token(str *source, str delim); +str str_sub(str s, size_t start, size_t end); +str str_trim_left(str s); +str str_trim_right(str s); +str str_trim(str s); +bool str_eq(str a, str b); +bool str_starts_with(str s, str prefix); +bool str_ends_with(str s, str suffix); +bool str_empty(str s); +void str_split(str s, str delim, str *first, str *second); + +str_find_result str_find_left(str haystack, str needle); +str_find_result str_find_right(str haystack, str needle); + +void str_print(str s); + +#define STR_FMT "%.*s" +#define STR_ARG(s) (int) (s).len, (const char *) (s).data + +#ifdef STR_IMPLEMENTATION + +#include + +str make_str(uint8_t *data, size_t len) { + return (str) { .data = data, .len = len }; +} + +str cstr(const char *cstr) { + size_t len = 0; + for (const char *it = cstr; *it; ++it) { + len++; + } + return (str) { + .data = (uint8_t *) cstr, + .len = len + }; +} + +str str_sub(str s, size_t start, size_t end) { + return (str) { + .data = s.data + start, + .len = end - start + }; +} + +static inline bool is_whitespace(int ch) { + return ch == ' ' || ch == '\r' || ch == '\n' + || ch == '\t' || ch == '\v' || ch == '\f'; +} + +str str_trim_left(str s) { + // TODO make more efficient! + while (s.len > 0 && is_whitespace(s.data[0])) { + s.data++; + s.len--; + } + return s; +} + +str str_trim_right(str s) { + // TODO make more efficient! + while (s.len > 0 && is_whitespace(s.data[s.len - 1])) { + s.len--; + } + return s; +} + +str str_trim(str s) { + return str_trim_right(str_trim_left(s)); +} + +bool str_eq(str a, str b) { + if (a.len != b.len) { + return false; + } + else { + for (size_t i = 0; i < a.len; ++i) { + if (a.data[i] != b.data[i]) { + return false; + } + } + return true; + } +} + +bool str_starts_with(str s, str prefix) { + if (prefix.len > s.len) { + return false; + } + for (size_t index = 0; index < prefix.len; ++index) { + if (s.data[index] != prefix.data[index]) { + return false; + } + } + return true; +} + +bool str_ends_with(str s, str suffix) { + if (suffix.len > s.len) { + return false; + } + size_t offset = s.len - suffix.len; + for (size_t index = 0; index < suffix.len; ++index) { + if (s.data[offset + index] != suffix.data[index]) { + return false; + } + } + return true; +} + +bool str_empty(str s) { + return s.len == 0; +} + +void str_print(str s) { + printf("%.*s", (int) s.len, s.data); +} + +str_find_result str_find_left(str haystack, str needle) { + if (needle.len > haystack.len) { + return (str_find_result) { .found = false }; + } + for (size_t start = 0; start < haystack.len - needle.len; ++start) { + bool found = true; + for (size_t offset = 0; offset < needle.len; ++offset) { + if (haystack.data[start + offset] != needle.data[offset]) { + found = false; + break; + } + } + if (found) { + return (str_find_result) { .found = true, .offset = start }; + } + } + return (str_find_result) { .found = false }; +} + +str_find_result str_find_right(str haystack, str needle) { + if (needle.len > haystack.len) { + return (str_find_result) { .found = false }; + } + // TODO is intmax_t a good idea? + for (intmax_t start = haystack.len - needle.len; + start >= 0; + ++start) + { + bool found = true; + for (size_t offset = 0; offset < needle.len; ++offset) { + if (haystack.data[start + offset] != needle.data[offset]) { + found = false; + break; + } + } + if (found) { + return (str_find_result) { .found = true, .offset = start }; + } + } + return (str_find_result) { .found = false }; +} + +str str_next_token(str *source, str delim) { + str_find_result match = str_find_left(*source, delim); + if (match.found) { + size_t skip_len = match.offset + delim.len; + str result = make_str(source->data, match.offset); + source->data += skip_len; + source->len -= skip_len; + return result; + } + else { + // if we reach here, we have reached the end of `source` without + // encountering the delimiter -> return the remainder of the string. + str result = *source; + source->data += source->len; + source->len = 0; + return result; + } +} + +void str_split(str s, str delim, str *first, str *second) { + *first = str_next_token(&s, delim); + *second = s; +} + +#endif /* STR_IMPLEMENTATION */ +#endif /* STR_H */