MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
MMgr

Zero-heap memory manager in C11.

You declare a pool: a block of bytes, whose placement is settled by which declaration you write. LocusCarcerum dresses a pool as a cellblock, and a cellblock hands out cells from both ends. The persistent tier grows up from base, the temporary tier grows down from size, and the gap between them is what either can still take. An allocation neither the gap nor a free cell of its tier can meet returns NULL and moves no boundary. Spans are bounded views over cells, and rings move bytes between one producer and one consumer.

Nothing calls malloc. Every size is fixed before the program runs. The footprint is a number you can check against a budget before the program starts.

#include "mmgr.h"
/* A pool of bytes, and the line that dresses it. Together they are the whole setup. */
/* The owner a cell is counted to, declared as data with the pool: its record in the program's owner table
and the const pointer `writer` into it. */
#define PROGRAM_OWNERS(owner_) owner_(writer, "writer")
MMGR_OWNERS(PROGRAM_OWNERS)
char *const buf = prison.work.persistent_buf_alloc(256u, writer);
size_t at = 0;
at = EMBED_CALL(verba_textus.put, VerbaTextusCfg, .out = buf, .cap = 256u, .at = at, .text = "id=");
at = EMBED_CALL(verba_numerus.u64, VerbaNumerusCfg, .out = buf, .cap = 256u, .at = at, .val = 4211u);
const size_t len = EMBED_CALL(verba_finis.finish, VerbaFinisCfg, .out = buf, .cap = 256u, .at = at);
#define MMGR_OWNERS(list_)
The program's owners declared as data: the table laid, its count, and each owner's identity defined.
#define MMGR_MINIMUM_SECURITY(name_)
Declares a minimum security cellblock over a pool.
#define LocusCarcerum(prisonsite_,...)
Declares a prison site and the cellblocks built at it.
The embedded_types headers every module builds on.
#define ParsMemoriaeInternae(name_, bytes_)
Declares a pool of bytes in internal memory.
Definition mmgr.h:188
Arguments for the entries that write the buffer itself, with no value to place in it.
char *const out
Arguments for the entries that write an integer.
char *const out
Arguments for the entries that write text into a buffer.
char *const out

What it claims

Each of these is a claim the documentation has to answer for, and each links to the page that does.

  • Nothing allocates. Storage is borrowed, never owned. Why there is no malloc
  • One region, two tiers. Persist grows up and interim grows down, each boundary one atomic word a take bumps by one fetch-add. Region, pool, span, ring
  • Scanning is SWAR, and the carrier is the machine word. The benches measure it. Doing eight bytes at a time
  • The width is a compile-time knob, and every width is built. Five environments, one build. The environment matrix
  • Host-buildable and host-testable. The only headers it reaches for are stddef, stdint and stdatomic. The test suite

Where to go next

If you want to Read
Build it and run something Start here
Understand how the pieces fit How it works
Find the module you need The modules
Look up a knob, a name or a number Reference
Know what is tested and analyzed Correctness
Contribute, or read the license Project

A note on the names

The modules carry Latin category names: locus_carcerum is the region and its pools, spatium is a span, verbum_scrutor is the SWAR scanner. Every one of them is decoded in The Latin, decoded.

The reason is that libc and <string.h> are everywhere, and these are not libc functions. They reach the same conclusion for the same input, across every domain, and share nothing else. How they reach it is entirely different.

The backend is machine-width parallel word processing throughout. Bitmath is extensive and SIMD within a register is used wherever it applies. That gives speed far above the traditional embedded string and memory routines in far less space. A name that collided with libc's would suggest a drop-in replacement of the implementation as well as the result, and it is not one.

That difference is also why the testing is what it is. Every translation unit is fuzzed with bit strobing, bit waves, clock stretching and other abuse the internals will never meet in service. On top of that: interoperability against libc, newlib and GNU; correctness; binary size; and resource allocation lifetime cycles.

The call-site idiom follows from that. Each module exposes a dispatch table named for a short stem, and every entry takes one argument: a pointer to that module's config struct. EMBED_CALL builds it as a compound literal. A call reads EMBED_CALL(spat.from, SpatiumCfg, .buf = buf, .cap = n) with the arguments named instead of ordered. Both forms exist and both are documented; the free function is what the table points at. The dispatch table idiom