sp.h (sp.h1) is a single‑header, C99‑only standard library that sidesteps the legacy of libc by building directly on OS syscalls, offering explicit memory management, length‑based strings, and a portable, configurable API. The author argues that libc’s abstractions are now a liability for both simple and asynchronous programs, and proposes a library that is tiny, explicit, and easy to embed across Linux, Windows, macOS, WASM, and exotic environments.
Thesis
The author contends that the C programming language needs a fresh, ultra‑portable standard library that abandons the entrenched, often‑harmful abstractions of traditional libc. By writing sp.h (also called sp.h1) as a single‑header, C99‑only collection that talks directly to the operating system’s lowest‑level primitives, the library promises both ergonomic high‑level code and the performance benefits of zero‑copy, syscall‑driven I/O.
Key Arguments
1. Programs should be built on syscalls, not on libc’s legacy layers
- The library’s core consists of roughly 40 syscalls that constitute the only platform‑specific code. All higher‑level functionality is composed from these primitives, eliminating the “cruft” that has accumulated between the OS and user code over decades.
- By avoiding the traditional
FILE*abstraction and other POSIX‑style interfaces, sp.h encourages developers to use the kernel’s native I/O mechanisms, which is increasingly important for asynchronous and high‑throughput workloads.
2. libc is actively harmful for modern development
- The author argues that libc no longer offers a useful interface: simple programs would rather be written in a higher‑level language, while sophisticated programs need finer‑grained control than
FILE*and other legacy APIs provide. - Asynchronous programming has shifted the performance bottleneck from CPU‑centric concerns (e.g., register allocation) to I/O‑centric concerns, making libc’s blocking abstractions a liability.
3. Memory allocation must be explicit, not an implicit heap
- sp.h introduces a first‑class allocator type (
sp_allocator_t) that forces callers to pass an allocator function for every allocation, resize, or free operation. - This design makes the fiction of an ever‑available heap explicit: the OS delivers memory in pages, and any sub‑page allocation is a policy decision made by the caller, not an invisible runtime.
- The library’s allocation model is opt‑in; if a program prefers
malloc‑style semantics it can still use them, but it must do so deliberately.
4. Null‑terminated strings are replaced by length‑aware slices
- The author replaces
char *withsp_str_t, a struct containing a pointer and a length. This eliminates the need for a terminating\0, enabling:- O(1) length queries.
- Safe, non‑owning substrings.
- Zero‑copy parsing, as demonstrated by a word‑count example that reads a file, splits it into lines and words, and updates a hash table without copying the underlying buffers.
- Interfacing with legacy C APIs that expect null‑terminated strings is handled by explicit conversion, which the author argues is a negligible cost compared to the safety and performance gains.
5. Portability and minimalism are core goals
- sp.h compiles with any C99‑compatible compiler (GCC, Clang, MSVC, TCC, etc.) and works on Linux, Windows, macOS, WASM, and even exotic libc implementations like Cosmopolitan.
- The library is a single header file, requiring no build system, no configuration, and no external dependencies.
- Functions are namespaced, annotated with
@tagsfor easy search, and the codebase is deliberately kept small (≈15 k lines) to aid readability and maintainability.
6. Explicitness over hidden magic
- Errors are always returned as values; there is no global
errno‑style state. - No mutable global variables exist; every operation that could affect state takes an explicit context argument (often an allocator).
- Memory is zero‑initialized by default, reducing the class of uninitialized‑memory bugs.
7. Non‑goals clarify the project’s scope
- Conformance to existing libc interfaces is not a priority; sp.h will interoperate when required but does not aim to be a drop‑in replacement.
- Obscure architectures are out of scope; the focus is on mainstream x86_64, aarch64, and WASM targets.
- Micro‑optimizations such as hand‑written SIMD or highly tuned hash tables are deferred; the library prefers clear abstractions that enable zero‑copy I/O and correct memory handling.
Implications
- For system‑level developers – sp.h offers a pathway to write C code that feels as modern as Rust or Zig while retaining C’s unparalleled compiler support and direct machine‑code generation.
- For educational contexts – the library’s explicit allocator and string models provide concrete examples of how low‑level resources are managed, making it a valuable teaching tool.
- For cross‑platform projects – a single header that compiles everywhere reduces the maintenance burden of juggling multiple libc variants or dealing with platform‑specific quirks.
- For security – eliminating null‑terminated strings and hidden global state directly mitigates whole classes of buffer‑overflow and use‑after‑free vulnerabilities.
- For the broader ecosystem – sp.h could inspire a new generation of minimal, syscall‑centric libraries in other languages, encouraging a re‑examination of the assumptions built into decades‑old standard libraries.
Counter‑Perspectives
- Performance skepticism – While zero‑copy I/O can be faster, some workloads still benefit from highly tuned libc implementations that have been refined for specific kernels. The library’s “no SIMD, no hand‑rolled hash tables” stance may limit its suitability for compute‑bound hot paths.
- Ecosystem integration – Existing codebases heavily rely on
stdio.h,stdlib.h, and other libc headers. Migrating to sp.h would require substantial refactoring, which may be a barrier for large, legacy projects. - Tooling and debugging – Many debuggers and static analysis tools have built‑in knowledge of libc’s conventions. A novel allocator and string model could reduce the effectiveness of those tools unless they are explicitly taught about sp.h’s types.
- Community adoption – The success of a standard library hinges on community contributions, documentation, and long‑term maintenance. As a single‑author project, sp.h will need a robust governance model to survive beyond its initial release.
Conclusion
sp.h presents a compelling philosophical shift: treat the operating system as the only trusted primitive, make memory and string handling explicit, and keep the implementation small enough to be read and understood in a single sitting. By doing so, it challenges the entrenched dominance of libc and offers a pragmatic, portable foundation for modern C development. Whether it becomes a widely adopted alternative will depend on how quickly the community can integrate its concepts into existing workflows and whether its minimalism can coexist with the performance expectations of production‑grade software.
Resources
- GitHub repository: https://github.com/spader/sp.h1
- Example programs and documentation are bundled in the repo.
- Discussion channels: Discord server, IRC
#sp, and email (see author’s domain for address).
Comments
Please log in or register to join the discussion