Zero-heap memory manager in C11. A pool is a block of bytes you declare — ParsMemoriaeInternae, or
ParsMemoriaeExternum where the part has external RAM and MMGR_ENABLE_EXTRAM is set. Which memory
a pool sits in is settled where it is declared, and nothing afterwards inspects an address to find
out.
LocusCarcerum dresses pools as a prison site. Each cellblock is bound to the pool it was declared
over, the warden is the const struct emitted under the site's name that holds those cellblocks as its
members and that every call goes through, all of it is initialized data, and nothing runs at startup.
The footprint is a number you can check against a budget.
A pool name stands for one region. Declaring it twice in a translation unit fails to compile, in two units fails to link, and dressing one pool twice fails to compile — each with a diagnostic naming what went wrong.
A cellblock hands out from both ends. The persistent tier runs up from base. The temporary tier runs
down from size (src/locus_carcerum/locus_carcerum.h:16-17). The gap between them is what either
can still take. A cell from the persistent tier is the caller's
[RETURNS OWNERSHIP] until it goes back through that same cellblock's persistent_buf_release
[TAKES OWNERSHIP], and a request the cellblock cannot meet returns NULL
(src/locus_carcerum/locus_carcerum.h:484-486). Spans bound a view over storage the caller keeps
[BORROWS], and rings carry bytes between one producer and one consumer.
#include "mmgr.h"
/* Two pools. Each is a block of bytes and nothing more until something dresses it. */
ParsMemoriaeInternae(work, 2048);
ParsMemoriaeInternae(keys, 2048);
/* Dressing them is the claim, and it happens here rather than at run time. Releasing a cell in work
leaves its bytes as they are. Releasing one in keys zeroes them. Nothing after this line can
change either, and a cellblock is reached by the name of the pool it was built over. */
LocusCarcerum(prison, MMGR_MINIMUM_SECURITY(work), MMGR_MAXIMUM_SECURITY(keys));
void frame(void)
{
void *const cell = prison.work.persistent_buf_alloc(256u);
mmgr_span span = EMBED_CALL(spat.from, SpatiumCfg, .buf = cell, .cap = 256u);
EMBED_CALL(byteio.put_be, OctetusCfg, .write_span = &span, .value = 0xDEADBEEFu, .bytes = 4u);
}src/ |
the library, one directory per module |
include/mmgr.h |
the header a consumer includes: pool declarations, the name guards, the assert hook |
deps/embedded_types/ |
the widths, the word, the attribute wrappers and the dispatch macros every module builds on |
test/ |
unit per translation unit, integration and interop across modules, environment per width |
test/harness.py |
build, run, the A/B, coverage, and the suite and generator checks |
tools/dev_env/ |
the generators for src/'s tables, the size sweep, the source rewriters |
docs/ |
the prose, and the Doxygen configuration that renders it |
Latin names, because they were carved out of a larger tree and the short English words were taken.
| module | what it is |
|---|---|
locus_carcerum |
the two-ended allocator that everything else sits on |
memoria_anularis |
the lock-free ring between a producer and a consumer |
spatium |
bounded views over caller memory; owns nothing |
cellularum_laboro |
bounded string work: search, compare, parse |
verbum_scrutor |
the SWAR core the scans are built from |
clz |
leading and trailing zero counts, branchless |
memoria_operor |
copy, move, compare, set, find |
proximus_operor |
word-width loads and stores at any alignment |
verba_scribo |
text out: integers, hex, floats, JSON, XML |
numeros_scribo |
records built from a field spec |
byteio / bitio / endian |
the wire |
fractio |
takes a double apart, without <math.h> |
transformo |
decimal to binary, into a double or an integer |
pow5 |
generated: the powers of five the decimal conversion needs |
ascii_persona_bitorum / impensa_ancorae_acus |
generated: character classes and byte rarity |
memoria_externa |
internal or external placement; MMGR_ENABLE_EXTRAM |
memoriam_praetereo |
DMA channels over weak port hooks; MMGR_ENABLE_DMA |
The first configure reaches the network: it fetches embedded_types into deps/ at the SHA pinned
in deps/CMakeLists.txt:19. Name a generator and a compiler, because the default on Windows is MSVC
and MSVC does not build this tree — it stops at deps/embedded_types/include/embed_types.h:263,
where a static assertion reports that EMBED_ENUM_PACKED is not honored and no enum keeps its
declared width. GCC and Clang build it.
To build without the network, put embedded_types at deps/embedded_types yourself, checked out at
the pinned SHA, and add -DFETCHCONTENT_FULLY_DISCONNECTED=ON. That skips the fetch and uses what is
on disk; measured here at 190 of 190 targets. It is an escape for a rebuild on a machine that already
has the dependency, not a first clone — disconnected mode with an empty deps/ has nothing to use.
cmake -S . -B build -G Ninja -DCMAKE_C_COMPILER=gcc -DMMGR_BUILD_TESTS=ON
cmake --build build
ctest --test-dir buildOr through the harness, which carries the flags each build tree needs:
python test/harness.py test # build and run the suites
python test/harness.py ab # this library, then the same suites against libc
python test/harness.py coverage # what of src/ the suites reachedTwo environment variables matter on a first build, because there is no earlier tree to read them
off. MMGR_CMAKE_ARGS carries the generator and the compiler. MMGR_BUILD_ROOT moves the build
trees, which Windows makes necessary. A full object path is capped at 250 characters there and the
deepest object here sits about 180 below its build directory. A checkout more than about 60
characters down cannot build in place (test/harness.py:27-34).
- Nothing allocates. Zero heap
- Every width is a compile-time knob, and all of them are built. Width and order
- The scans are SWAR, in plain C, with no intrinsics. SWAR
- The namespace idiom devirtualizes. The ns idiom
to_double,verba.gandverba.fixedare exact.to_floatrounds twice and can land on the wrong neighbor. Numbers, and how far they can be trusted- 240 targets, 100% of
src/lines and branches, green against libc as well. The test suite - What each optimization level costs. Optimization
0.1.0, pre-1.0, so the API may change. What is measured is measured — the numbers in
docs/quality/ come out of tools in this repository and can be rerun.
Licensed AGPL-3.0-or-later. // various commercial contracts available It will always be free to use under the AGPL. Educators: If you would like an exception to use this in your classrooms or research projects, please feel free to email dstroy0 (Douglas Quigg) dquigg123@gmail.com from your _.edu or _.org faculty email address, I would be happy to grant you an exception on a case-by-case basis. Your exception governs your use, specifically the accreditation requirement of underlying systems in any research/presentation materials. Academic exemptions can lead to viable market products, in which case this license shifts to a royalty ladder, based arbitrarily off of the amount of goodwill you've shown and how well you've adhered to crediting students and other faculty involved in the project, a portion of the royalties go directly to your institution at a minimum and straight to your department if their rules allow for it. See CONTRIBUTING.md and SECURITY.md.