commit 65268cad37d9a7a254e3160b4b814e97c9a7cc7c Author: David Gonzalez Martin Date: Thu Mar 20 20:12:35 2025 +0100 First commit diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..c94cf2b --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,16 @@ +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build + run: | + ./build.sh + - name: Install + run: | + ./install.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f05774 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/index.html +/public/ +/generate diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..683917b --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -eu +mkdir -p bin +clang src/generate.c -o bin/generate -std=gnu2x -g +rm -rf public || true +./bin/generate diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..0cadf87 --- /dev/null +++ b/install.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eux +rm -rf /var/www/html/birth-software.com/{*,.*} +cp -r ./public/* /var/www/html/birth-software.com diff --git a/src/generate.c b/src/generate.c new file mode 100644 index 0000000..ce82437 --- /dev/null +++ b/src/generate.c @@ -0,0 +1,378 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#define global_variable static +#define strlit_len(s) (sizeof(s) - 1) +#define strlit(s) (String){ .pointer = (u8*)(s), .length = strlit_len(s), } + +#if _MSC_VER +#define trap() __fastfail(1) +#elif __has_builtin(__builtin_trap) +#define trap() __builtin_trap() +#else +extern BB_NORETURN BB_COLD void abort(void); +fn BB_NORETURN BB_COLD void trap_ext() +{ +#ifdef __x86_64__ + asm volatile("ud2"); +#else + abort(); +#endif +} +#define trap() (trap_ext(), __builtin_unreachable()) +#endif + +#ifndef BB_DEBUG +#define BB_DEBUG 1 +#endif + +#ifndef BB_SAFETY +#define BB_SAFETY BB_DEBUG +#endif + +#define unused(x) (void)(x) + +#if _MSC_VER +#define BB_NORETURN __declspec(noreturn) +#define BB_COLD __declspec(noinline) +#elif defined(__TINYC__) +#define BB_NORETURN __attribute__((noreturn)) +#define BB_COLD __attribute__((cold)) +#else +#define BB_NORETURN [[noreturn]] +#define BB_COLD [[gnu::cold]] +#endif + + +#if _MSC_VER +#define expect(x, b) (!!(x)) +#else +#define expect(x, b) __builtin_expect(!!(x), b) +#endif +#define likely(x) expect(x, 1) +#define unlikely(x) expect(x, 0) +#define panic(format, ...) (os_exit(1)) + +#if BB_DEBUG +#define assert(x) (unlikely(!(x)) ? panic("Assert failed: \"" # x "\" at {cstr}:{u32}\n", __FILE__, __LINE__) : unused(0)) +#else +#define assert(x) unused(likely(x)) +#endif + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +#define fn static + +#define KB(n) ((n) * 1024) +#define MB(n) ((n) * 1024 * 1024) +#define GB(n) ((u64)(n) * 1024 * 1024 * 1024) +#define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024) + + +#define STRUCT_FORWARD_DECL(S) typedef struct S S +#define STRUCT(S) STRUCT_FORWARD_DECL(S); struct S +#define UNION_FORWARD_DECL(U) typedef union U U +#define UNION(U) UNION_FORWARD_DECL(U); union U + +#define Slice(T) Slice_ ## T +#define SliceP(T) SliceP_ ## T +#define declare_slice_ex(T, StructName) STRUCT(StructName) \ +{\ + T* pointer;\ + u64 length;\ +} + +#define declare_slice(T) declare_slice_ex(T, Slice(T)) +#define declare_slice_p(T) declare_slice_ex(T*, SliceP(T)) + +declare_slice(u8); +typedef Slice(u8) String; + +#define VirtualBuffer(T) VirtualBuffer_ ## T +#define VirtualBufferP(T) VirtualBufferPointerTo_ ## T + +#define decl_vb_ex(T, StructName) \ +struct StructName \ +{\ + T* pointer;\ + u32 length;\ + u32 capacity;\ +};\ +typedef struct StructName StructName + +#define decl_vb(T) decl_vb_ex(T, VirtualBuffer(T)) +#define decl_vbp(T) decl_vb_ex(T*, VirtualBufferP(T)) + +decl_vb(u8); +decl_vbp(u8); +decl_vb(u16); +decl_vbp(u16); +decl_vb(u32); +decl_vbp(u32); + +#define vb_size_of_element(vb) sizeof(*((vb)->pointer)) +#define vb_add(vb, count) (typeof((vb)->pointer)) vb_generic_add((VirtualBuffer(u8)*)(vb), (vb_size_of_element(vb)), (count)) +#define vb_add_scalar(vb, S) (S*) vb_generic_add(vb, 1, sizeof(S)) +#define vb_copy_scalar(vb, s) *vb_add_scalar(vb, typeof(s)) = s +#define vb_append_struct(vb, T, s) *(vb_add_struct(vb, T)) = s +#define vb_append_one(vb, item) (typeof((vb)->pointer)) vb_generic_append((VirtualBuffer(u8)*)(vb), &(item), (vb_size_of_element(vb)), 1) +#define vb_to_bytes(vb) (Slice(u8)) { .pointer = (u8*)((vb).pointer), .length = (vb_size_of_element(vb)) * (vb).length, } +#define vb_ensure_capacity(vb, count) vb_generic_ensure_capacity((VirtualBuffer(u8)*)(vb), vb_size_of_element(vb), (count)) +#define vb_copy_array(vb, arr) memcpy(vb_add(vb, array_length(arr)), arr, sizeof(arr)) +#define vb_add_any_array(vb, E, count) (E*)vb_generic_add(vb, vb_size_of_element(vb), sizeof(E) * count) +#define vb_copy_any_array(vb, arr) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(arr)), (arr), sizeof(arr)) +#define vb_copy_any_slice(vb, slice) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(*((slice).pointer)) * (slice).length), (slice).pointer, sizeof(*((slice).pointer)) * (slice).length) + +fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count); +fn u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count); +fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count); +fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes); +fn u32 vb_copy_string(VirtualBuffer(u8)* buffer, String string); +fn u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string); + +STRUCT(OSReserveProtectionFlags) +{ + u32 read:1; + u32 write:1; + u32 execute:1; + u32 reserved:29; +}; + +STRUCT(OSReserveMapFlags) +{ + u32 priv:1; + u32 anon:1; + u32 noreserve:1; + u32 reserved:29; +}; + +#define let_pointer_cast(PointerChildType, var_name, value) PointerChildType* var_name = (PointerChildType*)(value) +#if defined(__TINYC__) || defined(_MSC_VER) +#define let(name, value) typeof(value) name = (value) +#else +#define let(name, value) __auto_type name = (value) +#endif +#define let_cast(T, name, value) T name = cast_to(T, value) +#define assign_cast(to, from) to = cast_to(typeof(to), from) +#define let_va_arg(T, name, args) T name = va_arg(args, T) +#define transmute(D, source) *(D*)&source + +#if BB_SAFETY +#define cast_to(T, value) (assert((typeof(value)) (T) (value) == (value) && ((value) > 0) == ((T) (value) > 0)), (T) (value)) +#else +#define cast_to(T, value) (T)(value) +#endif + +#ifndef __APPLE__ +#define MY_PAGE_SIZE KB(4) +#else +#define MY_PAGE_SIZE KB(16) +#endif +global_variable const u64 page_size = MY_PAGE_SIZE; + +global_variable u64 minimum_granularity = MY_PAGE_SIZE; +// global_variable u64 middle_granularity = MB(2); +global_variable u64 default_size = GB(4); + +fn u64 align_forward(u64 value, u64 alignment) +{ + u64 mask = alignment - 1; + u64 result = (value + mask) & ~mask; + return result; +} + +fn u8 os_is_being_debugged() +{ + u8 result = 0; +#if _WIN32 + result = IsDebuggerPresent() != 0; +#else +#ifdef __APPLE__ + let(request, PT_TRACE_ME); +#else + let(request, PTRACE_TRACEME); +#endif + if (ptrace(request, 0, 0, 0) == -1) + { + let(error, errno); + if (error == EPERM) + { + result = 1; + } + } +#endif + + return result; +} + +BB_NORETURN BB_COLD fn void os_exit(u32 exit_code) +{ + if (exit_code != 0 && os_is_being_debugged()) + { + trap(); + } + _exit(exit_code); +} + +fn u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map) +{ +#if _WIN32 + DWORD map_flags = 0; + map_flags |= (MEM_RESERVE * map.noreserve); + DWORD protection_flags = 0; + protection_flags |= PAGE_READWRITE * (!protection.write && !protection.read); + protection_flags |= PAGE_READWRITE * (protection.write && protection.read); + protection_flags |= PAGE_READONLY * (protection.write && !protection.read); + return (u8*)VirtualAlloc((void*)base, size, map_flags, protection_flags); +#else + int protection_flags = (protection.read * PROT_READ) | (protection.write * PROT_WRITE) | (protection.execute * PROT_EXEC); + int map_flags = (map.anon * MAP_ANONYMOUS) | (map.priv * MAP_PRIVATE) | (map.noreserve * MAP_NORESERVE); + u8* result = (u8*)mmap((void*)base, size, protection_flags, map_flags, -1, 0); + assert(result != MAP_FAILED); + return result; +#endif +} + +fn void os_commit(void* address, u64 size) +{ +#if _WIN32 + VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE); +#else + int result = mprotect(address, size, PROT_READ | PROT_WRITE); + assert(result == 0); +#endif +} + +fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) +{ + u32 old_capacity = vb->capacity; + u32 wanted_capacity = vb->length + item_count; + + if (old_capacity < wanted_capacity) + { + if (old_capacity == 0) + { + vb->pointer = os_reserve(0, item_size * UINT32_MAX, (OSReserveProtectionFlags) {}, (OSReserveMapFlags) { .priv = 1, .anon = 1, .noreserve = 1 }); + } + + let_cast(u32, old_page_capacity, align_forward(old_capacity * item_size, minimum_granularity)); + let_cast(u32, new_page_capacity, align_forward(wanted_capacity * item_size, minimum_granularity)); + + let(commit_size, new_page_capacity - old_page_capacity); + void* commit_pointer = vb->pointer + old_page_capacity; + + os_commit(commit_pointer, commit_size); + + let(new_capacity, new_page_capacity / item_size); + vb->capacity = new_capacity; + } +} + +fn u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) +{ + u32 index = vb->length; + assert(vb->capacity >= index + item_count); + vb->length = index + item_count; + return vb->pointer + (index * item_size); +} + +fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) +{ + vb_generic_ensure_capacity(vb, item_size, item_count); + return vb_generic_add_assume_capacity(vb, item_size, item_count); +} + +fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes) +{ + let_cast(u32, len, bytes.length); + vb_generic_ensure_capacity(vb, sizeof(u8), len); + let(pointer, vb_generic_add_assume_capacity(vb, sizeof(u8), len)); + memcpy(pointer, bytes.pointer, len); + return pointer; +} + +fn u32 vb_copy_string(VirtualBuffer(u8)* buffer, String string) +{ + let(offset, buffer->length); + let_cast(u32, length, string.length); + let(pointer, vb_add(buffer, length)); + memcpy(pointer, string.pointer, length); + return offset; +} + +fn u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string) +{ + assert(string.pointer[string.length] == 0); + string.length += 1; + + vb_copy_string(buffer, string); + + return string.length; +} + +STRUCT(Writer) +{ + VirtualBuffer(u8) buffer; +}; + +fn void write_line(Writer* writer, String string) +{ + vb_copy_string(&writer->buffer, string); + *vb_add(&writer->buffer, 1) = '\n'; +} + +fn void write_head(Writer* writer) +{ + write_line(writer, strlit("")); + write_line(writer, strlit("")); + write_line(writer, strlit("Birth Software")); + write_line(writer, strlit("")); +} + +fn void write_body(Writer* writer) +{ + write_line(writer, strlit("")); + write_line(writer, strlit("This is Birth website")); + write_line(writer, strlit("")); +} + +fn void html_start(Writer* writer) +{ + write_line(writer, strlit("")); + write_line(writer, strlit("")); +} + +fn void html_end(Writer* writer) +{ + write_line(writer, strlit("")); +} + +fn void write_document(Writer* writer) +{ + html_start(writer); + write_head(writer); + write_body(writer); + html_end(writer); +} + +int main() +{ + let(mkdir_result, mkdir("public", 0755)); + assert(mkdir_result == 0); + int fd = open("public/index.html", O_TRUNC | O_WRONLY | O_CREAT, 0644); + assert(fd >= 0); + Writer writer = {}; + write_document(&writer); + let(result, write(fd, writer.buffer.pointer, writer.buffer.length)); + assert(result == writer.buffer.length); + close(fd); +}