This commit is contained in:
Georgios Samaras 2024-12-01 11:39:04 +01:00
commit 68a526c326
8 changed files with 659 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build/
.cache/

14
CMakeLists.txt Normal file
View File

@ -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()

72
aoc/aoc.c Normal file
View File

@ -0,0 +1,72 @@
#define STR_IMPLEMENTATION
#include "str.h"
#include "aoc.h"
#include <stdlib.h>
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;
}

66
aoc/aoc.h Normal file
View File

@ -0,0 +1,66 @@
#include "arena.h"
#include "str.h"
#include <sys/mman.h>
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 <stdio.h>
#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));
}

85
aoc/arena.c Normal file
View File

@ -0,0 +1,85 @@
#include "arena.h"
#include <string.h>
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);
}

40
aoc/arena.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <string.h>
#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)))

173
aoc/common.h Normal file
View File

@ -0,0 +1,173 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#include <stdio.h>
#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;
}

207
aoc/str.h Normal file
View File

@ -0,0 +1,207 @@
#ifndef STR_H
#define STR_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
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 <stdio.h>
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 */