Build it, run the suite, and write something that uses it.
Build and test
git clone https://github.com/dstroy0/MMgr.git
cd MMgr
cmake -S . -B build -DMMGR_BUILD_TESTS=ON
cmake --build build --parallel
ctest --test-dir build --output-on-failure
A clean run is 265 tests and takes a few seconds. Every suite is built once per compile-time environment, and there are five of them: one cmake invocation builds every width. See The environment matrix.
Three suites are skipped unless you ask for them, and the configure says so instead of dropping them quietly:
-- MMgr: 3 suites NOT built, their capability is off:
-- MMgr: test_memoria_externa (MMGR_ENABLE_EXTRAM=OFF)
-- MMgr: test_memoriam_praetereo (MMGR_ENABLE_DMA=OFF)
-- MMgr: test_memoria_externa_accuracy (MMGR_ENABLE_EXTRAM=OFF)
The smallest useful program
Storage comes from you. This is the whole shape of the library in twenty lines.
#include <stdio.h>
#define PROGRAM_OWNERS(owner_) owner_(report, "report")
int main(void)
{
char *const store = prison.work.persistent_buf_alloc(256, report);
if (store == NULL) {
return 1;
}
const size_t left = prison.work.buf_available();
size_t at = 0;
at = EMBED_CALL(verba.put, VerbaCfg, .out = store, .cap = 256u, .at = at,
.text = "bytes at hand: ");
at = EMBED_CALL(verba.uint, VerbaCfg, .out = store, .cap = 256u, .at = at, .val = (uint32_t)left);
const size_t len = EMBED_CALL(verba.finish, VerbaCfg, .out = store, .cap = 256u, .at = at);
if (len == 0u) {
return 2;
}
printf("%.*s\n", (int)len, store);
return 0;
}
#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.
Three things in that listing are the library's whole personality:
- The 4096 is the only size decision, and it is made at compile time, in the pool declaration. ParsMemoriaeInternae emits the storage and its alignment; LocusCarcerum emits each cellblock's state and the static asserts that the pool is a legal size for one. Neither is a call, and nothing runs at startup.
- persistent_buf_alloc borrows. It allocates nothing from the heap: it hands back part of the cellblock's own storage and moves a boundary, and what it returns dies when that tier unwinds. Its second argument is the CellOwner the cell's bytes are counted to, one of the table MMGR_OWNERS lays, and a take naming NULL or a pointer that is none of the table's is refused. See Who owns what.
- The error check is at the end, not after every write, because a writer with no room returns cap and every later writer does the same, and finish reports it. See How failure is reported.
Linking it
src/ is the include root. A consumer adds one include directory and reaches everything as <module>/<module>.h, or takes the lot through mmgr.h.
add_subdirectory(third_party/MMgr)
target_link_libraries(my_app PRIVATE mmgr_host)
mmgr_host is the aggregate for the host environment. Adding MMgr to a build covers PlatformIO, Arduino, and why there are no install() rules.
Where to go from here
- guide_first_region: persist against interim, and giving a cell back
- concept_architecture: how a region becomes pools, spans and rings
- nav_modules: the module you actually need