For decades, C developers have wrestled with the notorious pitfalls of null-terminated strings: buffer overflows, expensive length calculations, and substrings that force heap allocations. While newer languages sidestepped these issues, one developer argues C itself isn't the problem—it's the legacy ecosystem. In a bold treatise, they unveil sp_str_t, a modern string paradigm that transforms C into a surprisingly ergonomic language for contemporary tasks.

The Null-Terminated Trap

"string.h is a special kind of hell," the developer writes, highlighting fundamental flaws:
- Substrings require allocation and copying
- Length checks demand O(n) traversals
- Parser-friendly string views are impossible
- Security vulnerabilities proliferate

Enter sp_str_t: The Immutable View

At the core of their solution lies a simple struct:

typedef struct {
    const c8* data;
    u32 len;
} sp_str_t;

This immutable slice enables zero-copy operations:

  • Substrings without allocation: sp_str_sub() returns a view by adjusting pointers
  • Trimming: sp_str_trim_right() shrinks the length without mutation
  • Comparison: sp_str_equal() checks length first, then compares memory

The Builder Pattern: Mutable When Needed

For complex operations, sp_str_builder_t provides a mutable buffer:

SP_API sp_str_t sp_str_builder_write(sp_str_builder_t* builder);

This powers higher-level functions:
- Concatenation: Via sp_format("{}{}", a, b)
- Replacement: sp_str_replace_c8() scans and rebuilds
- Type-safe formatting: Custom sp_format() surpasses printf with compile-time checks

Solving the Allocation Problem

Immutable strings demand frequent allocations—but a global bump allocator eliminates the cost:

"A programming language is like a river. It defines a gradient of friction."

The thread-local bump allocator:
1. Reserves large memory blocks upfront
2. "Allocates" via pointer increments
3. Frees entire blocks at scope exit

This makes transient string operations nearly free, outperforming C++'s heap-heavy RAII approach.

Why Not Intrusive Pointers?

The developer considered header-based strings (à la stb_ds) but rejected them:

"You no longer know whether a const char* is null-terminated or one of our strings. The only way to avoid this extreme footgun is to null terminate all strings. And we're back where we started."

The Verdict

By decoupling C's core from its dated standard library, this approach delivers:
- Safety: Immutability prevents in-place mutation bugs
- Performance: Bump allocation slashes heap pressure
- Ergonomics: Builder APIs rival modern languages

As the author concludes: "My C string code is now as ergonomic as my Python code—minus a few operators." For developers shackled to C but yearning for modernity, this paradigm offers liberation.


Source: spader.zone