Table of Contents
- dasae-headers
- 🚀 Getting Started
- Introduction
- Key Features
- Modules Reference
builtin— Compiler & Platform Abstractioncore— Language Primitives & Syntax Extensionsprl— Prelude Typessimd— SIMD Vector Operationscmp— Comparison Utilitiesmath— Mathematical FunctionsRand— Random Number Generationsearch— Searching Algorithmssort— Sorting Algorithms- Linked Lists
- Tree Structures (planned)
- Array-Based Containers
- Hash-Based Containers
mem— Memory Utilitiesheap— Heap Allocatorsmeta— Runtime Record/Type Reflectionatom— Atomic OperationsThrd— ThreadingCo/async— Stackless Coroutinesmp— Multi-Processing (planned)ascii— ASCII character utilitiesutf8— UTF-8 encoding/decodingutf16— UTF-16 encoding/decodingwtf8— WTF-8 (UTF-8 superset for Windows)wtf16— WTF-16 encodingunicode— Unicode conversion hubos— OS-Specific APIsposix— POSIX Compatibilityproc— Process Management (planned)time— Time & Durationio— Input/Outputfmt— Formattinglog— Loggingfs— File Systemnet— Networking (planned)http— HTTP (planned)TEST— Testing Frameworkmain— Entry Point
- Meta System
- Platform Support
- Code Samples
- Documentation
- Naming
- Contribution and Contact
- License
Note: Currently, Build tool
dh-conly supports Clang as the compiler. GCC support is planned but not yet available.
- Clang 16.0.0+ (Recommended: 19.1.0+)
- Make (GNU Make or compatible)
git clone https://github.com/coding-pelican/dasae-headers
cd dasae-headerscd dh-c
./gen-makefile.sh
makeThis compiles the dh-c build tool to dh-c/build/dh-c
(or dh-c/build/dh-c.exe on Windows).
You need to configure two environment variables:
| Variable | Description | Example Value |
|---|---|---|
DH_HOME |
Path to the dh directory containing headers |
/path/to/dasae-headers/dh |
PATH |
Add the dh-c binary location |
/path/to/dasae-headers/dh-c/build |
Linux/macOS (bash/zsh):
# Add to ~/.bashrc or ~/.zshrc
export DH_HOME="/path/to/dasae-headers/dh"
export PATH="/path/to/dasae-headers/dh-c/build:$PATH"Then reload your shell configuration:
source ~/.bashrc # or source ~/.zshrcWindows (MSYS2/MinGW):
# Add to ~/.bashrc
export DH_HOME="/c/path/to/dasae-headers/dh"
export PATH="/c/path/to/dasae-headers/dh-c/build:$PATH"Windows (PowerShell):
# Add to your PowerShell profile or set as system environment variables
$env:DH_HOME = "C:\path\to\dasae-headers\dh"
$env:PATH = "C:\path\to\dasae-headers\dh-c\build;$env:PATH"dh-c --version
dh-c --helpdh-c project myproject
cd myproject
dh-c run#include <dh/main.h>
#include <dh/io/stream.h>
fn_((main(S$S_const$u8 args))(E$void) $scope) {
let_ignore = args;
let message = u8_l("Hello");
io_stream_println(u8_l("{:s}, world!"), message);
return_ok({});
} $unscoped_(fn);# Run directly
dh-c run
# Build executable
dh-c build
# Run tests
dh-c test
# Clean build artifacts
dh-c cleandasae-headers aims to improve the safety, expressiveness, and productivity of the C language by introducing modern programming paradigms. While maintaining the core principle of C—simplicity—it strengthens memory and type safety and provides a structured error-handling mechanism.
Currently in the early stages of development, it provides a transpiler-like environment leveraging the C preprocessor. This project aims to compensate for the structural limitations of the standard C library and is in the process of gradually establishing an independent development ecosystem.
dasae-headers adheres to the following design principles to overcome the constraints of traditional C environments and provide a modern development experience:
Design Principles
- Seamless Coexistence with C Ecosystem: Immediately introduce modern syntax and safety features without modifying existing C libraries or legacy codebases.
- Zero-cost Abstractions: Provide high-level features while minimizing runtime overhead through optimizations such as inlining, preprocessing-stage evaluation, and constant-folding.
- Incremental Adoption: Use only the necessary modules (e.g., error handling, allocators) selectively without needing to convert the entire project.
- Freestanding and Bare-metal Support: Support for freestanding environments—such as embedded systems or kernel development— is a top priority on the roadmap, making it effective at the lowest levels of the system.
- Minimal User-defined Macros: Users do not need to write additional complex macros to implement core features in general use cases. Generic instantiation is handled automatically during preprocessing as long as established patterns are followed.
- Debug-friendly Design: Macros are meticulously designed not to interfere with runtime debugging (call stack tracing, step-by-step execution), maintaining development productivity.
- Consistent Conventions: Strict and consistent code conventions and naming schemes ensure readability and maintainability even in large-scale codebases.
Show details
While maintaining the flexibility of C, dasae-headers brings modern language safety features and productivity tools in a form optimized for the system layer. Rather than simply adding features, it focuses on structurally addressing C's chronic design flaws and fragmented conventions.
Unifies fragmented language/architecture/OS/compiler-specific APIs and complex syntax of standard C into a single interface.
| Aspect | Traditional C (Standard C) | dasae-headers |
|---|---|---|
| Variables and Functions | Explicit type declarations and repetitive signatures | let (constant), var (mutable) type inference and fn_ modern syntax |
| Lambda/Closures | GCC nested functions or Clang blocks, platform-dependent | la_ syntax unifying both compiler extensions, Callable type for fn_ and la_ |
| Platform Support | Fragmented branching with #ifdef |
Unified API ({lang/arch/plat/comp}_cfg.h, os.h, posix.h) provided |
| Preprocessor Branching | Separate #ifdef branch definitions even for simple values |
Single definition with preprocessor branching via pp_if_, pp_switch_ |
Avoids unsafe patterns of returning success as bool and receiving result values
via out parameters.
Optional and Error Result enforce validation of absent values or error conditions
at the type system level.
| Aspect | Traditional C (Standard C) | dasae-headers |
|---|---|---|
| Resource Release | goto cleanup or scattered manual cleanup |
Automatic scope-based cleanup with defer_, errdefer_ |
| Result Return | bool/error code return + out pointer |
Optional (O$) / Error Result (E$) return |
| Result Branching | Manual validation with if (err) branches |
Result control with orelse_, unwrap_ / try_, catch_ syntax |
| Data Transfer | Pointer and length (len) passed separately |
Slice (S$) or array as value (A$) transfer |
| Strings/Arrays | 0(NULL) sentinel-based implicit length |
Explicit length field-based Boundary Check |
Provides ultra-lightweight asynchronous environments capable of handling tens of thousands of tasks
simultaneously without OS thread overhead.
Expresses state-machine-based control flow concisely with async_/await_ patterns.
| Aspect | Traditional C (Standard C) | dasae-headers |
|---|---|---|
| Async Model | OS native thread-centric design | OS native threads + State-machine-based Stackless Coroutines |
| Sync Primitives | Reliant on primitive mutex, cond |
RWLock, ResetEvent, WaitGroup, etc. |
| Control Flow | Callback hell or manually implemented state machines | Coroutine control with async_, await_, suspend_, resume_ syntax |
Escapes macro hell and validates type safety at compile time through the meta type system.
Provides a differentiated layer that statically guarantees logical compatibility
between anonymous user types not allowed in the C standard.
See Meta System for how the meta type system and the meta module
(record reflection) work and relate.
| Aspect | Traditional C (Standard C) | dasae-headers |
|---|---|---|
| Implementation | void* casting (type information loss) |
High-level abstraction based on Meta System (u_*) |
| Anonymous Types | Assignment impossible even with identical structure due to anonymity | Compatibility guaranteed when field memory layout and logical structure (Size/Align/Offset) match |
| API Exposure | Data structure API implementation directly embedded in macros | Macro-less API: Adheres to standard function definition format |
Abstracts hardware architecture-specific SIMD instructions and statically detects arithmetic overflow and inappropriate type casting. All safety features are optimized in release mode to ensure zero runtime overhead.
| Aspect | Traditional C (Standard C) | dasae-headers |
|---|---|---|
| Vector Ops | Manual loop-based operations or platform-dependent APIs | Architecture-independent accelerated operations via simd.h |
| Arithmetic Safety | Vulnerable to runtime exceptions like Overflow, DivisionByZero, NaN |
Compile-time Overflow/NaN static detection layer |
| Type Casting | Risk of data loss due to implicit type conversion | Mismatch tracking for Signed/Unsigned and size conversion, int<->float conversion |
Manages the entire process from project creation to build and test with built-in tools without additional dependencies. When errors occur, preserves call stack information beyond simple return values to immediately pinpoint the cause.
| Aspect | Traditional C (Standard C) | dasae-headers |
|---|---|---|
| Data Structures/Algorithms | Manual implementation or fragmented external libraries | Standard containers and algorithms like ArrList, ArrPDeq, HashMap provided |
| Memory Control | Dependent on fixed global allocation | Allocator or memory buffer injection possible for all APIs |
| Testing/Analysis | External framework integration required | Sophisticated tracking based on built-in TEST.h and ErrTrace.h |
Show details
Beyond simple syntax extensions, dasae-headers leverages deep understanding of compiler and static analyzer behavior to provide a user-centric development experience differentiated from other projects.
Static Analyzer Transparency:
Designed so that major analysis tools like clang-tidy can understand the source code
the same way as regular C code.
In particular, it blocks static analyzer malfunctions (e.g. type mismatches from checking
all branches) that occur when using _Generic, allowing developers to focus only
on "real" warnings and errors, not "recognition errors."
Transparent API Exposure (Macro-less API):
Core APIs, including data structures, are not wrapped in complex macro functions. Macros focus on acting as operators that replace special symbols, while actual logic follows standard function definition formats. This eliminates the burden of writing separate macros or understanding complex macro structures to call libraries, defining a predictable standard for "macros as operators" vs "actual function usage" in core logic.
Meta Type-based Generic System:
Implements generic logic through the meta type system (u_*) rather than macros,
which are blind spots for static analysis.
Provides a zero-cost abstraction layer that optimizes like inlined code via LTO,
constant folding, and constant propagation, even without inlining via macros.
See Meta System for the full picture
(meta type system + meta module and their relationship).
Structural Anonymous Type Compatibility Validation:
Unlike other libraries that require type aliasing via typedef to allow generics,
effectively accommodates anonymous user types.
Statically validates safety at compile time when field layout (Size, Align, Offset) matches,
ensuring safe interaction between anonymous types.
Compile-time Evaluation Priority for Operations and Casting Validation:
Statically detects arithmetic Overflow, DivisionByZero, NaN, and inappropriate type casting.
All safety validations prioritize compile-time evaluation and function as assertions at runtime.
In release mode, these validation items become branch optimization targets,
securing safety without binary overhead, while allowing faster and more accurate identification
of problem causes during development compared to traditional C.
This project was developed with inspiration from the syntax structures and standard library designs of Zig and Rust.
- Memory Safety:
Custom allocators, memory tracking, boundary checking,
defer_/errdefer_-based automatic resource management - Enhanced Type System: Compile-time validation, meta type system, algebraic data types (Variant), optional types
- Explicit Error Handling:
ok/errkeywords,try_/catch_patterns, error tracing with call stack information - Modern Syntax:
Type inference (
let/var), function definition (fn_), lambda expressions (la_), first-classCallabletype - Development Tools: Built-in testing framework, support for major C compilers (Clang, GCC) and multi-platform environments
Legend: Items marked with (planned) are under development and not yet publicly available.
Core Language Extensions
Preprocessor utilities, platform/compiler detection, source location tracking, type and container introspection.
- Submodules:
pp,lang_cfg,arch_cfg,plat_cfg,comp_cfg,comp,src_loc,static_assert,auto,lambda,type_info,container_info,mem,atom pp(preprocessor): Implements compile-time metaprogramming as preprocessor macros:- Token comparison:
pp_eq,pp_ne,pp_and,pp_or,pp_xor,pp_not,pp_Tok_eq— token equality and logic - Control flow:
pp_if_/pp_then_/pp_else_— conditional;pp_switch_/pp_case_/pp_default_— dispatch - Optionals:
pp_some,pp_none,pp_isSome,pp_isNone,pp_orelse_— preprocessor optional values - Utilities:
pp_strfy,pp_cat,pp_join,pp_join2/pp_join3/pp_join4,pp_uniqTok,pp_overload,pp_foreach,pp_countArg,pp_defer,pp_expand,nameOf
- Token comparison:
- Platform/compiler:
lang_cfg,arch_cfg,plat_cfg,comp_cfg— detection macros - Source location:
srcLoc$— compile-time source location capture - Type introspection (builtin):
typeInfo$,sizeOf$,alignOf$,TypeOf,TypeOfUnqual,Type_eq$,RefType$,DerefType$ - Container/field introspection:
FieldType$,FieldTypeUnqual$,FieldType_eq$,offsetTo,fieldPtr(incontainer_info)
Primitives, function syntax, assertions, debugging, scoped resource management, and safe arithmetic.
- Submodules:
prim,fn,Callable,claim,debug,range,op,cmp,pipe,chain,blk,scope,src_loc,type_info,struct_layout - Primitive types:
bool,i8..i64,u8..u64,isize,usize,f32,f64 - Syntax:
fn_,la_— function and lambda;Callable— first-class callable - Assertions:
claim_assert,claim_unreachable;debug_assert,debug_only - Control/scope:
R,for_— range iteration;defer_,errdefer_— scoped cleanup - Chaining:
pipe_,chain$,each_,filter_,map$,fold_,reduce_,all_,any_ - Core primitives (arithmetic, bitwise, comparison):
prim_add,prim_sub,prim_mul,prim_div,prim_rem,prim_neg,prim_abs,prim_sgn,prim_not,prim_shl,prim_shr,prim_and,prim_xor,prim_or,prim_eql,prim_neq,prim_ord,prim_min,prim_max,prim_clamp,prim_bitSet,prim_bitReset,prim_maskLo$,prim_maskHi$,prim_memcpy,prim_memset,prim_memmove,prim_memeql,bitCast$ - Safe integer ops (overflow-checked, in
prim):int_add,int_sub,int_mul,int_div,int_rem,int_divTrunc,int_divRound,int_divCeil,iint_divFloor,iint_divEuclid,iint_mod,intCast$ - Integer/float division variants:
int_remRound,int_modCeil;flt_divTrunc,flt_divFloor,flt_rem,flt_mod - Type predicates & limits:
isBool$,isUInt$,isIInt$,isFlt$,int_bits$,uint_limit$,int_limit_min$,int_limit_max$,flt_limit_min$,flt_limit_max$,flt_nan$,flt_inf$ - Type info (core):
typeInfo$— runtimeTypeInfo(size, align) for a type
Core algebraic types: Optional, Error Result, Slice, Array, Variant.
(Safe arithmetic lives in core/prim.)
- Submodules:
types(raw,Val,Ptr,Arr,Sli,Opt,ErrRes,Err,variant,meta),common,ErrTrace,int,flt - Key Types:
O$(T)(Optional) —some(v),none(),if_some((opt)(capture)),orelse_((opt)(default)),unwrap_(opt)E$(T)(Error Result) —ok(v),err(e),try_(expr),catch_((expr)(err, block)),return_ok,return_errS$(T)(Slice) —S_deref$,S_at,S_slice,S_prefix,S_suffix,S_lenA$(N, T)(Array) —A_zero,A_init$,A_from$,A_ref$,A_lenvariant_— Tagged union; create withunion_of$, match withmatch_,pattern_ErrTrace— Error tracing with call stack information
- prl/int, prl/flt:
Per-type safe wrappers (e.g.
u8_add,u32_div,i64_mod) with debug overflow checks; seecore/primfor genericint_add,intCast$, etc. - Zero-cost meta type system (
prl/types/meta): Type-erased generic layer over PRL types so algorithms can work on values without knowing the concrete type at compile time. How it works and how it relates to themetamodule (record/type reflection) are described in Meta System.
SIMD & Math
Architecture-independent SIMD operations (AVX, SSE, NEON) using compiler vector extensions.
- Types:
Vec$(N, T)— e.g.,Vec$(4, f32),Vec$(8, f32),Vec$(4, i32),Vec$(2, f64) - Initialization:
Vec_init$,Vec_splat$,Vec_from$,Vec_fromA$,Vec_toA$,Vec_cat$ - Arithmetic:
Vec_add,Vec_sub,Vec_mul,Vec_div,Vec_neg,Vec_abs,Vec_fma - Comparison:
Vec_eq,Vec_ne,Vec_lt,Vec_le,Vec_gt,Vec_ge - Min/Max:
Vec_min,Vec_max,Vec_clamp - Reduction:
Vec_reduceAdd,Vec_reduceMul,Vec_reduceMin,Vec_reduceMax - Shuffle:
Vec_shuffle$,Vec_select - Math:
Vec_sqrt,Vec_floor,Vec_ceil,Vec_round,Vec_trunc
cmp_Ord— Three-way ordering type:cmp_Ord_lt(−1),cmp_Ord_eq(0),cmp_Ord_gt(1). Represents the mathematical sign of (lhs − rhs): strictly less, equal, or strictly greater. Used when a total (or partial) order exists; all ordering-derived predicates are defined in terms ofcmp_ord$(_T)(lhs, rhs).cmp_Eql— Equality-only comparison (no order). Used when only equivalence is defined:cmp_eql$(_T)(lhs, rhs)andcmp_neq$(_T)(lhs, rhs).- Derived names and defaults:
- ord — Three-way comparison; default direction is ascending (smaller-first).
cmp_OrdFn_defaultAsc/cmp_OrdFn_defaultDesc(incmp.h) select ascending or descending. - eql / neq — Equality and its negation; not derived from order.
Implement
cmp_eql$(_T)(and optionallycmp_neq$(_T); the other is defaulted as its negation). - eq / ne — Equality/inequality derived from
cmp_ord$:eq⇔ (ord == cmp_Ord_eq),ne⇔ (ord != cmp_Ord_eq). So for ordered types,cmp_eq$/cmp_ne$are defaulted fromcmp_ord$; for equality-only types you implementcmp_eql$/cmp_neq$only.
- ord — Three-way comparison; default direction is ascending (smaller-first).
- Defining ordering (ord vs lt):
You can supply ordering for a type in either of two ways.
(1) ord defined: Implement
cmp_ord$(_T)(lhs, rhs)returningcmp_Ord(three-way). Thencmp_eq$,cmp_ne$,cmp_lt$,cmp_gt$,cmp_le$,cmp_ge$are defaulted from it via thecmp_fn_*_default$macros. (2) lt only: Implement onlycmp_lt$(_T)(lhs, rhs)(strict less-than, returns bool). Usecmp_fn_ord_default$(_T)to derive ord from lt (ord = lt(lhs,rhs) ? lt : lt(rhs,lhs) ? gt : eq); then eq/ne/lt/gt/le/ge are defaulted from that ord as in (1). The same pattern applies to context and approximate variants:ordCtx/ltCtx,ordApx/ltApx. - Distinction:
eql/neq are the primitive equality interface
(reflexive, symmetric, and for consistent types transitive).
eq/ne are the order-derived equality:
they coincide with eql/neq when the order is total and consistent with equality,
but eq/ne are defined only when
cmp_ord$exists. Use eql/neq for types that have equality but no order (e.g. slices by content); use eq/ne when you have an order and want (ord == cmp_Ord_eq) as equality. - Predicates on
cmp_Ord:cmp_Ord_isLt,cmp_Ord_isEq,cmp_Ord_isGt,cmp_Ord_isLe,cmp_Ord_isGe,cmp_Ord_inv
- Submodules:
common,vec/vec_types,mat/mat_types,quat/quat_types - Common (prefix
math_):math_abs,math_min,math_max,math_clamp,math_sign,math_wrap,math_floor,math_ceil,math_round,math_trunc,math_sqrt,math_pow,math_rsqrt,math_sin,math_cos,math_tan,math_asin,math_acos,math_atan,math_atan2; constants e.g.math_pi,math_f32_pi,math_limit_min$,math_limit_max$. - Vectors (types
m_V2f32,m_V3f32,m_V4f32, and f64/i32/i64 variants; prefixm_V*):m_V*_of,m_V*_splat,m_V*_add,m_V*_sub,m_V*_mul,m_V*_div,m_V*_dot,m_V*_cross,m_V*_norm,m_V*_len,m_V*_lenSq,m_V*_dist,m_V*_min,m_V*_max,m_V*_clamp,m_V*_rotate - Matrices (types
m_M2f32,m_M3f32,m_M4f32; prefixm_M*):m_M*_identity,m_M*_ofCols,m_M*_ofRows,m_M*_mulM,m_M*_mulV,m_M*_transpose,m_M*_det,m_M*_inv,m_M*_rotate,m_M*_scale,m_M*_scaleUniform - Quaternions (type
m_Q4f32; prefixm_Q4f32_):m_Q4f32_identity,m_Q4f32_of,m_Q4f32_mul,m_Q4f32_mulQ,m_Q4f32_conj,m_Q4f32_invQ,m_Q4f32_norm,m_Q4f32_slerp,m_Q4f32_fromAxisAngle,m_Q4f32_fromM3/fromM4,m_Q4f32_toM3/toM4
Random number generator (struct Rand, prefix Rand_).
- Init/seed:
Rand_init,Rand_initSeed,Rand_withSeed,Rand_setSeed - Next:
Rand_nextUInt,Rand_next$usize,Rand_next$u64,Rand_next$u32,Rand_next$u16,Rand_next$u8,Rand_nextIInt,Rand_next$isize,Rand_next$i64/…,Rand_nextFlt,Rand_next$f64,Rand_next$f32 - Ranges:
Rand_rangeUInt,Rand_rangeIInt,Rand_rangeFlt
Algorithms
| Function | Time | Description |
|---|---|---|
search_linearFirst |
O(N) | Find first matching index (forward scan) |
search_linearLast |
O(N) | Find last matching index (backward scan) |
search_binary |
O(log N) | Binary search on partitioned sequence |
search_lowerBound |
O(log N) | First index where element ≥ target |
search_upperBound |
O(log N) | First index where element > target |
search_partPoint |
O(log N) | Partition point where predicate changes |
search_eqRange |
O(log N) | Range of indices equal to target |
Optimal stable and unstable sorting functions isolated by auxiliary memory constraints.
| Function | Stability | Time | Space | Description |
|---|---|---|---|---|
sort_insert |
Stable | O(N²) | O(1) | Insertion sort for small/partially sorted arrays |
sort_heap |
Unstable | O(N log N) | O(1) | Heapsort for strict O(1) memory constraints |
sort_pdq |
Unstable | O(N log N) | O(log N) | Pattern-Defeating Quicksort, adaptive |
sort_block |
Stable | O(N log N) | O(1) | Block sort (WikiSort) with internal buffers |
sort_blockCache |
Stable | O(N log N) | O(K) | Block sort with provided cache buffer |
sort_blockAlloc |
Stable | O(N log N) | O(K) | Block sort with allocator-provided buffer |
- Context variants:
sort_insertCtx,sort_heapCtx,sort_pdqCtx,sort_blockCtx - Index-based variants:
sort_insertIdx,sort_heapIdx,sort_pdqIdx(for non-contiguous layouts) - Utilities:
sort_inOrdd(check if sorted)
Data Structures
| Module | Description | Key Functions |
|---|---|---|
ListSgl |
Singly linked list | empty, prepend, remove, shift |
ListDbl |
Doubly linked list | empty, prepend, append, remove, shift, pop |
| Module | Description | Key Functions |
|---|---|---|
BTree |
B-tree (planned) | — |
BTreeMap |
B-tree based ordered map (planned) | — |
BTreeSet |
B-tree based ordered set (planned) | — |
SegTree |
Segment tree (planned) | — |
| Module | Description | Key Functions |
|---|---|---|
ArrList |
Dynamic array list with amortized O(1) append | init, fini, append, appendS, pop, insert, removeOrdd, removeSwap, resize, ensureCap |
ArrStk |
Array-based stack (LIFO) | init, fini, push, pop, top, isEmpty |
ArrDeq |
Array-based double-ended queue | init, fini, append, prepend, pop, shift |
ArrQue |
Array-based queue (FIFO) | init, fini, enque, deque, front |
ArrPDeq |
Array-based priority double-ended queue | init, fini, enque, popMin, popMax, peekMin, peekMax |
ArrPQue |
Array-based priority queue (binary heap) | init, fini, enque, deque, peek, update |
| Module | Description | Key Functions |
|---|---|---|
Hash |
Hash utilities (planned) | — |
HashMap |
Hash map with open addressing | init, fini, put, by, ptrBy, for, entry, contains, remove, iter |
HashSet |
Hash set with open addressing | init, fini, put, ensure, contains, remove, iter |
HashMapSeq |
Ordered hash map preserving insertion order (planned) | — |
HashSetSeq |
Ordered hash set preserving insertion order (planned) | — |
Memory Management
Low-level memory operations with type-safe wrappers.
- Submodules:
common,Allocator,Tracker - Bit Operations:
mem_trailingZeros{8,16,32,64,Size},mem_leadingZeros{8,16,32,64,Size} - Byte Swap:
mem_byteSwap{16,32,64,Size} - Endian Conversion:
mem_littleToNative*,mem_bigToNative*,mem_nativeToLittle*,mem_nativeToBig* - Alignment:
mem_isValidAlign,mem_isAligned,mem_alignFwd,mem_alignBwd,mem_alignToLog2 - Memory Operations:
mem_copy,mem_move,mem_set,mem_set0— Copy, move, fillmem_eql,mem_ord,mem_eq,mem_ne,mem_lt,mem_gt,mem_le,mem_ge— Comparisonmem_swap,mem_reverse,mem_rotate— Manipulationmem_startsWith,mem_endsWith— Pattern matching
- Iterators:
mem_TokenIter— Tokenizer with value/pattern/choice delimitersmem_tokenizeValue,mem_tokenizePattern,mem_tokenizeChoicemem_SplitIter,mem_WindowIter(planned)
- Allocator Interface:
mem_Allocator— Unified allocator interface - Tracker:
mem_Tracker— Memory leak and double-free detection
| Allocator | Description |
|---|---|
Classic |
Traditional heap allocation (C runtime malloc/free) |
Page |
Page-aligned allocation for OS-mapped memory blocks |
Sbrk |
Sbrk-based allocation for linear memory growth and reuse |
Sys |
System-dependent allocation (Page or Sbrk) |
Fixed |
Fixed-size block allocator for bulk operations |
ThrdSafe |
Thread-safe wrapper for any allocator |
Smp |
SMP-aware allocation with per-core caching |
Arena |
Region-based allocation for bulk operations |
Pool |
Pool-based allocation for object reuse |
Record layout and field access from TypeInfo, operating on the meta type system
(u_P$raw, u_S$raw).
For the relationship between this module and the meta type system (prl/types/meta),
see Meta System.
Compile-time type info is in builtin/core (typeInfo$, sizeOf$, alignOf$).
- Record from fields:
u_typeInfoRecord,u_sizeOfRecord,u_alignOfRecord - Field offsets:
u_offsetTo,u_offsets - Field/record pointers (meta):
u_fieldPtr,u_fieldPtrMut,u_fieldPtrs,u_fieldPtrsMut,u_recordPtr,u_recordPtrMut - Array type info:
u_typeInfoA,u_sizeOfA,u_alignOfA - N-replicated records:
u_typeInfoRecordN,u_offsetToN,u_offsetsN,u_fieldSli,u_fieldSliMut,u_fieldSlis,u_fieldSlisMut,u_recordNPtr,u_recordNPtrMut
Concurrency
C11 atomics and type-safe atomic value wrappers (prefix atom_).
- Ordering:
atom_MemOrd—unordered,monotonic,acquire,release,acq_rel,seq_cst - Operations:
atom_fence,atom_load,atom_store,atom_fetchXchg,atom_cmpXchgWeak$,atom_cmpXchgStrong$,atom_fetchAdd,atom_fetchSub,atom_fetchAnd,atom_fetchOr,atom_fetchXor,atom_fetchNand - Atomic value wrapper:
atom_V$(_T),atom_V_zero$,atom_V_init$,atom_V_from— generic atomic variable type
OS thread management and synchronization primitives.
| Primitive | Description |
|---|---|
Thrd |
Thread creation, spawn, join, detach |
Ftx |
Futex (fast userspace mutex) |
Mtx |
Mutex |
Sem |
Semaphore |
Cond |
Condition variable |
RWLock |
Read-write lock |
ResetEvent |
Manual/auto reset event |
WaitGroup |
Wait for multiple tasks |
- Key Functions:
Thrd_spawn,Thrd_join,Thrd_detach,Thrd_current,Thrd_yield,Thrd_sleep
State-machine-based coroutines for ultra-lightweight async processing.
- Syntax:
async_fn_,await_,suspend_,resume_,areturn_ - Context:
Co_Ctx,Co_CtxFn$
Multi-processing utilities for parallel workloads.
Text & Encoding
ascii_isAlpha, ascii_isDigit, ascii_isSpace, ascii_toLower, ascii_toUpper
utf8_encode, utf8_decode, utf8_isValid, utf8_validate, utf8_count, utf8_view, utf8_iter
utf16_encode, utf16_decode, utf16_codepointSeqLen, utf16_isHighSurrogate, utf16_isLowSurrogate
wtf8_encode, wtf8_decode, wtf8_view, wtf8_iter
wtf16_iter, wtf16_Iter_next
unicode_utf8ToUTF16, unicode_utf16ToUTF8, unicode_wtf8ToWTF16, unicode_wtf16ToWTF8
System & OS
- Windows:
cfg,base,handle,debug,nls,sysinfo,mem,file,io,dll,console,proc,thrd,sync,nt,auth,crypt,sock
POSIX API compatibility layer for cross-platform code.
Process management utilities for cross-platform code.
- Submodules:
cfg,common,Duration,Instant,SysTime - Duration:
time_Duration_fromSecs,time_Duration_fromMillis,time_Duration_fromNanos,time_Duration_add,time_Duration_sub - Instant:
time_Instant_now,time_Instant_elapsed,time_Instant_durationSince - SysTime:
time_SysTime_now - Sleep:
time_sleep
I/O & Formatting, Filesystem
- Submodules:
common,stream,Reader,Writer,Fixed,Buf - Stream:
io_stream_print,io_stream_println,io_stream_eprint,io_stream_eprintln,io_stream_nl - Reader:
io_Reader_read,io_Reader_readExact,io_Reader_readByte,io_Reader_skip - Writer:
io_Writer_write,io_Writer_writeBytes,io_Writer_writeByte,io_Writer_print,io_Writer_println,io_Writer_nl - Fixed (in-memory fixed buffer):
io_Fixed_reading,io_Fixed_writing,io_Fixed_written,io_Fixed_reset,io_Fixed_Reader_init,io_Fixed_reader,io_Fixed_Writer_init,io_Fixed_writer - Buf (buffered Reader/Writer):
io_Buf_Reader_init,io_Buf_Reader_fill,io_Buf_Reader_peekByte,io_Buf_Reader_readUntilByte,io_Buf_Reader_skipUntilByte,io_Buf_Reader_skip,io_Buf_reader,io_Buf_Writer_init,io_Buf_Writer_flush,io_Buf_writer
String formatting and parsing with a spec system (prefix fmt_).
- Submodules:
common,cfg - Format API:
fmt_format,fmt_formatVaArgs— format to writer;fmt_Spec,fmt_Type,fmt_Size,fmt_Align,fmt_Sign - Format by type:
fmt_formatBool,fmt_format$bool,fmt_formatUInt,fmt_format$usize/$u64/$u32/…,fmt_formatIInt,fmt_format$isize/…,fmt_formatFlt,fmt_formatPtr,fmt_formatErr,fmt_formatStr,fmt_formatStrZ,fmt_formatASCII,fmt_formatUTF8 - Parse:
fmt_parseBool,fmt_parse$bool,fmt_parseUInt,fmt_parse$usize/…,fmt_parseIInt,fmt_parseFlt, … - Type specifiers (fmt_Type):
{:x}/{:X}(hex),{:o}(octal),{:b}(binary),{:B}(boolean),{:u}(unsigned),{:di}(signed),{:f}/{:F}(float),{:p}/{:P}(pointer),{:e}(error),{:0}(void),{:c}(ASCII code),{:C}(UTF-8 codepoint),{:z}(null-terminated string),{:s}(slice string) - Float conversion:
Ryu-based fast float-to-string (configurable via
fmt_cfg)
- Levels:
log_debug,log_info,log_warn,log_error
- Submodules:
common,path,File,Dir - File:
fs_File_close,fs_File_reader,fs_File_writer,fs_File_Handle_promote - Dir:
fs_Dir_create,fs_Dir_close,fs_Dir_openDir,fs_Dir_openFile,fs_Dir_createFile,fs_Dir_deleteFile,fs_Dir_deleteDir,fs_Dir_readFile,fs_Dir_readFileAlloc - Path:
fs_path_join,fs_path_dirname,fs_path_basename(partial —fs_path_extensionplanned)
Networking utilities for cross-platform code.
HTTP client/server utilities for cross-platform code.
Testing & Entry Point
- Macros:
TEST_fn_,TEST_expect,TEST_expectMsg,TEST_expectEq,TEST_expectNe - Usage:
Define tests with
TEST_fn_, run withdh-c test
- Macro:
fn_((main(S$S_const$u8 args))(E$void) $scope)— Standard entry point with argument parsing and error handling
The meta system has two related parts: the meta type system (type-erased generics)
and the meta module (record/type reflection).
Both use the same u_ prefix and share TypeInfo from core/type_info.h.
1. Meta type system (prl/types/meta.h, included via prl/types.h)
A zero-cost generic layer over PRL types so algorithms can operate on values without knowing the concrete type at compile time.
-
What it is: Type-erased wrappers that carry a raw representation (pointer, slice, array, optional, or result) plus runtime type information (
TypeInfo: size, align). A single implementation can handle any type that fits the same shape (e.g. any sliceS$Tasu_S$raw). -
How it works:
- Unified meta types:
u_V$raw(value),u_P$raw/u_P_const$raw(pointer),u_S$raw/u_S_const$raw(slice),u_A$raw(array),u_O$raw(optional),u_E$raw(error result). Each stores the same underlying pointer/slice/layout as the typed form plusTypeInfo, so there is no extra indirection. - Conversion:
Typed → meta:
u_retV$(_T)etc. from a type;u_anyP(_p),u_anyS(_s),u_anyV(_v),u_anyA(_a),u_anyO(_o),u_anyE(_e)from a value. Meta → typed:u_castP$((_T)(meta)),u_castV$((_T)(meta)),u_castS$((_T)(meta)),u_castA$((_N,_T)(meta)),u_castO$((_OT)(meta)),u_castE$((_ET)(meta)). - Generic operations:
Slicing/indexing use
TypeInfofor stride and length (u_atS,u_sliceP,u_sliceS,u_prefixP,u_suffixS, …). Memory and comparison use size/align and optional custom eql/ord (u_memcpy,u_memeql,u_memord,u_eql,u_ord,u_lt, …;u_*Byandu_*Ctxfor custom comparators).TypeInfo.alignis log2-encoded; usemem_log2ToAlign()when you need byte alignment.
- Unified meta types:
-
Zero-cost: No vtables or runtime dispatch; only
TypeInfoand inlined logic. With LTO and constant folding, meta-based code optimizes like hand-written typed code. In release builds, assertions become compiler optimization hints. Anonymous type compatibility: when layout (size, align, offsets) matches, anonymous types interoperate with meta without a typedef. -
Allocation, type safety, and value semantics:
1. Dynamic allocation
Stack allocation is built in:
u_allocV(_type),u_make(_type),u_create(_type)allocate a single value withalloca;u_allocA(_len, _type)allocates an array of values on the stack. All return meta wrappers (e.g.u_V$raw,u_P$raw,u_A$raw) whose referenced memory lives in the current stack frame. For heap allocation, the meta system does not allocate itself: you obtain a pointer (or slice) from your allocator, then wrap it with the meta layer usingu_init$S,u_anyP,u_anyS, etc. So stack vs heap is determined by where the underlying pointer comes from; the meta layer only attachesTypeInfoand the same operations apply.2. Type safety
The meta system is not fully type-safe in the static sense: you can cast to a wrong type with
u_cast*and misuse memory. It provides weak type safety in that it preserves the memory range and boundaries of a type:TypeInfo(size, align) is carried with every pointer and slice, and operations (copy, compare, index, slice) use that information so that accesses stay within the described size and alignment. Unlikevoid*, meta types carry layout information, enabling bounds-checked operations. The responsibility to use the correct target type when casting remains with the caller.3. Values vs pointers/slices; avoiding dangling pointers; lifetime
Elements allocated on the stack (via
u_allocV,u_make, etc.) are exposed as pointers in the meta representation (e.g.u_V$rawholds a pointerinnerplusTypeInfo). To prevent dangling pointers and keep lifetime clear across stack frames,meta values are treated as copy semantics.
-
Values are copied. When a value is passed to another function, the content referred to by the meta value is copied: the callee receives (or constructs) a meta value that refers to its own memory— e.g. the callee uses
u_load(_v)to get a newu_V$rawwhoseinnerpoints to a buffer in the callee's frame (or the caller allocates and copies before the call). So although the meta representation is "pointer + type", each logical copy has its own memory. That gives immutability between original and copies: the caller's and callee's storage are independent.When the callee returns a value, the referenced memory is copied back to the caller's side for use (e.g. into the caller's stack or a buffer the caller owns), so the caller again has its own copy. The lifetime of the referenced pointer is therefore clear: the callee's meta value refers to storage that lives in the callee's scope (or storage the callee owns); the caller's refers to the caller's. No shared reference to the other frame's stack.
-
Why meta-value types when we have pointer and slice types? Pointers (
u_P$raw) and slices (u_S$raw) are by-reference: they share the same underlying memory. They are used when the lifetime of the referent is known to outlive the use (e.g. caller-owned buffer, or heap). The value type (u_V$raw) exists to express by-value semantics: "this is a value that, when passed across boundaries, is copied." So for passing data across stack frames or to callees that should not alias the caller's storage, you use the value type and the copy convention (e.g.u_load, or copy-in/copy-out at call boundaries). That way you avoid dangling references and keep ownership and lifetime explicit.Reference vs copy:
u_deref(p)gives reference access to the pointee without copying;u_load(v)creates an independent stack copy for safe cross-boundary passing. For indirection patterns (e.g. storing indices or handles with context-based comparison), stack usage is bounded by the handle size, not the referenced data.
-
2. Meta module (dh/meta.h)
Record/type reflection built on top of the meta type system.
The module includes prl.h (and thus gets u_P$raw, u_S$raw, etc.)
and provides layout and field access in terms of meta pointers and slices.
- Record layout from fields:
Given a slice of field
TypeInfos, it computes recordTypeInfo, size, alignment, and field offsets:u_typeInfoRecord,u_sizeOfRecord,u_alignOfRecord,u_offsetTo,u_offsets. Array-shaped records:u_typeInfoA,u_sizeOfA,u_alignOfA; N-replicated records:u_typeInfoRecordN,u_offsetToN,u_offsetsN. - Field access as meta:
u_fieldPtr/u_fieldPtrMuttake a meta pointer to a record (u_P_const$raw/u_P$raw) and a slice of fieldTypeInfos, and return a meta pointer to the chosen field.u_fieldPtrs/u_fieldPtrsMutfill a slice of meta pointers for all fields.u_recordPtr/u_recordPtrMutgo from a field meta pointer back to the record meta pointer. So the meta module is the layer that lets you describe structs by their field types and then read/write fields through the same type-erasedu_*representation that the meta type system uses. - N-replicated (SoA-style) access:
u_fieldSli,u_fieldSliMut,u_fieldSlis,u_fieldSlisMutexpose a field across N records asu_S_const$raw/u_S$raw;u_recordNPtr/u_recordNPtrMutgo from a field slice back to the record meta pointer.
Relationship:
The meta type system defines the generic representation
(pointer/slice/array/option/result + TypeInfo).
The meta module uses that representation for reflection:
it computes layout from field TypeInfos
and gives you field and record access as u_P$raw / u_S$raw,
so generic code can walk records without knowing the concrete struct type.
Both rely on TypeInfo from core/type_info.h
(and compile-time typeInfo$(_T) from builtin/core).
| Category | Support Range |
|---|---|
| OS | Windows, Unix, Linux, macOS |
| Architecture | x86 (32-bit), x64 (64-bit) |
| Clang | 19.1.0+ (Recommended) / 16.0.0+ (Supported) / 9.0.0+ (Minimum, Requires -std=gnu11) |
| GCC | 15.1.0+ (Recommended) / 13.1.0+ (Supported) / N/A (TBU) (Minimum, Requires -std=gnu11) |
| MSVC | Planned (TBD) |
Note: The
dh-cbuild tool's support range is Prerequisites.
fn_((findValueIndex(i32 value, S_const$i32 items))(O$i32) $scope) {
for_(($s(items), $rf(0))(item, index) {
if (*item != value) return_some(index);
});
return_none();
} $unscoped_(fn);
fn_((example(void))(void)) {
var nums = A_from$((i32){ 10, 20, 30, 40, 50 });
let found = findValueIndex(30, A_ref$((S$i32)(nums)).as_const);
io_stream_println(u8_("found = {:?d}"), found);
if_some((found)(index)) {
io_stream_println(u8_("- Found at: {:d}"), index);
} else_none {
io_stream_println(u8_("- Not found"));
}
let value = orelse_((found)(-1));
let value_assumed = unwrap_(found);
}errset_((math_Err)(
DivisionByZero,
Overflow,
Underflow
));
T_use_E$($set(math_Err)(i32));
$attr($must_check)
fn_((safeDivide(i32 num, i32 denom))(math_Err$i32) $scope) {
if (denom == 0) return_err(math_Err_DivisionByZero());
return_ok(num / denom);
} $unscoped_(fn);
$attr($must_check)
fn_((example(mem_Allocator gpa))(E$void) $guard) {
// Allocate resources
var buffer = u_castS$((S$i32)(try_(mem_Allocator_alloc(gpa, typeInfo$(i32), 100))));
defer_(mem_Allocator_free(gpa, u_anyS(buffer)));
// Only executed when an error occurs and propagates
errdefer_(err, io_stream_eprintln(u8_l("Occurred error!: {:e}"), err));
// Error propagation (try_) and handling (catch_)
let divided = try_(safeDivide(10, 0));
let divided_handled = catch_((safeDivide(10, 0))($ignore, 1)); // Use default value 1 when error occurs
return_ok({});
} $unguarded_(fn);typedef variant_((InputEvent $bits(8))(
(InputEvent_press_key, struct { i32 key; }),
(InputEvent_release_button, struct { i8 button; })
)) InputEvent;
T_use_O$(InputEvent);
fn_((pullInputEvent(void))(O$InputEvent));
fn_((example(void))(void)) {
if_some((pullInputEvent())(event)) match_(event) {
pattern_((InputEvent_press_key)(on_pressed)) {
debug_assert_true_fmt(
-1 < on_pressed->key && on_pressed->key <= 255,
"key is out of range"
);
break;
} $end(pattern);
pattern_((InputEvent_release_button)(on_released)) {
debug_assert_true_fmt(
-1 < on_released->button && on_released->button <= 5,
"button is out of range"
);
break;
} $end(pattern);
fallback_(claim_unreachable);
} $end(match);
} $unscoped_(fn);T_use$((i32)(
ArrList,
ArrList_init,
ArrList_fini,
ArrList_appendWithin
));
fn_((collectEvenSq(S_const$i32 items, mem_Allocator gpa))(mem_Err$ArrList$i32) $scope) {
let init = ArrList_init$i32;
let appendWithin = ArrList_appendWithin$i32;
return_ok(chain$((ArrList$i32)(items)(
filter_((x)(int_isEven(*x))),
map$((i32)(x)(int_sq(*x))),
fold_(try_(init(gpa, items.len)), (collect, x)(appendWithin(&collect, *x), collect))
)));
} $unscoped_(fn);
fn_((reduceSumEvenSq(S_const$i32 items))(O$i32)) {
return chain$((O$i32)(items)(
filter_((x)(int_isEven(*x))),
map$((i32)(x)(int_sq(*x))),
reduce_((acc, x)(acc + *x))
));
};
$attr($must_check)
fn_((example(void))(E$void) $guard) {
var page = (heap_Page){};
let gpa = heap_Page_allocator(&page);
let nums = A_ref$((S$i32)(A_from$((i32){ 1, 2, 3, 4, 5, 6, 7, 8 }))).as_const;
let even_sqs = try_(collectEvenSq(nums, gpa));
defer_(ArrList_fini$i32(&even_sqs, gpa));
let sum = chain$((i32)(even_sqs.items)(fold_((0), (acc, item)(acc + *item))));
let sum_even_sqs = orelse_((reduceSumEvenSq(nums))(0));
claim_assert(sum == sum_even_sqs);
return_ok({});
} $unguarded_(fn);In addition to traditional OS threads, state-machine-based coroutines are provided for ultra-lightweight asynchronous processing.
Thrd_fn_(((timesTwoThread)(i32 input))(i32) $scope($ignore, args)) {
time_sleep(time_Duration_fromMillis(10));
return_(args->input * 2);
} $unscoped_(Thrd_fn);
fn_((mainThread(S$S_const$u8 args))(E$void) $scope) {
let_ignore = args;
var task = try_(Thrd_spawn(Thrd_SpawnConfig_default, Thrd_FnCtx_from$((timesTwoThread)(10)).as_raw));
let result = Thrd_FnCtx_ret$((timesTwoThread)(Thrd_join(task)));
io_stream_println(u8_l("result: {:d}"), result);
return_ok({});
} $unscoped_(fn);
async_fn_(((timesTwoAsync)(O$$(Co_Ctx*) caller, i32 input))(i32) $scope({
var_(sleep_ctx, Co_CtxFn$(exec_sleep));
})(self_ctx, args, locals)) {
callAsync((locals->sleep_ctx)((exec_sleep)(
some(orelse_((caller)(self_ctx->anyraw))), time_Duration_fromMillis(10)
)));
areturn_(args->input * 2);
} $unscoped_(async_fn);
async_fn_(((mainAsync)(S$S_const$u8 args))(Void) $scope({
var_(task, Co_CtxFn$(timesTwoAsync));
})($ignore, $ignore, $ignore)) {
locals->task = async_ctx((timesTwoAsync)(none(), 10));
await_(resume_(locals->task));
io_stream_println(u8_l("result: {:d}"), Co_Ctx_returned(task));
areturn_({});
} $unscoped_(async_fn);More Code Samples
Provides type-safe and intuitive API for load, store, CAS operations by wrapping C11 Atomics.
TODO: document
Provides vector parallel operation acceleration through a unified interface independent of CPU architectures (AVX, NEON, etc.).
TODO: document
Provides a generic data structure processing and serialization foundation
by leveraging compile-time type information (typeInfo$) and reflection.
See Meta System for more details.
TODO: document
Designs all data structures and functions to be dynamically allocated, accepting allocators or memory buffers to fully control memory layout.
TODO: document
#include <dh/main.h>
#include <dh/TEST.h>
// Define functions to test
fn_((mathAdd(i32 a, i32 b))(i32)) { return a + b; }
fn_((mathMul(i32 a, i32 b))(i32)) { return a * b; }
TEST_fn_("Basic Math Operations Test" $scope) {
// Addition test
let_(a, i32) = 5;
let_(b, i32) = 7;
let_(sum, i32) = mathAdd(a, b);
// Validate results
try_(TEST_expect(sum == 12));
try_(TEST_expectMsg(sum > 10, "Sum should be greater than 10"));
// Multiplication test
let product = mathMul(a, b);
try_(TEST_expect(product == 35));
// Failing test (intentional error)
let should_fail = TEST_expect(product == 30); // Fails: 35 != 30
try_(TEST_expect(isErr(should_fail)));
} $unscoped_(TEST_fn);Note: Comprehensive documentation is a work in progress. For now, please refer to:
- The code samples in this README
- Header files in
dh/include/dh/which contain detailed comments and API declarations- Example projects in the repository
The name "dasae-headers" originates from the project's roots as a header-only library designed to collect frequently used C utility code.
Due to continuous evolution and functional expansion, it has now adopted a structure that includes dedicated build tools and source files, moving beyond the scope of a simple "header-only" library. We maintain structural flexibility to enhance the user experience and allow for further high-level optimization.
Consequently, the current name does not yet fully fix the final identity of the project. If you have suggestions for a unique name that better reflects the philosophy dasae-headers aims to achieve, please let us know :D
We welcome issues, feature requests, and pull requests! Please refer to the Contributing Guide for more details.
- Author: Gyeongtae Kim (dev-dasae)
- Email: codingpelican@gmail.com
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright © 2024-2026 Gyeongtae Kim.