diff --git a/duffle/Readme.md b/duffle/Readme.md new file mode 100644 index 0000000..3980f27 --- /dev/null +++ b/duffle/Readme.md @@ -0,0 +1,8 @@ +# C Duffle Library + +```txt +ISA: amd64 +Sandbox: Windows 11 (for now) +Compiler: clang +Standard: c11 +``` diff --git a/duffle/dsl.h b/duffle/dsl.h new file mode 100644 index 0000000..06c57dc --- /dev/null +++ b/duffle/dsl.h @@ -0,0 +1,268 @@ +/* +C DSL Duffle +ISA: amd64 +Sandbox: Windows 11 +Compiler: clang +Standard: c23 +*/ +#pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wuninitialized" +#pragma comment(lib, "Advapi32.lib") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "Kernel32.lib") +#pragma comment(lib, "msvcrt.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "ucrt.lib") +#pragma comment(lib, "vcruntime.lib") +#define WinAPI __attribute((__stdcall__)) __attribute__((__force_align_arg_pointer__)) // Win32 Syscall FFI + +#define offset_of(type, member) cast(U8,__builtin_offsetof(type,member)) +#define static_assert _Static_assert +#define typeof __typeof__ +#define typeof_ptr(ptr) typeof((ptr)[0]) +#define typeof_same(a, b) _Generic((a), typeof((b)): 1, default: 0) + +#define m_expand(...) __VA_ARGS__ +#define glue_impl(A, B) A ## B +#define glue(A, B) glue_impl(A, B) +#define tmpl(prefix, type) prefix ## _ ## type + +#define stringify_impl(S) #S +#define stringify(S) stringify_impl(S) + +#define VA_Sel_1( _1, ... ) _1 // <-- Of all th args passed pick _1. +#define VA_Sel_2( _1, _2, ... ) _2 // <-- Of all the args passed pick _2. +#define VA_Sel_3( _1, _2, _3, ... ) _3 // etc.. + +#define global static // Mark global data +#define gknown // Mark global data used in procedure + +#define LP_ static // static data within procedure scope +#define internal static // internal + +#define asm __asm__ + +#define align_(value) __attribute__((aligned (value))) // for easy alignment +#define C_(type,data) ((type)(data)) // for enforced precedence +#define expect_(x, y) __builtin_expect(x, y) // so compiler knows the common path +#define cexpr_ __builtin_constant_p +#define I_ internal inline +#define FI_ inline __attribute__((always_inline)) // inline always +#define NI_ internal __attribute__((noinline)) // inline never +#define RO_ __attribute__((section(".rodata"))) // Read only data allocation +#define T_ typeof // +#define T_same(a,b) _Generic((a), typeof((b)): 1, default: 0) + +#define R_ restrict +#define V_ volatile + +// R_ (restrict) establishes an "Eigen" or "Proprius" mapping. +// Unlike volatile (V_), which assumes the memory can be changed by anything, +// R_ tells the compiler that this pointer holds the *sole*, private (idios) ownership of the memory slice. +// Writes to this memory are exclusively bound to this single symbolic mapping for the duration of the scope, guaranteeing zero aliasing. + +#pragma region Fictional //, used for intiution + +#define EUB_ restrict // Execute Unit Bound: Data is siloed in the ALU Register File. The Load/Store Unit is bypassed. (Route to Execution Unit. Keep in registers) +#define ISO_ restrict // Isolated Provenance: Alternative to Exu_. Guarantees electrical memory isolation, + // unlocking the compiler’s ability to safely pack data across multiple parallel SIMD lanes (vectorization). +#define LSU_ volatile // Load/Store Unit Bound: The compiler is forbidden from caching in registers. Forces physical L1 Cache matrix sampling. +#define LIVE_ volatile // Live External Data: Alternative to Lsu_ emphasizing the memory is tapped by an external electrical actor. + +#define latch_store /* ~: atomic_store*/ // Blasts voltages from the Store Buffer into the L1 SRAM, physically flipping the cross-coupled inverters to lock the state. +#define pulse_rfo /* ~: atomic_xchg*/ // Broadcasts an electrical RFO (Request For Ownership) pulse across the CPU mesh network to invalidate other L1 caches. +#define tact_acquire /* ~: memory_order_acquire*/ // Clamp. Sends a voltage signal to the instruction decoder to halt the Out-of-Order engine until the load resolves. +#define tact_release /* ~: memory_order_release*/ // Drain. Forces the Store Buffer flip-flops to completely empty into the L1 cache before proceeding. + +// ----------------------------------------------------------------------------- +// Out-of-Order (OoO) Pipeline Modifiers +// ----------------------------------------------------------------------------- +#define ooo_drift_ __ATOMIC_RELAXED // OoO engine allowed to drift +#define ooo_anchor_ __ATOMIC_ACQUIRE // Anchor the Load Queue (halt spec lookahead) +#define ooo_drain_ __ATOMIC_RELEASE // Drain the Store Buffer (force writeback) +#define ooo_weld_ __ATOMIC_SEQ_CST // Weld pipeline (total order bus lock) + +// Latch operations with physical queue modifiers +#define latch_load_anchor(ptr) //__atomic_load_n(ptr, ooo_anchor_) +#define latch_store_drain(ptr, val) //__atomic_store_n(ptr, val, ooo_drain_) +#define pulse_xchg_weld(ptr, val) //__atomic_exchange_n(ptr, val, ooo_weld_) + +#pragma endregion Fictional + +#define r_(ptr) C_(T_(ptr[0])*R_, ptr) // Constrain pointer to restrict +#define v_(ptr) C_(T_(ptr[0])V_*, ptr) // +#define tr_(type, ptr) C_(type *R_, ptr) +#define tv_(type, ptr) C_(type V_*, ptr) + +#define TypeR_(type) type *R_ type ## _R // type *restrict type_R +#define TypeV_(type) type V_* type ## _V // type volatile* type_V +#define PtrSet_(type) TypeR_(type); typedef TypeV_(type) +#define TSet_(type) type; typedef PtrSet_(type) + +#define Array_len(a) (U4)(sizeof(a) / sizeof(typeof((a)[0]))) +#define Array_decl(type, ...) (type[]){__VA_ARGS__} +#define Array_sym(type,len) A ## len ## _ ## type +#define Array_expand(type,len) type Array_sym(type, len)[len]; typedef PtrSet_(Array_sym(type, len)) +#define Array_(type,len) Array_expand(type,len) +#define Bit_(id,b) id = (1 << b), tmpl(id,pos) = b +#define Enum_(underlying_type, symbol) underlying_type TSet_(symbol); enum symbol +#define Proc_(symbol) symbol +#define Relative_(symbol) // Does nothing but annotate that a symbol is associated with another. +#define Struct_(symbol) struct symbol TSet_(symbol); struct symbol +#define Union_(symbol) union symbol TSet_(symbol); union symbol + +#define Opt_(proc) Struct_(tmpl(Opt,proc)) +#define opt_(symbol, ...) (tmpl(Opt,symbol)){__VA_ARGS__} +#define Ret_(proc) Struct_(tmpl(Ret,proc)) +#define ret_(proc) tmpl(Ret,proc) proc + +// Using Byte-Width convention for the fundamental types. +typedef __UINT8_TYPE__ TSet_(U1); +typedef __UINT16_TYPE__ TSet_(U2); +typedef __UINT32_TYPE__ TSet_(U4); +typedef __UINT64_TYPE__ TSet_(U8); +typedef __INT8_TYPE__ TSet_(S1); +typedef __INT16_TYPE__ TSet_(S2); +typedef __INT32_TYPE__ TSet_(S4); +typedef __INT64_TYPE__ TSet_(S8); +typedef unsigned char TSet_(B1); +typedef __UINT16_TYPE__ TSet_(B2); +typedef __UINT32_TYPE__ TSet_(B4); +typedef __UINT64_TYPE__ TSet_(B8); +typedef float F4_2 __attribute__((vector_size(16))); + +#define u1_(value) C_(U1, value) +#define u2_(value) C_(U2, value) +#define u4_(value) C_(U4, value) +#define u8_(value) C_(U8, value) +#define s1_(value) C_(S1, value) +#define s2_(value) C_(S2, value) +#define s4_(value) C_(S4, value) +#define s8_(value) C_(S8, value) + +#define u1_r(value) C_(U1 *R_, value) +#define u2_r(value) C_(U2 *R_, value) +#define u4_r(value) C_(U4 *R_, value) +#define u8_r(value) C_(U8 *R_, value) +#define u1_v(value) C_(U1 V_*, value) +#define u2_v(value) C_(U2 V_*, value) +#define u4_v(value) C_(U4 V_*, value) +#define u8_v(value) C_(U8 V_*, value) +enum { false = 0, true = 1, true_overflow, }; + +#define u4_lo(value) ((value) & 0xFFFFU) +#define u4_hi(value) ((value) >> 12) + +typedef void Proc_(VoidFn) (void); + +#define kilo(n) (n << 10) +#define mega(n) (n << 20) +#define giga(n) (n << 30) +#define tera(n) (n << 40) + +#define null C_(U8, 0) +#define nullptr C_(void*, 0) +#define O_(type, field) C_(U8, & C_(type*,0)->field) +#define OA_(type, member, idx) C_(U8, & C_(type*,0)->member[idx]) +#define OT_(field) O_(typeof_ptr(& field), filed)) +#define S_(data) C_(U8, sizeof(data)) + +#define sop_1(op,a,b) C_(U1, s1_(a) op s1_(b)) +#define sop_2(op,a,b) C_(U2, s2_(a) op s2_(b)) +#define sop_4(op,a,b) C_(U4, s4_(a) op s4_(b)) +#define sop_8(op,a,b) C_(U8, s8_(a) op s8_(b)) + +#undef def_signed_op +#define def_signed_op(id,op,width) FI_ U ## width id ## _s ## width(U ## width a, U ## width b) {return sop_ ## width(op, a, b); } +#define def_signed_ops(id,op) def_signed_op(id, op, 1) def_signed_op(id, op, 2) def_signed_op(id, op, 4) def_signed_op(id, op, 8) +def_signed_ops(add, +) +def_signed_ops(sub, -) +def_signed_ops(mut, *) +def_signed_ops(div, /) +def_signed_ops(gt, >) +def_signed_ops(lt, <) +def_signed_ops(ge, >=) +def_signed_ops(le, <=) +#undef def_signed_ops +#undef def_signed_op + +#define def_generic_sop(op, a, ...) _Generic((a), U1: op ## _s1, U2: op ## _s2, U4: op ## _s4, U8: op ## _s8) (a, __VA_ARGS__) +#define add_s(a,b) def_generic_sop(add,a,b) +#define sub_s(a,b) def_generic_sop(sub,a,b) +#define mut_s(a,b) def_generic_sop(mut,a,b) +#define gt_s(a,b) def_generic_sop(gt, a,b) +#define lt_s(a,b) def_generic_sop(lt, a,b) +#define ge_s(a,b) def_generic_sop(ge, a,b) +#define le_s(a,b) def_generic_sop(le, a,b) +#undef def_generic_sop + +#define alignas _Alignas +#define alignof _Alignof +#define byte_pad(amount, ...) B1 glue(_PAD_, __VA_ARGS__) [amount] +#define pcast(type, data) (C_(type*, & (data)) [0]) + +#define dbg_args(...) __VA_ARGS__ + +#pragma region Control Flow & Iteration +#define each_iter(type, iter, end) (type iter = 0; iter < end; ++ iter) +#define index_iter(type, iter, begin, op, end) (type iter = begin; iter op end; (begin < end ? ++ iter : -- iter)) +#define range_iter(iter,op,range) (T_((range).p0) iter = (range).p0; iter op (range).p1; ((range).p0 < (range).p1 ? ++ iter : -- iter)) + +#define defer(expr) for(U4 once= 1; once!=1;++ once,(expr)) // Basic do something after body +#define scope(begin,end) for(U4 once=(1,(begin)); once!=1;++ once,(end )) // Do things before or after a scope +#define defer_rewind(cursor) for(T_(cursor) sp=cursor,once=0; once!=1;++ once,cursor=sp) // Used with arenas/stacks +#define defer_info(type,expr, ...) for(type info= {__VA_ARGS__}; info.once!=1;++info.once,(expr)) // Defer with tracked state + +#define do_while(cond) for (U4 once=0; once!=1 || (cond); ++once) + +#define span_iter(type, iter, m_begin, op, m_end) ( \ + tmpl(Iter_Span,type) iter = { \ + .r = {(m_begin), (m_end)}, \ + .cursor = (m_begin) }; \ + iter.cursor op iter.r.end; \ + ++ iter.cursor \ +) +#define Span_(type) \ + Struct_(tmpl( Span,type)) { type begin; type end; }; \ + typedef Struct_(tmpl(Iter_Span,type)) { tmpl(Span,type) r; type cursor; } +#pragma endregion Control Flow & Iteration + +typedef Span_(S4); +typedef Span_(U4); +typedef Span_(U8); + + +#pragma region Math +#define u8_max 0xffffffffffffffffull + +#define min(A,B) (((A) < (B)) ? (A) : (B)) +#define max(A,B) (((A) > (B)) ? (A) : (B)) +#define clamp_bot(X,B) max(X, B) // Clamp "X" by "B" + +#define clamp_decrement(X) (((X) > 0) ? ((X) - 1) : 0) + +typedef Struct_(R1_U1){ U1 p0; U1 p1; }; +typedef Struct_(R1_U2){ U2 p0; U2 p1; }; +typedef Struct_(R1_U4){ U4 p0; U2 p4; }; +typedef Struct_(R1_U8){ U8 p0; U8 p4; }; + +typedef Struct_(V2_U1){ U1 x; U1 y;}; + +FI_ B8 add_of (U8 a, U8 b, U8*R_ res) { return __builtin_uaddll_overflow(a, b, res); } +FI_ B8 sub_of (U8 a, U8 b, U8*R_ res) { return __builtin_usubll_overflow(a, b, res); } +FI_ B8 mul_of (U8 a, U8 b, U8*R_ res) { return __builtin_umulll_overflow(a, b, res); } +FI_ B8 add_s_of(S8 a, S8 b, S8*R_ res) { return __builtin_saddll_overflow(a, b, res); } +FI_ B8 sub_s_of(S8 a, S8 b, S8*R_ res) { return __builtin_ssubll_overflow(a, b, res); } +FI_ B8 mul_s_of(S8 a, S8 b, S8*R_ res) { return __builtin_smulll_overflow(a, b, res); } +#pragma endregion Math + +#pragma region Debug +#define debug_trap() __builtin_debugtrap() +#if BUILD_DEBUG +FI_ void assert(U8 cond) { if(cond){return;} else{debug_trap(); ms_exit_process(1);} } +#else +#define assert(cond) +#endif +#pragma endregion Debug diff --git a/duffle/encoding.h b/duffle/encoding.h new file mode 100644 index 0000000..bdbaa94 --- /dev/null +++ b/duffle/encoding.h @@ -0,0 +1,11 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "dsl.h" +#endif + +#pragma region Encoding +FI_ void u64_to_hex(U8 val, char* buf, S4 chars) { + static const char hex_chars[] = "0123456789ABCDEF"; + for(S1 i = chars - 1; i >= 0; --i) { buf[i] = hex_chars[val & 0xF]; val >>= 4; } +} +#pragma endregion Encoding diff --git a/duffle/hashing.h b/duffle/hashing.h new file mode 100644 index 0000000..e731be2 --- /dev/null +++ b/duffle/hashing.h @@ -0,0 +1,17 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "dsl.h" +# include "memory.h" +#endif + +#pragma region Hashing +FI_ void hash64_fnv1a(U8_R hash, Slice data, U8 seed) { + LP_ U8 const default_seed = 0xcbf29ce484222325; + if (seed == 0) seed = default_seed; + hash[0] = seed; for (U8 elem = data.ptr; elem != slice_end(data); elem += 1) { + hash[0] ^= u1_r(elem)[0]; + hash[0] *= 0x100000001b3; + } +} +FI_ U8 hash64_fnv1a_ret(Slice data, U8 seed) { U8 h = 0; hash64_fnv1a(& h, data, seed); return h; } +#pragma endregion Hashing diff --git a/duffle/memory.h b/duffle/memory.h new file mode 100644 index 0000000..5ddba72 --- /dev/null +++ b/duffle/memory.h @@ -0,0 +1,130 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "dsl.h" +#endif + +#define MEM_ALIGNMENT_DEFAULT 4 + +#define assert_bounds(point, start, end) for(;0;){ \ + assert((start) <= (point)); \ + assert((point) <= (end)); \ +} while(0) + +I_ U8 align_pow2(U8 x, U8 b) { + assert(b != 0); + assert((b & (b - 1)) == 0); // Check power of 2 + return ((x + b - 1) & (~(b - 1))); +} + +#define align_struct(type_width) ((U8)(((type_width) + 3) & ~3)) + +FI_ void mem_bump_u4(U4 start, U4 cap, U4_R used, U4 amount) { + assert(amount <= (cap - used[0])); + used[0] += amount; +} +FI_ void mem_bump_u8(U8 start, U8 cap, U8_R used, U8 amount) { + assert(amount <= (cap - used[0])); + used[0] += amount; +} +#define mem_bump(start, cap, used, amount) _Generic(start, U4: mem_bump_u4, U8: mem_bump_u8)(start, cap, used, amount) + +FI_ U8 mem_copy (U8 dest, U8 src, U8 len) { return (U8)(__builtin_memcpy ((void*)dest, (void const*)src, len)); } +FI_ U8 mem_copy_overlapping(U8 dest, U8 src, U8 len) { return (U8)(__builtin_memmove((void*)dest, (void const*)src, len)); } +FI_ U8 mem_fill (U8 dest, U8 value, U8 len) { return (U8)(__builtin_memset ((void*)dest, (int) value, len)); } +FI_ B4 mem_zero (U8 dest, U8 len) { if(dest == 0){return false;} mem_fill(dest, 0, len); return true; } + +#pragma region DAG + +#define check_nil(nil, p) ((p) == 0 || (p) == nil) +#define set_nil(nil, p) ((p) = nil) + +#define sll_stack_push_n(f, n, next) do { (n)->next = (f); (f) = (n); } while(0) + +#define sll_queue_push_nz(nil, f, l, n, next) \ +( \ + check_nil(nil, f) ? ( \ + (f) = (l) = (n), \ + set_nil(nil, (n)->next) \ + ) \ + : ( \ + (l)->next=(n), \ + (l) = (n), \ + set_nil(nil,(n)->next) \ + ) \ +) +#define sll_queue_push_n(f, l, n, next) sll_queue_push_nz(0, f, l, n, next) + +#pragma endregion DAG + +#pragma region Slice + +typedef unsigned char TSet_(UTF8); +typedef Struct_(Str8) { UTF8* ptr; U8 len; }; +typedef Str8 Slice_UTF8; +typedef Struct_(Slice_Str8) { Str8* ptr; U8 len; }; +#define slit8(string_literal) (Str8){ (UTF8*) string_literal, S_(string_literal) - 1 } + +typedef Struct_(Slice) { U8 ptr; U8 len; }; // Untyped Slice +FI_ Slice slice_ut_(U8 ptr, U8 len) { return (Slice){ptr, len}; } + +#define Slice_(type) Struct_(tmpl(Slice,type)) { type* ptr; U8 len; } +typedef Slice_(B1); +#define slice_assert(s) do { assert((s).ptr != 0); assert((s).len > 0); } while(0) +#define slice_end(slice) ((slice).ptr + (slice).len) +#define S_slice(s) ((s).len * S_((s).ptr[0])) + +#define slice_ut(ptr,len) slice_ut_(u4_(ptr), u4_(len)) +#define slice_ut_arr(a) slice_ut_(u4_(a), S_(a)) +#define slice_to_ut(s) slice_ut_(u4_((s).ptr), S_slice(s)) + +#define slice_iter(container, iter) (T_((container).ptr) iter = (container).ptr; iter != slice_end(container); ++ iter) +#define slice_arg_from_array(type, ...) & (tmpl(Slice,type)) { .ptr = array_decl(type,__VA_ARGS__), .len = array_len( array_decl(type,__VA_ARGS__)) } +#define slice_from_array(type, array) (tmpl(Slice,type)) { .ptr = array, .len = S_(array) } + +FI_ void slice_zero_(Slice s) { slice_assert(s); mem_zero(s.ptr, s.len); } +#define slice_zero(s) slice_zero_(slice_to_ut(s)) + +FI_ void slice_copy_(Slice dest, Slice src) { + assert(dest.len >= src.len); + slice_assert(dest); + slice_assert(src); + mem_copy(dest.ptr, src.ptr, src.len); +} +#define slice_copy(dest, src) do { \ + static_assert(T_same(dest, src)); \ + slice_copy_(slice_to_ut(dest), slice_to_ut(src)); \ +} while(0) + +typedef Slice_(U4); + +#pragma endregion Slice + +#pragma region FArena + +typedef Opt_(farena) { U8 alignment, type_width; }; +typedef Struct_(FArena) { U8 start, capacity, used; }; +FI_ void farena_init(FArena_R arena, Slice mem) { assert(arena != nullptr); + arena->start = mem.ptr; + arena->capacity = mem.len; + arena->used = 0; +} +FI_ FArena farena_make(Slice mem) { FArena a; farena_init(& a, mem); return a; } +I_ Slice farena_push(FArena_R arena, U8 amount, Opt_farena o) { + if (amount == 0) { return (Slice){}; } + U8 desired = amount * (o.type_width == 0 ? 1 : o.type_width); + U8 to_commit = align_pow2(desired, o.alignment ? o.alignment : MEM_ALIGNMENT_DEFAULT); + U8 ptr = arena->start + arena->used; + mem_bump_u8(arena->start, arena->capacity, & arena->used, to_commit); + return (Slice){ ptr, to_commit }; +} +FI_ void farena_reset (FArena_R arena) { arena->used = 0; } +FI_ void farena_rewind(FArena_R arena, U4 save_point) { + U8 end = arena->start + arena->used; assert_bounds(save_point, arena->start, end); + arena->used -= save_point - arena->start; +} +FI_ U8 farena_save(FArena arena) { return arena.used; } +#define farena_push_(arena, amount, ...) farena_push((arena), (amount), opt_(farena, __VA_ARGS__)) +#define farena_push_type(arena, type, ...) C_(type*, farena_push((arena), 1, opt_(farena, .type_width=S_(type), __VA_ARGS__)).ptr) +#define farena_push_array(arena, type, amount, ...) (tmpl(Slice,type)){ C_(type*, farena_push((arena), (amount), opt_(farena, .type_width=S_(type), __VA_ARGS__)).ptr), (amount) } + +#pragma endregion FArena diff --git a/duffle/tables.h b/duffle/tables.h new file mode 100644 index 0000000..4fa1bf4 --- /dev/null +++ b/duffle/tables.h @@ -0,0 +1,39 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "dsl.h" +# include "memory.h" +# include "hashing.h" +#endif + +#pragma region Key Table Linear (KTL) + +enum { KT_SLot_value = S_(U8), }; +#define KTL_Slot_(type) Struct_(tmpl(KTL_Slot,type)) { \ + U8 key; \ + type value; \ +} +#define KTL_(type) Slice_(tmpl(KTL_Slot,type)); \ + typedef tmpl(Slice_KTL_Slot,type) tmpl(KTL,type) +typedef Slice KTL_Byte; +typedef Struct_(KTL_Meta) { + U8 slot_size; + U8 type_width; +}; + +typedef Array_(Str8, 2); +typedef Slice_(A2_Str8); +typedef KTL_Slot_(Str8); +typedef KTL_(Str8); +FI_ void ktl_populate_slice_a2_str8(KTL_Str8* kt, Slice_A2_Str8 values) { + assert(kt != null); slice_assert(* kt); + if (values.len == 0) return; + assert(kt->len == values.len); + for index_iter(U4, id, 0, <, values.len) { + hash64_fnv1a(& kt->ptr[id].key, slice_to_ut(values.ptr[id][0]), 0); + mem_copy(u8_(& kt->ptr[id].value), u8_(& values.ptr[id][1]), S_(Str8)); + } +} +#define ktl_str8_key(str) hash64_fnv1a_ret(slice_to_ut(str8(str)), 0) +#define ktl_str8_from_arr(arr) (KTL_Str8){arr, array_len(arr)} + +#pragma endregion KTL diff --git a/duffle/text.h b/duffle/text.h new file mode 100644 index 0000000..430d669 --- /dev/null +++ b/duffle/text.h @@ -0,0 +1,204 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "dsl.h" +# include "memory.h" +# include "hashing.h" +# include "tables.h" +#endif + +// NOTE(rjf): Includes reverses for uppercase and lowercase hex. +RO_ global U8 integer_symbol_reverse[128] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +FI_ B4 char_is_upper(UTF8 c) { return('A' <= c && c <= 'Z'); } +FI_ UTF8 char_to_lower(UTF8 c) { if (char_is_upper(c)) { c += ('a' - 'A'); } return(c); } +FI_ B4 char_is_digit(UTF8 c, U4 base) { + B4 result = 0; if (0 < base && base <= 16) { + if (integer_symbol_reverse[c] < base) result = 1; + } + return result; +} +FI_ UTF8 integer_symbols(UTF8 value) { + LP_ UTF8 lookup_table[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', }; + return lookup_table[C_(UTF8, value)]; +} +FI_ U8 u8_from_str8(Str8 str, U4 radix) { + U8 x = 0; if(1 < radix && radix <= 16) { + for each_iter(U8, cursor, str.len) { + x *= radix; + x += integer_symbol_reverse[str.ptr[cursor] & 0x7F]; + } + } + return x; +} + +typedef Struct_(Info_str8_from_u4) { + Str8 prefix; + U4 digit_group_size; + U4 needed_leading_zeros; + U4 size_required; +}; +I_ Info_str8_from_u4 str8_from_u4_info(U4 num, U4 radix, U4 min_digits, U4 digit_group_separator) +{ + Info_str8_from_u4 info = {0}; + LP_ Str8 tbl_prefix[] = { slit8("0x"), slit8("0o"), slit8("0b") }; + switch (radix) { + case 16: { info.prefix = tbl_prefix[0]; } break; + case 8: { info.prefix = tbl_prefix[1]; } break; + case 2: { info.prefix = tbl_prefix[2]; } break; + } + info.digit_group_size = 3; + switch (radix) { + default: break; + case 2: + case 8: + case 16: { + info.digit_group_size = 4; + } + break; + } + info.needed_leading_zeros = 0; + { + U4 needed_digits = 1; + { + U4 u32_reduce = num; + for(;;) + { + u32_reduce /= radix; + if (u32_reduce == 0) { + break; + } + needed_digits += 1; + } + } + info.needed_leading_zeros = (min_digits > needed_digits) ? min_digits - needed_digits : 0; + U4 needed_separators = 0; + if (digit_group_separator != 0) + { + needed_separators = (needed_digits + info.needed_leading_zeros) / info.digit_group_size; + if (needed_separators > 0 && (needed_digits + info.needed_leading_zeros) % info.digit_group_size == 0) { + needed_separators -= 1; + } + } + info.size_required = info.prefix.len + info.needed_leading_zeros + needed_separators + needed_digits; + } + return info; +} +I_ Str8 str8_from_u4_buf(Slice buf, U4 num, U4 radix, U4 min_digits, U4 digit_group_separator, Info_str8_from_u4 info) +{ + assert(buf.len >= info.size_required); + Str8 result = { C_(UTF8*, buf.ptr), info.size_required }; + /*Fill Content*/ { + U4 num_reduce = num; + U4 digits_until_separator = info.digit_group_size; + for (U8 idx = 0; idx < result.len; idx += 1) + { + U8 separator_pos = result.len - idx - 1; + if (digits_until_separator == 0 && digit_group_separator != 0) { + result.ptr[separator_pos] = u1_(digit_group_separator); + digits_until_separator = info.digit_group_size + 1; + } + else { + result.ptr[separator_pos] = (U1) char_to_lower(integer_symbols(u1_(num_reduce % radix))); + num_reduce /= radix; + } + digits_until_separator -= 1; + if (num_reduce == 0) break; + } + for (U8 leading_0_idx = 0; leading_0_idx < info.needed_leading_zeros; leading_0_idx += 1) { + result.ptr[info.prefix.len + leading_0_idx] = '0'; + } + } + /*Fill Prefix*/ if (info.prefix.len > 0) { slice_copy(result, info.prefix); } + return result; +} +I_ Str8 str8_fmt_ktl_buf(Slice buffer, KTL_Str8 table, Str8 fmt_template) +{ + slice_assert(buffer); + slice_assert(table); + slice_assert(fmt_template); + UTF8_R cursor_buffer = C_(UTF8_R, buffer.ptr); + U8 buffer_remaining = buffer.len; + UTF8_R cursor_fmt = fmt_template.ptr; + U8 left_fmt = fmt_template.len; + while (left_fmt && buffer_remaining) + { + // Forward until we hit the delimiter '<' or the template's contents are exhausted. + U8 copy_offset = 0; + if (cursor_fmt[0] == '<') + { + UTF8_R potential_token_cursor = cursor_fmt + 1; // Skip '<' + U8 potential_token_len = 0; + B4 fmt_overflow = false; + while(true) { + UTF8_R cursor = potential_token_cursor + potential_token_len; + fmt_overflow = cursor >= slice_end(fmt_template); + B4 found_terminator = potential_token_cursor[potential_token_len] == '>'; + if (fmt_overflow || found_terminator) { break; } + ++ potential_token_len; + } + if (fmt_overflow) { + // Failed to find a subst and we're at end of fmt, just copy segment. + copy_offset = 1 + potential_token_len; // '<' + token + goto write_to_buffer; + } + // Hashing the potential token and cross checking it with our token table + U8 key = hash64_fnv1a_ret(slice_ut(u8_(potential_token_cursor), potential_token_len), 0); + Str8_R value = nullptr; for slice_iter(table, token) { + // We do a linear iteration instead of a hash table lookup because the user should never subst with more than 100 unqiue tokens.. + if (token->key == key) { value = & token->value; break; } + } + if (value) + { + // We're going to appending the string, make sure we have enough space in our buffer. + // NOTE(Ed): this version doesn't support growing the buffer (No Allocator Interface) + assert((buffer_remaining - potential_token_len) > 0); + copy_offset = min(buffer_remaining, value->len); // Prevent Buffer overflow. + mem_copy(u8_(cursor_buffer), u8_(value->ptr), buffer_remaining); + // Sync cursor format to after the processed token + cursor_buffer += copy_offset; + buffer_remaining -= copy_offset; + cursor_fmt = potential_token_cursor + 1 + potential_token_len; // '<' + token + left_fmt -= potential_token_len + 2; // The 2 here are the '<' & '>' delimiters being omitted. + continue; + } + // If not a subsitution, we copy the segment and continue. + copy_offset = 1 + potential_token_len; // '<' + token + goto write_to_buffer; + } + else do { + ++ copy_offset; + } + while ( (cursor_fmt[copy_offset] != '<' && (cursor_fmt + copy_offset) < slice_end(fmt_template)) ); + write_to_buffer: + assert((buffer_remaining - copy_offset) > 0); + copy_offset = min(buffer_remaining, copy_offset); // Prevent buffer overflow. + mem_copy(u8_(cursor_buffer), u8_(cursor_fmt), copy_offset); + buffer_remaining -= copy_offset; + left_fmt -= copy_offset; + cursor_buffer += copy_offset; + cursor_fmt += copy_offset; + } + return (Str8){C_(UTF8*, buffer.ptr), buffer.len - buffer_remaining}; +} + +typedef Struct_(Str8Gen) { UTF8* ptr; U8 cap, len; }; +FI_ Slice str8gen_buf(Str8Gen_R gen) { return (Slice){u8_(gen->ptr) + gen->len, gen->cap - gen->len}; } + +FI_ void str8gen_append_str8(Str8Gen_R gen, Str8 str) { assert(gen != nullptr); + U8 ptr = mem_bump_u8(u8_(gen->ptr), gen->cap, & gen->len, str.len).ptr; + mem_copy(ptr, u8_(str.ptr), str.len); +} +FI_ void str8gen_append_fmt(Str8Gen_R gen, Str8 fmt, KTL_Str8 tbl) { + Str8 result = str8_fmt_ktl_buf(str8gen_buf(gen), tbl, fmt); + gen->len += result.len; +} +#define str8gen_append_str8_(gen, s) str8gen_append_str8(gen, str8(s)) diff --git a/duffle/win32.h b/duffle/win32.h new file mode 100644 index 0000000..14f88f7 --- /dev/null +++ b/duffle/win32.h @@ -0,0 +1,153 @@ +#if INTELLISENSE_DIRECTIVES +# pragma once +# include "dsl.h" +# include "memory.h" +#endif + +WinAPI void ms_exit_process(U4 uExitCode) asm("ExitProcess"); // Kernel 32 + +#pragma region IO +enum { + MS_STD_INPUT_CODE = u4_(-10), + MS_STD_OUTPUT_CODE = u4_(-11), +#define MS_STD_INPUT u4_(MS_STD_INPUT_CODE) +#define MS_STD_OUTPUT u4_(MS_STD_OUTPUT_CODE) +}; +typedef Struct_(MS_Handle){U8 id;}; +#pragma endregion IO + +// --- WinAPI Minimal Definitions --- +typedef struct MS_WNDCLASSA { + U4 style; + S8 (*lpfnWndProc)(void*, U4, U8, S8); + S4 cbClsExtra; + S4 cbWndExtra; + void* hInstance; + void* hIcon; + void* hCursor; + void* hbrBackground; + char const* lpszMenuName; + char const* lpszClassName; +} MS_WNDCLASSA; +typedef struct MS_POINT { S4 x, y; } MS_POINT; +typedef struct MS_MSG { void* hwnd; U4 message; U8 wParam; S8 lParam; U4 time; MS_POINT pt; } MS_MSG; +typedef struct MS_RECT { S4 left, top, right, bottom; } MS_RECT; +typedef struct MS_PAINTSTRUCT { void* hdc; S4 fErase; MS_RECT rcPaint; S4 fRestore; S4 fIncUpdate; U1 rgbReserved[32]; } MS_PAINTSTRUCT; + +// --- Kernel32 --- +WinAPI void ms_exit_process(U4 uExitCode) asm("ExitProcess"); +WinAPI MS_Handle ms_get_std_handle(U4 handle_type) asm("GetStdHandle"); +WinAPI void* ms_virtual_alloc(void* lpAddress, U8 dwSize, U4 flAllocationType, U4 flProtect) asm("VirtualAlloc"); +WinAPI B4 ms_read_console( + MS_Handle handle, + UTF8_R buffer, + U4 to_read, + U4_R num_read, + U8 reserved_input_control +) asm("ReadConsoleA"); +WinAPI B4 ms_write_console( + MS_Handle handle, + UTF8 const*R_ buffer, + U4 chars_to_write, + U4_V chars_written, + U8 reserved +) asm("WriteConsoleA"); + +// --- User32 --- +WinAPI U2 ms_register_class_a(MS_WNDCLASSA const* lpWndClass) asm("RegisterClassA"); +WinAPI void* ms_create_window_ex_a( + U4 dwExStyle, + char const* lpClassName, + char const* lpWindowName, + U4 dwStyle, + S4 X, + S4 Y, + S4 nWidth, + S4 nHeight, + void* hWndParent, + void* hMenu, + void* hInstance, + void* lpParam +) asm("CreateWindowExA"); +WinAPI S4 ms_show_window(void* hWnd, S4 nCmdShow) asm("ShowWindow"); +WinAPI S4 ms_get_message_a(MS_MSG* lpMsg, void* hWnd, U4 wMsgFilterMin, U4 wMsgFilterMax) asm("GetMessageA"); +WinAPI S4 ms_translate_message(MS_MSG const* lpMsg) asm("TranslateMessage"); +WinAPI S8 ms_dispatch_message_a(MS_MSG const* lpMsg) asm("DispatchMessageA"); +WinAPI S8 ms_def_window_proc_a(void* hWnd, U4 Msg, U8 wParam, S8 lParam) asm("DefWindowProcA"); +WinAPI void ms_post_quit_message(S4 nExitCode) asm("PostQuitMessage"); +WinAPI S4 ms_invalidate_rect(void* hWnd, MS_RECT const* lpRect, S4 bErase) asm("InvalidateRect"); +WinAPI S2 ms_get_async_key_state(S4 vKey) asm("GetAsyncKeyState"); + +// --- GDI32 --- +WinAPI void* ms_begin_paint(void* hWnd, MS_PAINTSTRUCT* lpPaint) asm("BeginPaint"); +WinAPI S4 ms_end_paint(void* hWnd, MS_PAINTSTRUCT const* lpPaint) asm("EndPaint"); +WinAPI U4 ms_set_text_color(void* hdc, U4 color) asm("SetTextColor"); +WinAPI U4 ms_set_bk_color(void* hdc, U4 color) asm("SetBkColor"); +WinAPI S4 ms_text_out_a(void* hdc, S4 x, S4 y, char const* lpString, S4 c) asm("TextOutA"); +WinAPI void* ms_get_stock_object(S4 i) asm("GetStockObject"); +WinAPI void* ms_create_font_a( + S4 cHeight, + S4 cWidth, + S4 cEscapement, + S4 cOrientation, + S4 cWeight, + U4 bItalic, + U4 bUnderline, + U4 bStrikeOut, + U4 iCharSet, + U4 iOutPrecision, + U4 iClipPrecision, + U4 iQuality, + U4 iPitchAndFamily, + char const* pszFaceName +) asm("CreateFontA"); +WinAPI void* ms_create_compatible_dc(void* hdc) asm("CreateCompatibleDC"); +WinAPI void* ms_create_compatible_bitmap(void* hdc, S4 cx, S4 cy) asm("CreateCompatibleBitmap"); +WinAPI B4 ms_bit_blt(void* hdcDest, S4 x, S4 y, S4 w, S4 h, void* hdcSrc, S4 xSrc, S4 ySrc, U4 rop) asm("BitBlt"); +WinAPI B4 ms_delete_dc(void* hdc) asm("DeleteDC"); +WinAPI B4 ms_get_client_rect(void* hwnd, MS_RECT* lpRect) asm("GetClientRect"); +WinAPI void* ms_select_object(void* hdc, void* h) asm("SelectObject"); +WinAPI S4 ms_rectangle(void* hdc, S4 left, S4 top, S4 right, S4 bottom) asm("Rectangle"); +WinAPI S4 ms_set_bk_mode(void* hdc, S4 mode) asm("SetBkMode"); +WinAPI void* ms_create_solid_brush(U4 color) asm("CreateSolidBrush"); +WinAPI S4 ms_delete_object(void* ho) asm("DeleteObject"); + +enum { + MS_MEM_COMMIT = 0x00001000, + MS_MEM_RESERVE = 0x00002000, + MS_PAGE_READWRITE = 0x04, + MS_SRCCOPY = 0x00CC0020, + MS_WM_DESTROY = 0x0002, + MS_WM_SIZE = 0x0005, + MS_WM_PAINT = 0x000F, + MS_WM_ERASEBKGND = 0x0014, + MS_WM_KEYDOWN = 0x0100, + MS_WM_KEYUP = 0x0101, + MS_WM_MOUSEMOVE = 0x0200, + MS_WM_LBUTTONDOWN = 0x0201, + MS_WM_LBUTTONUP = 0x0202, + MS_WM_RBUTTONDOWN = 0x0204, + MS_WM_RBUTTONUP = 0x0205, + MS_WM_MBUTTONDOWN = 0x0207, + MS_WM_MBUTTONUP = 0x0208, + MS_WM_MOUSEWHEEL = 0x020A, + MS_WS_OVERLAPPEDWINDOW = 0x00CF0000, + MS_WS_VISIBLE = 0x10000000, + MS_VK_LEFT = 0x25, + MS_VK_UP = 0x26, + MS_VK_RIGHT = 0x27, + MS_VK_DOWN = 0x28, + + MS_PAGE_EXECUTE_READWRITE = 0x40, + + MS_WM_CHAR = 0x0102, + MS_VK_RETURN = 0x0D, + MS_VK_BACK = 0x08, + MS_VK_TAB = 0x09, + MS_VK_SPACE = 0x20, + MS_VK_F5 = 0x74, + MS_VK_PRIOR = 0x21, + MS_VK_NEXT = 0x22, + + MS_VK_SHIFT =0x10, +}; diff --git a/scripts/build.lua b/scripts/build.lua deleted file mode 100644 index e69de29..0000000