The SafeC standard library (std/) covers core utilities, collections, allocators, threads and async, networking, TLS and HTTP, GUI, audio, DSP, machine learning, filesystems, encoding and serialization, cryptography, debugging, and bare-metal support. Each module is a .h header, plus a .sc implementation for the non-generic parts; generic containers are header-only. #include only ever names .h files — safeguard links the .sc implementations of the modules a program uses (from the prebuilt std, or compiled into build/deps/libsafec_std.a; see Including the Standard Library). Every public declaration, with its doc comment, is in the API Reference. Include prelude.h to pull in most modules at once; GUI, audio/MIDI, encoding, HTTP/RPC/wasm, async, the real-time scheduler, ML and SIMD are included by their own headers.
c
#include <std/prelude.h>
intmain() {
println("Hello from SafeC stdlib!");
return0;
}
TlsStream clients and TlsAcceptor servers over a socket: the OS's TLS (OpenSSL via dlopen, SChannel, SecureTransport) or SafeC's own TLS 1.3; system-root verification, ALPN, client certificates, non-blocking TLS_WANT_*
"Write once, call from anywhere" JSON-marshaled server functions (Dioxus/Leptos-style)
reactive
reactive/signal.h
Signal<T> fine-grained reactive state (WASM client hydration)
wasm
wasm/dom.h, wasm/hydrate.h
wasm32 DOM and browser APIs (events, timers, fetch, storage, history) + signal hydration, with the wasm/safec_dom.js glue; wasm/wasm_rt.h is the freestanding runtime (size-class malloc/free over memory.grow, mem*/str*/strto*/qsort, 128-bit compiler-rt builtins, trapping abort) for wasm32/wasm64 — only a wasm target's std archive includes it (see WebAssembly)
scx
n/a (transpiler)
JSX/TSX-style HTML templating — .scx files transpile to plain SafeC before safec ever sees them
Windows (options, HiDPI, cursors, focus), events, colors, the clipboard and clock over an RGBA8888 buffer per window; X11, Wayland, Win32, Cocoa, WebAssembly, headless and bare-metal framebuffer backends
app
gui/gui_app.h
GuiApp: several windows from one event loop, timers, per-frame animation, redraw-on-change, a pumpable loop
backend
gui/gui_backend.h
The GuiBackend interface every backend implements, and GuiPresenter for GPU presentation
headless
gui/gui_headless.h
An in-memory backend with a virtual clock and input injection, for tests
canvas
gui/gui_canvas.h
Anti-aliased vector canvas: paths, strokes, dashes, gradients, patterns, transforms, clipping, blend modes, images, text — see GUI Graphics
path
gui/gui_path.h
GuiPath (with SVG path data), GuiMatrix, GuiRect
text
gui/gui_text.h
TrueType/OpenType reader (GuiTypeface, kerning, system fonts, a built-in fallback face) and GuiTextLayout
widget
gui/gui_widget.h
Retained widget toolkit: layouts, themes, focus, menus, dialogs, tooltips and 30+ widgets — see GUI Widgets
png
gui/gui_png.h
GuiImage: from-scratch PNG decoder and encoder, scaling
draw
gui/gui_draw.h
Integer rectangles, borders, lines, built-in 8x8 bitmap text
font
gui/gui_font.h
GuiFont bitmap fonts of any glyph size; SCXF save/load
svg
gui/gui_svg.h
SVG subset renderer (shapes, paths with cubic curves)
Containers and other value-holding APIs are generic structs and functions, monomorphized per element type. They hand out ?&T references (narrow them with match, .default(x), or std::expect_ref), return ?T when there may be no value, and take typed fn callbacks — the standard library has no void* element or callback parameters. T is written once, on the variable, and the constructor infers it from that target type:
c
#include <std/collections/vec.h>
intmain() {
structVec<int> v = std::Vec::new();
v.push(42);
v.push(100);
match (v.get(0UL)) {
casesome(x): *x = *x + 1; // 43, written in placecasenull: { }
}
int last = v.pop().default(-1); // 100
v.free();
return last == 100 ? 0 : 1;
}
Where a container must hold callbacks with different context types at once (a timer wheel, a widget tree, a VFS mounting several drivers), the slot stores the pair type-erased, but the only way to fill it is a generic method that takes a matching fn(?&T) and ?&T — TimerWheel::add, Widget::on_click, Vfs::mount, TaskScheduler::spawn — so every call is still checked at compile time. The erased slot is a ?&Erased (std/erased.h), never a void*. std has no void* even at the C ABI boundary: untyped memory is bytes (unsigned char*, what mem.h's raw allocator returns), libc/OS/C-library objects (FILE, pthread objects, SSL, CoreFoundation references, Win32 handles, ...) are opaque structs, and callbacks are fn types. tests/std_modules/api_lint.py (run by the smoke test) fails the build if a raw pointer appears in a public declaration, or a void* anywhere in std.
Buffers are slices. Wherever a C API would take a pointer and a length (or return an array through a pointer), std takes a built-in slice: []T when it writes the elements, []const T when it only reads them — f.read(buf[..]), sha256(data, digest[..]), fft(xs[0..n]), String::from_bytes(str_bytes("text")). A slice carries its length, indexing it is bounds-checked, and an output that must have a fixed size (a key, a digest) is checked on entry. See Slices.
When building with the safeguard package manager, the std modules the program includes (and the ones its code calls without an include: new, channels) are compiled into build/deps/libsafec_std.a and linked, with the system libraries they need — or, in a release install, taken from its prebuilt std archive (see Standard Library Modules).
When building manually, put the SafeC checkout on the include path (headers are named <std/...>) and link an archive of all of std — safeguard build of a project with [build] std = "all" makes one (or build it from every std/**/*.sc) — with the system libraries of the modules you use (safeguard std-modules std/x.h lists them):