From b6c66136f7c9859fe0754822188a0d0924e1439c Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Thu, 5 Dec 2024 21:22:19 +0100 Subject: [PATCH] Day 1 --- .gitignore | 5 + .vscode/extensions.json | 5 + .vscode/settings.json | 8 ++ Makefile | 18 ++++ README.md | 16 ++++ src/01.c | 200 +++++++++++++++++++++++++++++++++++++++ stdlib/include/arena.h | 18 ++++ stdlib/include/buffer.h | 6 ++ stdlib/include/common.h | 27 ++++++ stdlib/include/syscall.h | 24 +++++ stdlib/src/arena.c | 22 +++++ stdlib/src/buffer.c | 14 +++ stdlib/src/start.s | 12 +++ stdlib/src/syscall.c | 77 +++++++++++++++ 14 files changed, 452 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/01.c create mode 100644 stdlib/include/arena.h create mode 100644 stdlib/include/buffer.h create mode 100644 stdlib/include/common.h create mode 100644 stdlib/include/syscall.h create mode 100644 stdlib/src/arena.c create mode 100644 stdlib/src/buffer.c create mode 100644 stdlib/src/start.s create mode 100644 stdlib/src/syscall.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..855cdab --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +in/ +lib/ +obj/ +out/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..90d772e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-vscode.cpptools", + ], +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a80517c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "C_Cpp.default.includePath": [ + "stdlib/include", + ], + "files.associations": { + "*.h": "c", + }, +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eb6fce4 --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f08bc0 --- /dev/null +++ b/README.md @@ -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`. diff --git a/src/01.c b/src/01.c new file mode 100644 index 0000000..a2ec988 --- /dev/null +++ b/src/01.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#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); +} diff --git a/stdlib/include/arena.h b/stdlib/include/arena.h new file mode 100644 index 0000000..0b49bf2 --- /dev/null +++ b/stdlib/include/arena.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +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)) diff --git a/stdlib/include/buffer.h b/stdlib/include/buffer.h new file mode 100644 index 0000000..cc4b759 --- /dev/null +++ b/stdlib/include/buffer.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +// The size has to be page-aligned +void* buffer_init(usize size); diff --git a/stdlib/include/common.h b/stdlib/include/common.h new file mode 100644 index 0000000..4143223 --- /dev/null +++ b/stdlib/include/common.h @@ -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 diff --git a/stdlib/include/syscall.h b/stdlib/include/syscall.h new file mode 100644 index 0000000..543993e --- /dev/null +++ b/stdlib/include/syscall.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#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); diff --git a/stdlib/src/arena.c b/stdlib/src/arena.c new file mode 100644 index 0000000..2a7f647 --- /dev/null +++ b/stdlib/src/arena.c @@ -0,0 +1,22 @@ +#include +#include +#include + +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; +} diff --git a/stdlib/src/buffer.c b/stdlib/src/buffer.c new file mode 100644 index 0000000..7b5d6cd --- /dev/null +++ b/stdlib/src/buffer.c @@ -0,0 +1,14 @@ +#include +#include + +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; +} diff --git a/stdlib/src/start.s b/stdlib/src/start.s new file mode 100644 index 0000000..df1ad32 --- /dev/null +++ b/stdlib/src/start.s @@ -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 diff --git a/stdlib/src/syscall.c b/stdlib/src/syscall.c new file mode 100644 index 0000000..3a7de65 --- /dev/null +++ b/stdlib/src/syscall.c @@ -0,0 +1,77 @@ +#include + +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; +}