CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
Overview

String Types

The library uses three related types:

Most functions take strref for input (read-only) and strhandle for output. This pattern prevents accidental modification and makes ownership clear.

Conceptual Model - Immutable String Semantics

While the implementation uses copy-on-write optimization, the API is designed to behave as if strings were immutable values. Think of string variables like you would in Python, Java, or other languages with immutable strings:

string s1 = _S"hello"; // Create a string
string s2 = 0;
strDup(&s2, s1); // "Copy" s1 to s2 (shares buffer internally)
strAppend(&s1, _S" world"); // Modify s1 (gets its own copy automatically)
void strDup(strhandle o, strref s)
#define _S
Creates a static ASCII string from a string literal.
Definition strbase.h:392
bool strAppend(strhandle io, strref s)

After the append, s1 and s2 appear to be independent strings, even though they initially shared the same buffer. The copy-on-write mechanism is transparent - you program as if each variable owns its own immutable string value, but the library optimizes away unnecessary copies behind the scenes.

This mental model makes it easy to reason about string operations:

NULL Strings

A string variable set to NULL (or 0) is treated as an empty string throughout the entire API. This is an intentional design decision for convenience:

string s = 0; // NULL string
strLen(s); // Returns 0
strEmpty(s); // Returns true
strEq(s, _S""); // Returns true
strDestroy(&s); // Safe, does nothing
strAppend(&s, _S"hello"); // Works, creates new string
uint32 strLen(strref s)
bool strEmpty(strref s)
void strDestroy(strhandle ps)
bool strEq(strref s1, strref s2)

This eliminates the need for null checks in most code and allows operations to work naturally with uninitialized or empty strings. Functions that return strings via output parameters will properly destroy any existing string first, so you can always safely initialize to NULL and let the functions manage it.

Copy-on-Write Mechanics

The library uses atomic reference counting to track how many variables reference the same underlying string buffer. When you use strDup(), both variables point to the same buffer with an incremented reference count. When a modification operation is performed (strAppend, strBuffer, etc.), the library automatically checks the reference count and creates a private copy if the buffer is shared.

String literals created with the _S macro have static storage and don't need reference counting - they're truly immutable and exist for the program's lifetime.

Creating Strings

String literals:

string s = _S"Hello"; // Static ASCII string literal
string s = _SU"Hello 世界"; // Static UTF-8 string literal
#define _SU
Creates a static UTF-8 string from a string literal.
Definition strbase.h:397

Building strings dynamically:

string s = 0; // Always initialize to NULL
strReset(&s, 256); // Create empty with capacity hint
strDup(&s, _S"content"); // Copy from another string
strAppend(&s, _S" more"); // Append operations
void strReset(strhandle o, uint32 sizehint)

Memory Management

All strings created dynamically (not static literals) MUST be destroyed:

string s = 0;
strDup(&s, _S"hello");
// ... use s ...
strDestroy(&s); // Required! Decrements ref count, frees if last

Strings initialized to NULL/0 are safe to destroy without allocation.

Thread Safety

Reference counting uses atomic operations for thread safety. Multiple threads can safely call strDup() on the same source string concurrently. However, modifying a string is not thread-safe - if multiple threads need to modify the same logical string, external synchronization is required.

Optimizations

The library automatically optimizes for different use cases: