This commit is contained in:
commit
65268cad37
16
.gitea/workflows/ci.yml
Normal file
16
.gitea/workflows/ci.yml
Normal file
@ -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
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/index.html
|
||||
/public/
|
||||
/generate
|
6
build.sh
Executable file
6
build.sh
Executable file
@ -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
|
4
install.sh
Executable file
4
install.sh
Executable file
@ -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
|
378
src/generate.c
Normal file
378
src/generate.c
Normal file
@ -0,0 +1,378 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#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("<head>"));
|
||||
write_line(writer, strlit("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"));
|
||||
write_line(writer, strlit("<title>Birth Software</title>"));
|
||||
write_line(writer, strlit("</head>"));
|
||||
}
|
||||
|
||||
fn void write_body(Writer* writer)
|
||||
{
|
||||
write_line(writer, strlit("<body>"));
|
||||
write_line(writer, strlit("This is Birth website"));
|
||||
write_line(writer, strlit("</body>"));
|
||||
}
|
||||
|
||||
fn void html_start(Writer* writer)
|
||||
{
|
||||
write_line(writer, strlit("<!DOCTYPE html>"));
|
||||
write_line(writer, strlit("<html lang=\"en\">"));
|
||||
}
|
||||
|
||||
fn void html_end(Writer* writer)
|
||||
{
|
||||
write_line(writer, strlit("</html>"));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user