rstdlib

Generic Base Layer for writing C Programs
git clone anongit@rnpnr.xyz:rstdlib.git
Log | Files | Refs | Feed | README | LICENSE

Commit: c59ba1f5d312c5b1ea6929e35e3364242227d5a3
Parent: b6e0bcf72b189a2bbe520d3dbe7c49d6410369fa
Author: Randy Palamar
Date:   Tue, 27 Jan 2026 21:47:44 -0700

minimal initial implementation

Diffstat:
A.gitignore | 1+
MLICENSE | 5++---
MREADME.md | 17++++++++++++++---
Abuild.sh | 9+++++++++
Arstd_compiler.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arstd_core.h | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arstd_intrinsics.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arstd_platform.h | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arstd_types.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 656 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +rstdlib.h diff --git a/LICENSE b/LICENSE @@ -1,4 +1,4 @@ -© 2025 Randy Palamar <randy@rnpnr.xyz> +© 2025-2026 Randy Palamar <randy@rnpnr.xyz> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. @@ -9,4 +9,4 @@ OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -\ No newline at end of file +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md @@ -1,5 +1,16 @@ -# Base +# Randy's Standard Library -collection of all my base layer functions +Collection of all my base layer functions. -meant to be copy-pasted into other places +Meant to be copy-pasted into other places. As this is meant for +serious programs it requires a number of compiler extensions. It +tries to support MSVC, GCC, and CLANG. Most toy compilers are +missing too many features to be supported. The minimum supported C +standard is C11 which includes support for anonymous structs and +unions. Most other C11 features are not used. + +## build.sh + +Generates an amalgamation in the correct order for inclusion. Just +copy the output `rstdlib.h` into your project and you should be +good to go. diff --git a/build.sh b/build.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +cat \ + rstd_compiler.h \ + rstd_types.h \ + rstd_intrinsics.h \ + rstd_core.h \ + rstd_platform.h \ +>> rstdlib.h diff --git a/rstd_compiler.h b/rstd_compiler.h @@ -0,0 +1,69 @@ +/////////////////////////////////// +// NOTE: Compiler Context Cracking +#ifndef RSTD_COMPILER_H +#define RSTD_COMPILER_H + +#if defined(__linux__) + #define OS_LINUX 1 +#elif defined(__APPLE__) + #define OS_MACOS 1 +#elif defined(_WIN32) + #define OS_WINDOWS 1 +#else + #error Unsupported Operating System +#endif + +#if defined(__clang__) + #define COMPILER_CLANG 1 +#elif defined(_MSC_VER) + #define COMPILER_MSVC 1 +#elif defined(__GNUC__) + #define COMPILER_GCC 1 +#else + #error Unsupported Compiler +#endif + +#if COMPILER_MSVC + #if defined(_M_AMD64) + #define ARCH_X64 1 + #elif defined(_M_ARM64) + #define ARCH_ARM64 1 + #else + #error Unsupported Architecture + #endif +#else + #if defined(__x86_64__) + #define ARCH_X64 1 + #elif defined(__aarch64__) + #define ARCH_ARM64 1 + #else + #error Unsupported Architecture + #endif +#endif + +#if !defined(OS_WINDOWS) + #define OS_WINDOWS 0 +#endif +#if !defined(OS_LINUX) + #define OS_LINUX 0 +#endif +#if !defined(OS_MACOS) + #define OS_MACOS 0 +#endif +#if !defined(COMPILER_CLANG) + #define COMPILER_CLANG 0 +#endif +#if !defined(COMPILER_MSVC) + #define COMPILER_MSVC 0 +#endif +#if !defined(COMPILER_GCC) + #define COMPILER_GCC 0 +#endif +#if !defined(ARCH_X64) + #define ARCH_X64 0 +#endif +#if !defined(ARCH_ARM64) + #define ARCH_ARM64 0 +#endif + +#endif /* RSTD_COMPILER_H */ diff --git a/rstd_core.h b/rstd_core.h @@ -0,0 +1,132 @@ +#ifndef RSTD_CORE_H +#define RSTD_CORE_H + +///////////////////////// +// NOTE: Standard Macros +#define function static +#define global static +#define local_persist static + +#ifndef asm + #define asm __asm__ +#endif + +#ifndef typeof + #define typeof __typeof__ +#endif + +#define alignof _Alignof +#define static_assert _Static_assert + +#define countof(a) (sizeof(a) / sizeof(*a)) + +#define arg_list(type, ...) (type []){__VA_ARGS__}, sizeof((type []){__VA_ARGS__}) / sizeof(type) + +#define Abs(a) ((a) < 0 ? (-a) : (a)) +#define Between(x, a, b) ((x) >= (a) && (x) <= (b)) +#define Clamp(x, a, b) ((x) < (a) ? (a) : (x) > (b) ? (b) : (x)) +#define Min(a, b) ((a) < (b) ? (a) : (b)) +#define Max(a, b) ((a) > (b) ? (a) : (b)) + +#define IsDigit(c) (Between((c), '0', '9')) + +#ifdef _DEBUG + #define assert(c) do { if (!(c)) debugbreak(); } while (0) +#else /* !_DEBUG */ + #define assert(c) +#endif /* !_DEBUG */ + +#define InvalidCodePath assert(0) +#define InvalidDefaultCase default:{ assert(0); }break + +//////////////////////// +// NOTE: Core Functions + +#if COMPILER_MSVC + +function force_inline u32 +clz_u32(u32 a) +{ + u32 result = 32, index; + if (a) { + _BitScanReverse(&index, a); + result = index; + } + return result; +} + +function force_inline u32 +ctz_u32(u32 a) +{ + u32 result = 32, index; + if (a) { + _BitScanForward(&index, a); + result = index; + } + return result; +} + +function force_inline u64 +ctz_u64(u64 a) +{ + u64 result = 64, index; + if (a) { + _BitScanForward64(&index, a); + result = index; + } + return result; +} + +#else /* !COMPILER_MSVC */ + +function force_inline u32 +clz_u32(u32 a) +{ + u32 result = 32; + if (a) result = (u32)__builtin_clz(a); + return result; +} + +function force_inline u32 +ctz_u32(u32 a) +{ + u32 result = 32; + if (a) result = (u32)__builtin_ctz(a); + return result; +} + +function force_inline u64 +ctz_u64(u64 a) +{ + u64 result = 64; + if (a) result = (u64)__builtin_ctzll(a); + return result; +} + +#endif /* !COMPILER_MSVC */ + +function void * +memory_clear(void *restrict destination, u8 byte, s64 size) +{ + u8 *p = destination; + while (size > 0) p[--size] = byte; + return p; +} + +function void +memory_copy(void *restrict destination, void *restrict source, s64 size) +{ + u8 *s = source, *d = destination; + for (; size > 0; size--) *d++ = *s++; +} + +function force_inline s64 +round_up_to(s64 value, s64 multiple) +{ + s64 result = value; + if (value % multiple != 0) + result += multiple - value % multiple; + return result; +} + +#endif /* RSTD_CORE_H */ diff --git a/rstd_intrinsics.h b/rstd_intrinsics.h @@ -0,0 +1,68 @@ +/////////////////// +// NOTE: Intrisics +#ifndef RSTD_INTRINSICS_H +#define RSTD_INTRINSICS_H + +#if COMPILER_CLANG || COMPILER_GCC + #define force_inline inline __attribute__((always_inline)) +#elif COMPILER_MSVC + #define force_inline __forceinline +#endif + +#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS) + #pragma section(".rdata$", read) + #define read_only __declspec(allocate(".rdata$")) +#elif COMPILER_CLANG + #define read_only __attribute__((section(".rodata"))) +#elif COMPILER_GCC + /* TODO(rnp): not supported on GCC, putting it in rodata causes warnings and writing to + * it doesn't cause a fault */ + #define read_only +#endif + +#if COMPILER_MSVC + + #define alignas(n) __declspec(align(n)) + #define no_return __declspec(noreturn) + + #define likely(x) (x) + #define unlikely(x) (x) + + #define assume(x) __assume(x) + #define debugbreak __debugbreak + #define unreachable() __assume(0) + + #if ARCH_ARM64 + #define cpu_yield() __yield() + #endif + +#else /* !COMPILER_MSVC */ + + #define alignas(n) __attribute__((aligned(n))) + #define no_return __attribute__((noreturn)) + + #define likely(x) (__builtin_expect(!!(x), 1)) + #define unlikely(x) (__builtin_expect(!!(x), 0)) + + #if COMPILER_CLANG + #define assume(x) __builtin_assume(x) + #else + #define assume(x) __attribute__((assume(x))) + #endif + #define unreachable() __builtin_unreachable() + + #if ARCH_ARM64 + /* TODO(rnp)? debuggers just loop here forever and need a manual PC increment (step over) */ + #define debugbreak() asm volatile ("brk 0xf000") + #define cpu_yield() asm volatile ("yield") + #else + #define debugbreak() asm volatile ("int3; nop") + #endif + +#endif /* !COMPILER_MSVC */ + +#if ARCH_X64 + #define cpu_yield() _mm_pause() +#endif + +#endif /* RSTD_INTRINSICS_H */ diff --git a/rstd_platform.h b/rstd_platform.h @@ -0,0 +1,307 @@ +////////////////////////////////////// +// NOTE: Wrappable Platform Syscalls +#ifndef RSTD_PLATFORM_H +#define RSTD_PLATFORM_H + +#define MaxPathLength (1024UL) + +#define InvalidHandleValue (-1ULL) +#define InvalidHandle {InvalidHandleValue} + +typedef struct { u64 value[1]; } FileHandle; + +typedef enum { + FileOpenFlag_Write = 1 << 0, + FileOpenFlag_Read = 1 << 1, + FileOpenFlag_Reset = 1 << 2, + FileOpenFlag_Create = 1 << 3, +} FileOpenFlags; + +#if OS_LINUX + +#define ValidLinuxResult(r) ((r) <= -4096UL) +#define InvalidLinuxResult(r) ((r) > -4096UL) + +#define LinuxConstant_O_RDONLY (0x0000) +#define LinuxConstant_O_WRONLY (0x0001) +#define LinuxConstant_O_RDWR (0x0002) +#define LinuxConstant_O_CREAT (0x0040) +#define LinuxConstant_O_TRUNC (0x0200) +#define LinuxConstant_O_APPEND (0x0400) + +#define LinuxConstant_EINTR (-4UL) + +#define LinuxConstant_AT_FDCWD (-100) + +#define LINUX_EINTR_LOOP(result, expr) do { \ + (result) = (expr); \ +} while (InvalidLinuxResult(result) && (result) == LinuxConstant_EINTR) + +#if ARCH_X64 + +typedef enum { + LinuxSyscall_read = 0, + LinuxSyscall_write = 1, + LinuxSyscall_close = 3, + LinuxSyscall_exit = 60, + LinuxSyscall_openat = 257, +} LinuxSyscall; + +function force_inline u64 +syscall1(LinuxSyscall n, s64 a1) +{ + u64 result; + asm volatile ("syscall" + : "=a"(result) + : "a"(n), "D"(a1) + : "rcx", "r11", "memory" + ); + return result; +} + +function force_inline u64 +syscall2(LinuxSyscall n, s64 a1, s64 a2) +{ + s64 result; + asm volatile ("syscall" + : "=a"(result) + : "a"(n), "D"(a1), "S"(a2) + : "rcx", "r11", "memory" + ); + return result; +} + +function force_inline u64 +syscall3(LinuxSyscall n, s64 a1, s64 a2, s64 a3) +{ + u64 result; + asm volatile ("syscall" + : "=a"(result) + : "a"(n), "D"(a1), "S"(a2), "d"(a3) + : "rcx", "r11", "memory" + ); + return result; +} + +function force_inline u64 +syscall4(LinuxSyscall n, s64 a1, s64 a2, s64 a3, s64 a4) +{ + u64 result; + register s64 r10 asm("r10") = a4; + asm volatile ("syscall" + : "=a"(result) + : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10) + : "rcx", "r11", "memory" + ); + return result; +} + + +function force_inline u64 +syscall5(LinuxSyscall n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5) +{ + u64 result; + register s64 r10 asm("r10") = a4; + register s64 r8 asm("r8") = a5; + asm volatile ("syscall" + : "=a"(result) + : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8) + : "rcx", "r11", "memory" + ); + return result; +} + +function force_inline u64 +syscall6(LinuxSyscall n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5, s64 a6) +{ + s64 result; + register s64 r10 asm("r10") = a4; + register s64 r8 asm("r8") = a5; + register s64 r9 asm("r9") = a6; + asm volatile ("syscall" + : "=a"(result) + : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9) + : "rcx", "r11", "memory" + ); + return result; +} + +#elif ARCH_ARM64 + +typedef enum { + LinuxSyscall_openat = 56, + LinuxSyscall_close = 57, + LinuxSyscall_read = 63, + LinuxSyscall_write = 64, + LinuxSyscall_exit = 93, +} LinuxSyscall; + +function force_inline s64 +syscall1(s64 n, s64 a1) +{ + register s64 x8 asm("x8") = n; + register s64 x0 asm("x0") = a1; + asm volatile ("svc 0" + : "=r"(x0) + : "0"(x0), "r"(x8) + : "memory", "cc" + ); + return x0; +} + +function force_inline s64 +syscall2(s64 n, s64 a1, s64 a2) +{ + register s64 x8 asm("x8") = n; + register s64 x0 asm("x0") = a1; + register s64 x1 asm("x1") = a2; + asm volatile ("svc 0" + : "=r"(x0) + : "0"(x0), "r"(x8), "r"(x1) + : "memory", "cc" + ); + return x0; +} + +function force_inline s64 +syscall3(s64 n, s64 a1, s64 a2, s64 a3) +{ + register s64 x8 asm("x8") = n; + register s64 x0 asm("x0") = a1; + register s64 x1 asm("x1") = a2; + register s64 x2 asm("x2") = a3; + asm volatile ("svc 0" + : "=r"(x0) + : "0"(x0), "r"(x8), "r"(x1), "r"(x2) + : "memory", "cc" + ); + return x0; +} + +function force_inline s64 +syscall4(s64 n, s64 a1, s64 a2, s64 a3, s64 a4) +{ + register s64 x8 asm("x8") = n; + register s64 x0 asm("x0") = a1; + register s64 x1 asm("x1") = a2; + register s64 x2 asm("x2") = a3; + register s64 x3 asm("x3") = a4; + asm volatile ("svc 0" + : "=r"(x0) + : "0"(x0), "r"(x8), "r"(x1), "r"(x2), "r"(x3) + : "memory", "cc" + ); + return x0; +} + +function force_inline s64 +syscall5(s64 n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5) +{ + register s64 x8 asm("x8") = n; + register s64 x0 asm("x0") = a1; + register s64 x1 asm("x1") = a2; + register s64 x2 asm("x2") = a3; + register s64 x3 asm("x3") = a4; + register s64 x4 asm("x4") = a5; + asm volatile ("svc 0" + : "=r"(x0) + : "0"(x0), "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), + : "memory", "cc" + ); + return x0; +} + +function force_inline s64 +syscall6(s64 n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5, s64 a6) +{ + register s64 x8 asm("x8") = n; + register s64 x0 asm("x0") = a1; + register s64 x1 asm("x1") = a2; + register s64 x2 asm("x2") = a3; + register s64 x3 asm("x3") = a4; + register s64 x4 asm("x4") = a5; + register s64 x5 asm("x5") = a6; + asm volatile ("svc 0" + : "=r"(x0) + : "0"(x0), "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) + : "memory", "cc" + ); + return x0; +} + +#else +#error Architecture not supported on Linux +#endif + +function FileHandle +sys_stdout_handle(void) +{ + FileHandle result = {1}; + return result; +} + +function void +sys_close(FileHandle h) +{ + u64 sysret; + LINUX_EINTR_LOOP(sysret, syscall1(LinuxSyscall_close, (s64)h.value[0])); +} + +function FileHandle +sys_open_file_at(str8 path, FileHandle directory, FileOpenFlags flags) +{ + char cpath[MaxPathLength]; + + u64 path_length = Min(path.length, countof(cpath) - 1); + memory_copy(cpath, path.str, path_length); + cpath[path_length] = 0; + + s32 linux_flags = 0; + if (flags & FileOpenFlag_Write) { + linux_flags |= (flags & FileOpenFlag_Read) ? LinuxConstant_O_RDWR : LinuxConstant_O_WRONLY; + } else if (flags & FileOpenFlag_Read) { + linux_flags |= LinuxConstant_O_RDONLY; + } + + if (flags & FileOpenFlag_Create) linux_flags |= LinuxConstant_O_CREAT; + if (flags & FileOpenFlag_Reset) linux_flags |= LinuxConstant_O_TRUNC; + else linux_flags |= LinuxConstant_O_APPEND; + + FileHandle result = InvalidHandle; + s64 directory_descriptor = directory.value[0]; + if (directory_descriptor == -1) directory_descriptor = LinuxConstant_AT_FDCWD; + + u64 sysret; + LINUX_EINTR_LOOP(sysret, syscall4(LinuxSyscall_openat, directory_descriptor, (s64)cpath, linux_flags, 0644)); + if ValidLinuxResult(sysret) + result.value[0] = sysret; + + return result; +} + +function s64 +sys_read(FileHandle h, void *output_buffer, s64 read_length) +{ + u64 result; + LINUX_EINTR_LOOP(result, syscall3(LinuxSyscall_read, (s64)h.value[0], (s64)output_buffer, read_length)); + return result; +} + +function s64 +sys_write(FileHandle h, void *data, s64 write_length) +{ + u64 result; + LINUX_EINTR_LOOP(result, syscall3(LinuxSyscall_write, (s64)h.value[0], (s64)data, write_length)); + return (s64)result; +} + +function no_return void +sys_exit(s64 exit_status) +{ + syscall1(LinuxSyscall_exit, exit_status); + unreachable(); +} + +#endif /* OS_LINUX */ + +#endif /* RSTD_PLATFORM_H */ diff --git a/rstd_types.h b/rstd_types.h @@ -0,0 +1,54 @@ +//////////////////////// +// NOTE: Standard Types + +#ifndef RSTD_TYPES_H +#define RSTD_TYPES_H + +#if COMPILER_MSVC + typedef unsigned __int64 u64; + typedef signed __int64 s64; + typedef unsigned __int32 u32; + typedef signed __int32 s32; + typedef unsigned __int16 u16; + typedef signed __int16 s16; + typedef unsigned __int8 u8; + typedef signed __int8 s8; +#else + typedef __UINT64_TYPE__ u64; + typedef __INT64_TYPE__ s64; + typedef __UINT32_TYPE__ u32; + typedef __INT32_TYPE__ s32; + typedef __UINT16_TYPE__ u16; + typedef __INT16_TYPE__ s16; + typedef __UINT8_TYPE__ u8; + typedef __INT8_TYPE__ s8; +#endif + +typedef u8 b8; +typedef u16 b16; +typedef u32 b32; +typedef u64 b64; +typedef float f32; +typedef double f64; +typedef s64 sptr; +typedef u64 uptr; + +#define U64_MAX (0xFFFFFFFFFFFFFFFFull) +#define U32_MAX (0xFFFFFFFFul) +#define U16_MAX (0xFFFFu) +#define U8_MAX (0xFFu) +#define S64_MAX (0x7FFFFFFFFFFFFFFFll) +#define S32_MAX (0x7FFFFFFFl) +#define S16_MAX (0x7FFF) +#define S8_MAX (0x7F) + +#define GB(a) ((u64)(a) << 30ULL) +#define MB(a) ((u64)(a) << 20ULL) +#define KB(a) ((u64)(a) << 10ULL) + +typedef struct {s64 length; u16 *str;} str16; +typedef struct {s64 length; union {u8 *str; s8 *c_str;}; } str8; +#define str8(s) (str8){.length = (s64)sizeof(s) - 1, .str = (u8 *)s} +#define str8_comp(s) {sizeof(s) - 1, (u8 *)s} + +#endif /* RSTD_TYPES_H */