# sp
> Guide for sp.h, a single-header C standard library replacement. You must use this guide when using or discussing sp.h in any capacity.
- Author: Thomas Spader
- Repository: tspader/sp
- Version: 20260111095139
- Stars: 3
- Forks: 0
- Last Updated: 2026-02-07
- Source: https://github.com/tspader/sp
- Web: https://mule.run/skillshub/@@tspader/sp~sp:20260111095139
---
---
name: sp
description: Guide for sp.h, a single-header C standard library replacement. You must use this guide when using or discussing sp.h in any capacity.
license: MIT
---
# sp.h Overview
- sp.h is a single-header C standard library replacement
- You MUST annotate references to functions from sp.h with verbatim function headers
- When providing references to code from `sp.h`, you MUST provide a matching declaration from `references/index.md`. Function names without the full declaration are COMPLETELY useless, and WILL NOT be tolerated.
user: How do I use the asset registry from sp.h?
assistant: [Reads the index, uses the Task tool to search through sources bundled with skill (sp.h, spn.c), includes "sp_str_t sp_str_sub(sp_str_t str, s32 index, s32 len)" in answer]
user: Write a function that reads a file and logs its contends
assistant: [Searches through bundled source code with Task tool to find relevant APIs and writes function]
- NEVER, EVER MODIFY THE REFERENCE CODE
## Usage
- Search `references/index.md` before trying to search through the codebase. Do not guess; refer to `references/index.md` to find a precise search term.
user: How do I read a file in sp.h?
assistant: [Reads index.md, searches through sp.h and spn.c with Task tool, provides concise, annotated answer]
- Search through `references/sp.h` judiciously as needed; do not guess symbol names, function signatures, or implementation details. Read the source code.
- Function signatures are prefixed with `SP_API`
- Types are prefixed with `sp_` and suffixed with `_t`
## Rules
- Never use `malloc`, `calloc`, or `realloc`; use `sp_alloc` (which zero initializes)
- Unless explicitly interfacing with an existing C API, never use `const char*`; use `sp_str_t` (pointer + length)
- Never use `strcmp`, `strlen`, or any `string.h` functions with `sp_str_t`; use `sp_str_*`
- Never use `strcmp`, `strlen`, or any `string.h` functions with `const char*`; use `sp_cstr_*`
- Always use `SP_ZERO_INITIALIZE()`. When you need a type, use `SP_ZERO_STRUCT(T)`
- Always use `sp_da(T)` and `sp_ht(T)` for dynamic arrays and hash maps (`sp_dyn_array_*` and `sp_ht_*`)
- Always use `sp_dyn_array_for(arr, it)` and `sp_ht_for(ht, it)` to iterate sp_da and sp_ht
- Never check `str.len > 0`; always use `!sp_str_empty(str)`
- Always use C99 designated initializers for struct literals when possible
- Always use short literal types (`s32`, `u8`, `c8`, `const c8*`)
- Never use `printf` family; always use `SP_LOG()`
- Always use `sp_carr_for()` when iterating a C array
- Always explicitly handle all enum cases in a switch statement. Fallthroughs are OK, `default` is not.
## Namespaces
Use these when searching through `references/index.md`, `references/sp.h`, or `references/spn.c`
- Memory: `sp_alloc`, `sp_context`, `sp_allocator`, `sp_os`
- Strings: `sp_str`, `sp_str_builder`, `sp_cstr`
- Containers: `sp_dyn_array` / `sp_da`, `sp_ht`, `sp_rb`
- IO: `sp_io`
- Process: `sp_ps`
- Filesystem: `sp_os`
- Platform: `sp_os`
- Time: `sp_tm`
- Concurrency: `sp_thread`, `sp_mutex`, `sp_semaphore`, `sp_atomic`, `sp_spin_lock`
- Logging: `sp_format`, `SP_LOG`, `SP_FMT_*`
## Common Patterns
### Initialization
```c
// Always zero-initialize structs
sp_str_builder_t builder = SP_ZERO_INITIALIZE();
sp_dynamic_array_t arr = SP_ZERO_INITIALIZE();
```
### String Handling
```c
// Create strings
sp_str_t literal = sp_str_lit("hello"); // Compile-time string literal
sp_str_t view = sp_str_view(some_char_ptr); // Runtime C string (calculates length)
sp_str_t copy = sp_str_from_cstr("hello"); // Allocates and copies
const char* cstr = sp_str_to_cstr(str);
```
### Dynamic Arrays (stb-style)
```c
sp_dyn_array(int) numbers = SP_NULLPTR;
sp_dyn_array_push(numbers, 42);
sp_dyn_array_push(numbers, 100);
sp_dyn_array_for(numbers, i) {
SP_LOG("numbers[{}] = {}", SP_FMT_U32(i), SP_FMT_S32(numbers[i]));
}
u32 count = sp_dyn_array_size(numbers);
u32 capacity = sp_dyn_array_capacity(numbers);
// Cleanup happens automatically via allocator
```
### Hash Tables (stb-style)
```c
sp_ht(s32, s32) hta = SP_NULLPTR;
sp_ht(sp_str_t, s32) htb = SP_NULLPTR;
sp_ht_set_fns(hta, sp_ht_on_hash_str_key, sp_ht_on_compare_str_key);
sp_ht_insert(htb, SP_LIT("answer"), 42);
s32* value_ptr = sp_ht_getp(htb, SP_LIT("answer"));
sp_ht_key_exists(htb, SP_LIT("answer"));
sp_ht_for(htb, it) {
sp_str_t* key = sp_ht_it_getkp(map, it);
s32* val = sp_ht_it_getp(map, it);
}
// Cleanup happens automatically via allocator
```
### Formatting and Logging
```c
// Type-safe formatting with color support
SP_LOG(
"Processing {:fg cyan} with {} {}",
SP_FMT_STR(name),
SP_FMT_U32(count),
SP_FMT_CSTR("items")
);
sp_str_t msg = sp_format("Result: {}", SP_FMT_S32(42));
// Colors: :fg, :bg, :color
// Colors: black, red, green, yellow, blue, magenta, cyan, white
// Add 'bright' prefix for bright variants
```
### Switch Statements
```c
// Always use braces, always handle all cases
switch (state) {
case STATE_IDLE: {
break;
}
case STATE_RUNNING: {
break;
}
default: {
SP_UNREACHABLE_CASE();
}
}
```
### Error Handling
```c
// Return an enum for recoverable errors (consumer app may have their own error type)
sp_err_t load_config(sp_str_t path, config_t* config) {
if (!sp_os_does_path_exist(path)) {
SP_LOG("Config not found: {}", SP_FMT_STR(path));
return SP_ERR_WHATEVER;
}
return SP_ERR_OK;
}
// Prefer to SP_ASSERT when possible
void process_array(int* arr, u32 size) {
SP_ASSERT(arr);
SP_ASSERT(size > 0);
}
// SP_FATAL is SP_LOG + SP_ASSERT(false)
if (critical_failure) {
SP_FATAL("Cannot continue: {:fg red}", SP_FMT_STR(reason));
}
```