|
MULE 0.0.1
Multi-Use Library Essentials
|
Each module's topic page explains that module. This page explains the rules every module shares. Learn these once and each function behaves the way you expect before you read its documentation.
A project copies one file, dist/mule.h. Include it anywhere for the declarations. In exactly one .c file, define MULE_IMPLEMENTATION first, and the library compiles into that file:
Include it before any system header. Base sets _POSIX_C_SOURCE, which only takes effect before the first one. Define MULE_TEST as well to get the Test harness.
Inside this repository, programs build as one translation unit instead. Each test and example includes the .c files it needs, as in #include "str/str.c". That is why every example here compiles with a single compiler call.
The library needs a C11 compiler and is written in the style of C99. It is tested with clang 17+ and gcc 13+ on macOS and Linux.
Each module is a header and a source file under src/. They depend on each other in this order, so a module only uses the ones above it.
| Module | Gives you | Uses |
|---|---|---|
| Base | Feature macros, bool, checked arithmetic | |
| Mem | Allocator, Arena | base |
| Str and StrView | Str, StrView | mem |
| Arr | Arr(T) | mem |
| Rand | Rand | base |
| Time | time_now_ns() | base |
| Log | log_info() and friends, log_assert() | str |
| Test | TEST, CHECK, RUN | log |
A function that can fail returns bool. False means it did nothing: a failed call leaves every argument byte for byte as it was. The tests prove this by making each allocation fail in turn and comparing the arguments afterwards.
Because nothing changes on failure, calls chain with &&, and the chain stops at the first one that fails:
The compiler warns when a result that must be checked is ignored. gcc warns even through a (void) cast.
Two other kinds of failure do not return bool:
Str, StrView, Arena, Arr and Rand are ready to use when zero-initialised, and own nothing until something is put in them:
The free functions return a value to this empty state, keeping only its allocator. Calling one twice is safe, and a struct whose fields were zero-initialised can be freed even when it was only half filled in.
Every allocation goes through an Allocator. A Str or an Arr holds a pointer to one in its alloc field, and null means the libc heap. Point it at an arena with arena_allocator(), and the value grows inside the arena and is freed with it:
The value that owns memory frees it. A Str is freed with str_free(), an Arr with arr_free(), and an arena with everything in it by arena_free(). A struct that holds owning fields gets its own _free function that frees each one.
Never copy an owning struct by assignment. Str b = a; copies the pointer, not the bytes, and freeing both is a double free. Copy the contents with str_from(), or move them by copying the struct and resetting the source to { 0 }.
Anything that points into a buffer dies when the buffer moves:
Reserve the full size first with str_reserve() or arr_reserve(), and the appends that fit will not move the buffer.
The library keeps no global state except the log level and the log output. Set both before starting threads. Each log line is written with one fwrite, so lines from different threads do not interleave. Every other value belongs to whoever holds it, so give each thread its own, or lock around a shared one.
Releases follow Semantic Versioning. Each one is a GitHub release with mule.h attached, and CHANGELOG.md lists what changed. Before 1.0.0, a minor release may break the API, and a patch release never does. MULE_VERSION and the MULE_VERSION_* macros in Base say which version a program was built with.
This reference follows the main branch, which may be ahead of the latest release.