This commit is contained in:
Szymon Nowakowski 2024-12-05 21:22:19 +01:00
parent cc23753573
commit b6c66136f7
14 changed files with 452 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
bin/
in/
lib/
obj/
out/

5
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"recommendations": [
"ms-vscode.cpptools",
],
}

8
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"C_Cpp.default.includePath": [
"stdlib/include",
],
"files.associations": {
"*.h": "c",
},
}

18
Makefile Normal file
View File

@ -0,0 +1,18 @@
STDLIB_HEADERS := $(wildcard stdlib/include/*)
STDLIB_SOURCES := $(wildcard stdlib/src/*)
TARGETS := $(patsubst src/%.c,bin/%,$(wildcard src/*.c))
CFLAGS := -O2 -nostdlib -Istdlib/include -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-builtin -std=c99 -pedantic -Wall -Werror -fno-stack-protector -static -Wl,-n
.PHONY: all clean
all: $(TARGETS)
bin/%: src/%.c $(STDLIB_SOURCES) $(STDLIB_HEADERS)
@mkdir -p $(@D)
gcc $(CFLAGS) $< $(STDLIB_SOURCES) -o $@
strip -R .comment $@
clean:
rm -rf bin

16
README.md Normal file
View File

@ -0,0 +1,16 @@
# Advent of Code 2024
Prerequisites:
* Linux (WSL may work)
* x86_64 CPU
* gcc
* make
## Building
Run `make` to build everything.
## Running
Every program accepts the input through `stdin` and outputs through `stdout`.

200
src/01.c Normal file
View File

@ -0,0 +1,200 @@
#include <common.h>
#include <arena.h>
#include <buffer.h>
#include <syscall.h>
#define BUFFER_SIZE 4096UL
#define ARENA_CAPACITY 2147483648UL
typedef enum parser_state {
parser_state_left,
parser_state_whitespace,
parser_state_right,
parser_state_eol,
} parser_state;
typedef struct parser {
i32 left;
i32 right;
parser_state state;
} parser;
arena left_arena;
arena right_arena;
arena left_occurences_arena;
arena right_occurences_arena;
usize number_pair_count = 0;
i32 max_left = -1;
i32 max_right = -1;
void parser_process(parser* parser, const c8* ptr, usize len);
void interpreter_process(i32 left, i32 right);
bool is_digit(c8 c);
void insert_sort(i32* ptr, usize len);
void println_u64(u64 value);
i32 main(i32 argc, char *argv[])
{
c8* buffer = buffer_init(BUFFER_SIZE);
left_arena = arena_init(ARENA_CAPACITY);
right_arena = arena_init(ARENA_CAPACITY);
left_occurences_arena = arena_init(ARENA_CAPACITY);
right_occurences_arena = arena_init(ARENA_CAPACITY);
usize buffer_ptr = 0;
isize bytes_read = 0;
parser parser = {0};
while ((bytes_read = read(stdin, &buffer[buffer_ptr], BUFFER_SIZE)) > 0) {
parser_process(&parser, &buffer[buffer_ptr], bytes_read);
buffer_ptr = (buffer_ptr + bytes_read) % BUFFER_SIZE;
}
i32 *left_array = left_arena.ptr;
i32 *right_array = right_arena.ptr;
insert_sort(left_array, number_pair_count);
insert_sort(right_array, number_pair_count);
// Calculate part I
u64 cumdiff = 0;
for (usize i = 0; i < number_pair_count; ++i)
{
i32 diff = left_array[i] - right_array[i];
if (diff < 0) {
diff = -diff;
}
cumdiff += diff;
}
println_u64(cumdiff);
// Calculate part II
u16 *right_occurences = right_occurences_arena.ptr;
u64 similarity = 0;
for (usize i = 0; i < number_pair_count; ++i)
{
i32 left = left_array[i];
if (left > max_right) continue;
similarity += (u64)left * (u64)right_occurences[left];
}
println_u64(similarity);
return 0;
}
void parser_process(parser* parser, const c8* ptr, usize len)
{
const c8* end = &ptr[len];
while (ptr < end) {
c8 c = *ptr;
switch (parser->state) {
case parser_state_left:
if (is_digit(c)) {
parser->left = parser->left * 10 + (c - '0');
ptr += 1;
} else {
parser->state = parser_state_whitespace;
}
break;
case parser_state_whitespace:
if (is_digit(c)) {
parser->state = parser_state_right;
} else {
ptr += 1;
}
break;
case parser_state_right:
if (is_digit(c)) {
parser->right = parser->right * 10 + (c - '0');
ptr += 1;
} else {
parser->state = parser_state_eol;
interpreter_process(parser->left, parser->right);
parser->left = 0;
parser->right = 0;
}
break;
case parser_state_eol:
if (is_digit(c)) {
parser->state = parser_state_left;
} else {
ptr += 1;
}
break;
}
}
}
void interpreter_process(i32 left, i32 right)
{
i32 *left_array = left_arena.ptr;
i32 *right_array = right_arena.ptr;
u16 *left_occurences = left_occurences_arena.ptr;
u16 *right_occurences = right_occurences_arena.ptr;
ARENA_ENSURE(i32, &left_arena, number_pair_count + 1);
ARENA_ENSURE(i32, &right_arena, number_pair_count + 1);
ARENA_ENSURE(u16, &left_occurences_arena, left + 1);
ARENA_ENSURE(u16, &right_occurences_arena, right + 1);
left_array[number_pair_count] = left;
right_array[number_pair_count] = right;
left_occurences[left] += 1;
right_occurences[right] += 1;
if (left > max_left) max_left = left;
if (right > max_right) max_right = right;
number_pair_count += 1;
}
bool is_digit(c8 c)
{
return c >= '0' && c <= '9';
}
void insert_sort(i32* ptr, usize len)
{
for (usize i = 1; i < len; ++i)
{
i32 x = ptr[i];
usize j = i;
while (j > 0 && ptr[j - 1] > x) {
ptr[j] = ptr[j - 1];
j -= 1;
}
ptr[j] = x;
}
}
void println_u64(u64 value)
{
/* The longest string we produce would be "18446744073709551615\n", which is
* 21 bytes long (we don't care about the null terminator).
*/
c8 buffer[21];
usize strlen = 1;
buffer[20] = '\n';
do
{
c8 digit = (value % 10) + '0';
buffer[20 - strlen] = digit;
strlen += 1;
value /= 10;
} while (value > 0);
write(stdout, &buffer[21 - strlen], strlen);
}

18
stdlib/include/arena.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <common.h>
typedef struct arena {
void* ptr;
usize available;
usize capacity;
} arena;
// The capacity has to be page-aligned
arena arena_init(usize capacity);
// The available size will be rounded up to a page boundary
// Will not grow beyond its capacity
void arena_ensure(arena* arena, usize available);
#define ARENA_ENSURE(T, arena, available) arena_ensure(arena, (available) * sizeof(T))

6
stdlib/include/buffer.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <common.h>
// The size has to be page-aligned
void* buffer_init(usize size);

27
stdlib/include/common.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
typedef char c8;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long u64;
typedef signed char i8;
typedef signed short i16;
typedef signed int i32;
typedef signed long i64;
typedef unsigned long int usize;
typedef signed long isize;
#define asm __asm__
#define null ((void*)0)
#define bool _Bool
#define false 0
#define true 1
#define COUNT(array) (sizeof(array) / sizeof(array[0]))
#define PAGE_SIZE 4096UL

24
stdlib/include/syscall.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <common.h>
#define stdin 0
#define stdout 1
#define stderr 2
#define PROT_NONE 0x0
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
isize read(i32 fd, void* buf, usize size);
isize write(i32 fd, const void* buf, usize size);
void* mmap(void* addr, usize length, i32 prot, i32 flags, i32 fd, isize offset);
i32 mprotect(void* addr, usize len, i32 prot);
i32 ftruncate(i32 fd, isize length);
i32 memfd_create(const char* name, u32 flags);

22
stdlib/src/arena.c Normal file
View File

@ -0,0 +1,22 @@
#include <common.h>
#include <arena.h>
#include <syscall.h>
arena arena_init(usize capacity)
{
void* ptr = mmap(null, capacity, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return (arena){ .ptr = ptr, .available = 0, .capacity = capacity };
}
void arena_ensure(arena* arena, usize available)
{
if (arena->available >= available) return;
available = (available + PAGE_SIZE - 1) & -PAGE_SIZE;
if (available > arena->capacity) {
available = arena->capacity;
}
mprotect((c8*)arena->ptr + arena->available, available - arena->available, PROT_READ | PROT_WRITE);
arena->available = available;
}

14
stdlib/src/buffer.c Normal file
View File

@ -0,0 +1,14 @@
#include <common.h>
#include <syscall.h>
void* buffer_init(usize size)
{
i32 fd = memfd_create("buffer", 0);
ftruncate(fd, size);
void* ptr = mmap(null, 2 * size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
mmap( ptr, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
mmap((c8*)ptr + size, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
return ptr;
}

12
stdlib/src/start.s Normal file
View File

@ -0,0 +1,12 @@
.intel_syntax noprefix
.text
.global _start
_start:
xor rbp, rbp
pop rdi
mov rsi, rsp
and rsp, -16
call main
mov rdi, rax
mov rax, 60
syscall

77
stdlib/src/syscall.c Normal file
View File

@ -0,0 +1,77 @@
#include <syscall.h>
isize read(i32 fd, void* buf, usize size)
{
isize ret;
asm volatile (
"syscall"
: "=a" (ret)
: "a" (0), "D" (fd), "S" (buf), "d" (size)
: "rcx", "r11", "memory"
);
return ret;
}
isize write(i32 fd, const void* buf, usize size)
{
isize ret;
asm volatile (
"syscall"
: "=a" (ret)
: "a" (1), "D" (fd), "S" (buf), "d" (size)
: "rcx", "r11", "memory"
);
return ret;
}
void* mmap(void* addr, usize length, i32 prot, i32 flags, i32 fd, isize offset)
{
register i32 r10 asm ("r10") = flags;
register i32 r8 asm ("r8") = fd;
register isize r9 asm ("r9") = offset;
void* ret;
asm volatile (
"syscall"
: "=a" (ret)
: "a" (9), "D" (addr), "S" (length), "d" (prot), "r" (r10), "r" (r8), "r" (r9)
: "rcx", "r11", "memory"
);
return ret;
}
i32 mprotect(void* addr, usize len, i32 prot)
{
i32 ret;
asm volatile (
"syscall"
: "=a" (ret)
: "a" (10), "D" (addr), "S" (len), "d" (prot)
: "rcx", "r11", "memory"
);
return ret;
}
i32 ftruncate(i32 fd, isize length)
{
i32 ret;
asm volatile (
"syscall"
: "=a" (ret)
: "a" (77), "D" (fd), "S" (length)
: "rcx", "r11", "memory"
);
return ret;
}
i32 memfd_create(const char* name, u32 flags)
{
i32 ret;
asm volatile (
"syscall"
: "=a" (ret)
: "a" (319), "D" (name), "S" (flags)
: "rcx", "r11", "memory"
);
return ret;
}