setup
This commit is contained in:
commit
68a526c326
|
|
@ -0,0 +1,2 @@
|
|||
build/
|
||||
.cache/
|
||||
|
|
@ -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()
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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)))
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
||||
Loading…
Reference in New Issue