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

Single-producer, single-consumer byte ring, a segment view over it, and loculus keepouts. More...

#include "mmgr.h"

Go to the source code of this file.

Data Structures

struct  mmgr_ring_span
 Single-producer, single-consumer byte ring, a segment view over it, and loculus keepouts. More...
struct  mmgr_ring
 Storage a caller declares for one ring, whose contents belong to the implementation. More...
struct  AnularisCfg
 Arguments for every mmgr_anular_ entry, each reading only the members it needs. More...
struct  MemoriaAnularisNs
 Type of the anularis dispatch table. More...

Macros

#define MMGR_RING_WORDS
 Size of the ring storage a caller declares, counted in size_t units.
#define MMGR_RING_LOCULI_MAX
 Loculi a mask can address, which is one bit per loculus in a machine word.
#define MMGR_RING_LOCULI
 Loculi this build reserves, which a build may set before including this header.
#define MMGR_RING_POW2(count_)
 Asserts the loculi a build declares fit in one machine word.
#define MMGR_RING_WRAP(index_, capacity_)
 Wraps a ring offset back into a ring of capacity_ bytes.
#define MemoriaAnularis(name_, pool_)
 Declares a ring's storage and claims the pool it is laid over.

Functions

embed_bool mmgr_anular_init (const AnularisCfg *args)
 Lays a fresh ring into args->ring, over the bytes at args->buf.
size_t mmgr_anular_available (const AnularisCfg *args)
 Returns the bytes waiting to be read.
size_t mmgr_anular_vacant (const AnularisCfg *args)
 Returns the bytes still free to write.
embed_bool mmgr_anular_read_byte (const AnularisCfg *args)
 Takes one byte into args->dst and advances the tail past it.
size_t mmgr_anular_read (const AnularisCfg *args)
 Takes up to args->bytes into args->dst and advances the tail once at the end.
void mmgr_anular_peek (const AnularisCfg *args)
 Copies args->bytes starting args->offset ahead of the tail into args->dst, leaving the tail alone.
void mmgr_anular_consume (const AnularisCfg *args)
 Advances the tail past args->bytes.
embed_bool mmgr_anular_put (const AnularisCfg *args)
 Writes args->bytes of args->src into the ring, or refuses the whole span.
size_t mmgr_anular_seg_inflight (const AnularisCfg *args)
 Returns the segments filled and not yet released.
embed_bool mmgr_anular_seg_next (const AnularisCfg *args)
 Reports the index of the segment the producer fills next.
void mmgr_anular_seg_publish (const AnularisCfg *args)
 Makes the filled segment visible to the consumer.
embed_bool mmgr_anular_seg_front (const AnularisCfg *args)
 Reports the index of the segment the consumer takes next.
void mmgr_anular_seg_release (const AnularisCfg *args)
 Frees the front segment.
uint8_t * mmgr_anular_seg_at (const AnularisCfg *args)
 Returns the contiguous span of segment args->index.
embed_word mmgr_anular_loculus_ready (const AnularisCfg *args)
 Returns the loculi that are free and not pinned.
embed_iword mmgr_anular_loculus_next (const AnularisCfg *args)
 Returns the index of the lowest set bit of args->mask.
embed_bool mmgr_anular_loculus_hold (const AnularisCfg *args)
 Takes loculus args->index and records the args->bytes at args->src that it keeps out.
const mmgr_ring_span * mmgr_anular_loculus_keepout (const AnularisCfg *args)
 Returns the region loculus args->index is keeping out.
void mmgr_anular_loculus_drop (const AnularisCfg *args)
 Gives loculus args->index back.
void mmgr_anular_loculus_mark (const AnularisCfg *args)
 Marks loculus args->index free.

Variables

static const MemoriaAnularisNs anularis
 Dispatch table instance named anularis, whose members are the mmgr_anular_ entries.

Detailed Description

Single-producer, single-consumer byte ring, a segment view over it, and loculus keepouts.

Author
dstroy0 (Douglas Quigg) dquig.nosp@m.g123.nosp@m.@gmai.nosp@m.l.co.nosp@m.m
Date
2026-08-29
Note
Nothing of the implementation is written here. mmgr_ring is a run of opaque bytes, and the state laid into it is declared nowhere a consumer can reach. That lets the layout change without a consumer recompiling against a different struct.
The caller declares the ring and supplies the bytes; every index, mask and span is the ring's.
Exactly one producer advances head and exactly one consumer advances tail, and so ordering is all that is needed and no entry takes a lock or a read-modify-write on those two.
No byte of the ring is ever scrubbed, and a drop leaves the loculus record alone. A restream can run again. Only mmgr_anular_init clears those records.

Definition in file memoria_anularis.h.

Macro Definition Documentation

◆ MMGR_RING_WORDS

#define MMGR_RING_WORDS

Size of the ring storage a caller declares, counted in size_t units.

Note
The implementation asserts its state fits inside this. A change there fails a build instead of overrunning a caller's object.
Most of the state is the keepout array, and so raising MMGR_RING_LOCULI grows it and may leave this short. The assertion below RingState in memoria_anularis.c is the single one that fails then.
A build sets this before including this header, the same way it sets the loculus count.

Definition at line 53 of file memoria_anularis.h.

Referenced by mmgr_ring::MMGR_ALIGN().

◆ MMGR_RING_LOCULI_MAX

#define MMGR_RING_LOCULI_MAX

Loculi a mask can address, which is one bit per loculus in a machine word.

Warning
A build declaring more loculi than this has no bit left to name them. Widening the two masks or replacing them with a scan is what raises the ceiling. The assertion below fails until one of those happens.

Definition at line 63 of file memoria_anularis.h.

◆ MMGR_RING_LOCULI

#define MMGR_RING_LOCULI

Loculi this build reserves, which a build may set before including this header.

Note
The keepout spans are most of the ring's state. A build with no use for the loculus view sets this to 0 and can then lower MMGR_RING_WORDS. Setting this alone shrinks the state. The storage a caller declares follows MMGR_RING_WORDS alone.
At 0, mmgr_anular_loculus_ready reports an empty mask, mmgr_anular_loculus_hold refuses every index, and mmgr_anular_loculus_keepout returns NULL. mmgr_anular_loculus_drop and mmgr_anular_loculus_mark leave both masks unchanged.

Definition at line 77 of file memoria_anularis.h.

◆ MMGR_RING_POW2

#define MMGR_RING_POW2 ( count_)

Asserts the loculi a build declares fit in one machine word.

Note
The free and pinned masks are each one embed_word, one bit per loculus. A count past MMGR_RING_LOCULI_MAX has no bit to sit in. ring_loculus_bit in memoria_anularis.c builds that bit by shifting, and this bound keeps the shift inside the word.

Reports whether count_ is a power of two.

Parameters
[in]count_Count to test, either a byte capacity or a segment count.
Returns
Non-zero when count_ has at most one bit set.
Warning
Also reports non-zero for a count_ of 0. mmgr_anular_init tests for 0 on a line of its own.
count_ appears twice in the expansion. An argument with a side effect is evaluated twice.

Definition at line 99 of file memoria_anularis.h.

◆ MMGR_RING_WRAP

#define MMGR_RING_WRAP ( index_,
capacity_ )

Wraps a ring offset back into a ring of capacity_ bytes.

Parameters
[in]index_Offset to wrap, which may have run past the end of the buffer.
[in]capacity_Ring size in bytes, a power of two.
Returns
index_ masked into the range 0 through capacity_ minus one.
Warning
Correct only because capacity_ is a power of two, which mmgr_anular_init enforces.

Definition at line 109 of file memoria_anularis.h.

◆ MemoriaAnularis

#define MemoriaAnularis ( name_,
pool_ )

Declares a ring's storage and claims the pool it is laid over.

Parameters
[in]name_Name the ring is reached by.
[in]pool_Pool the ring covers, as ParsMemoriaeInternae or ParsMemoriaeExternum declared it [BORROWS].
Note
The claim keeps one pool from being dressed twice. A pool already holding a cellblock and then laid out as a ring carries two sets of records over the same bytes, each written without regard to the other, and nothing at run time reports it.
A declaration, because MMGR_PARS_CLAIMED_ONCE emits an enumerator and an enumerator inside a function body is block scoped. Put at the mmgr_anular_init call it would shadow the cellblock's and never collide.
Emits the storage and nothing in it. mmgr_anular_init lays the state into those bytes and still has to run, since the sizes it is handed can be refused.
Warning
The pool is named here and named again at mmgr_anular_init. Nothing holds those two to the same pool. A ring declared over one pool and initialized over another compiles.

Definition at line 143 of file memoria_anularis.h.

Function Documentation

◆ mmgr_anular_init()

embed_bool mmgr_anular_init ( const AnularisCfg * args)

Lays a fresh ring into args->ring, over the bytes at args->buf.

Parameters
[in,out]argsStorage, bytes, capacity and segment count [BORROWS].
Returns
EMBED_TRUE when the ring is ready, EMBED_FALSE when a size was rejected.
Note
Refuses a capacity or a segment_count that is 0 or not a power of two, and a segment_count above capacity.
Given args->segment_bytes, the capacity may be any size that holds segment_count segments of it, and the ring has segments and loculi and no byte stream: the vacant bytes read 0, a put is refused and a read takes nothing. The segments' counters wrap by segment_count, and a segment's place is its index times its size, and so no size needs to be a power of two.
Marks every loculus free and none pinned, and clears the recorded keepouts.
Warning
args->ring and args->buf are both the caller's, and args->buf is kept by the ring [BORROWS].
Neither may be null, and nothing holds them there outside a MMGR_DEBUG_CHECKS build. A null ring is written through as soon as the sizes pass, and a null buf is taken and not noticed until the first move.
args->buf must carry the alignment the caller's own accesses need. This call does not align it.

References mmgr_anular_init().

Referenced by mmgr_anular_init().

◆ mmgr_anular_available()

size_t mmgr_anular_available ( const AnularisCfg * args)

Returns the bytes waiting to be read.

Parameters
[in]argsRing to inspect [BORROWS].
Returns
Distance from the tail to the head, wrapped into the ring.
Warning
The answer is only a snapshot. A concurrent producer may add more before the caller acts on it.

References mmgr_anular_available().

Referenced by mmgr_anular_available().

◆ mmgr_anular_vacant()

size_t mmgr_anular_vacant ( const AnularisCfg * args)

Returns the bytes still free to write.

Parameters
[in]argsRing to inspect [BORROWS].
Returns
The capacity mmgr_anular_init was given, less one, less the readable bytes.
Note
One byte is reserved always, as keeps a full ring apart from an empty one.
Warning
The answer is only a snapshot. A concurrent consumer may free more before the caller acts on it.

References mmgr_anular_vacant().

Referenced by mmgr_anular_vacant().

◆ mmgr_anular_read_byte()

embed_bool mmgr_anular_read_byte ( const AnularisCfg * args)

Takes one byte into args->dst and advances the tail past it.

Parameters
[in,out]argsRing and the destination byte [BORROWS].
Returns
EMBED_TRUE when a byte was taken, EMBED_FALSE when the ring was empty.
Note
Writes through args->dst only when it returns EMBED_TRUE.
One byte per call, and so the cost is the call. A caller drawing a run of bytes wants mmgr_anular_read, which moves them a word at a time under one tail store.
Warning
args->dst must be writable for one byte.

References mmgr_anular_read_byte().

Referenced by mmgr_anular_read_byte().

◆ mmgr_anular_read()

size_t mmgr_anular_read ( const AnularisCfg * args)

Takes up to args->bytes into args->dst and advances the tail once at the end.

Parameters
[in,out]argsRing, destination and the most to take [BORROWS].
Returns
Bytes actually taken, which is 0 when the ring was empty.
Note
Moves the bytes in at most two runs, and so the wrap costs one extra move and not one per byte.
Reads the tail once and publishes it once and not per byte.
Warning
args->dst must be writable for args->bytes.

References mmgr_anular_read().

Referenced by mmgr_anular_read().

◆ mmgr_anular_peek()

void mmgr_anular_peek ( const AnularisCfg * args)

Copies args->bytes starting args->offset ahead of the tail into args->dst, leaving the tail alone.

Parameters
[in,out]argsRing, destination, byte count and starting offset [BORROWS].
Note
Moves the bytes in at most two runs, the same way mmgr_anular_read does.
Warning
Copies args->bytes regardless of whether that many have arrived. Read mmgr_anular_available first.
An args->bytes above the ring's capacity is clamped there. Two runs cover one lap at most.
args->dst must be writable for args->bytes.

References mmgr_anular_peek().

Referenced by mmgr_anular_peek().

◆ mmgr_anular_consume()

void mmgr_anular_consume ( const AnularisCfg * args)

Advances the tail past args->bytes.

Parameters
[in,out]argsRing and the byte count to drop [BORROWS].
Warning
Advances regardless of whether that many have arrived. Read mmgr_anular_available first.

References mmgr_anular_consume().

Referenced by mmgr_anular_consume().

◆ mmgr_anular_put()

embed_bool mmgr_anular_put ( const AnularisCfg * args)

Writes args->bytes of args->src into the ring, or refuses the whole span.

Parameters
[in,out]argsRing, the bytes to write and their count [BORROWS].
Returns
EMBED_TRUE when the span was written, EMBED_FALSE when it would not fit.
Note
Checks the whole span against the vacant bytes first. A partial write never happens.
Advances a local head across the wrap and publishes it once, and so no half span is ever visible.
Warning
args->src must be readable for args->bytes.

References mmgr_anular_put().

Referenced by mmgr_anular_put().

◆ mmgr_anular_seg_inflight()

size_t mmgr_anular_seg_inflight ( const AnularisCfg * args)

Returns the segments filled and not yet released.

Parameters
[in]argsRing to inspect [BORROWS].
Returns
The distance between the claim and release counters.
Warning
The two counters are read one after the other, and so the answer is only a snapshot. A publish or a release may land between the reads, or after both.

References mmgr_anular_seg_inflight().

Referenced by mmgr_anular_seg_inflight().

◆ mmgr_anular_seg_next()

embed_bool mmgr_anular_seg_next ( const AnularisCfg * args)

Reports the index of the segment the producer fills next.

Parameters
[in,out]argsRing, and where to write the index [BORROWS].
Returns
EMBED_TRUE with the index in args->out_index, EMBED_FALSE when every segment is in flight.
Note
Writes through args->out_index only when it returns EMBED_TRUE.
Publishing is separate. A half-filled segment is never visible to the consumer.
A EMBED_FALSE can go stale the moment the consumer releases a segment. A EMBED_TRUE cannot, since releases only make room.
Warning
args->out_index must be writable.

References mmgr_anular_seg_next().

Referenced by mmgr_anular_seg_next().

◆ mmgr_anular_seg_publish()

void mmgr_anular_seg_publish ( const AnularisCfg * args)

Makes the filled segment visible to the consumer.

Parameters
[in,out]argsRing to advance [BORROWS].
Note
Only the producer calls this. The counter is read and written as two steps, which needs no atomicity between them only because one side owns it.
Warning
Advances regardless of whether a segment was filled. A publish with no matching mmgr_anular_seg_next puts more in flight than the ring holds, and that call then refuses until releases catch up.

References mmgr_anular_seg_publish().

Referenced by mmgr_anular_seg_publish().

◆ mmgr_anular_seg_front()

embed_bool mmgr_anular_seg_front ( const AnularisCfg * args)

Reports the index of the segment the consumer takes next.

Parameters
[in,out]argsRing, and where to write the index [BORROWS].
Returns
EMBED_TRUE with the index in args->out_index, EMBED_FALSE when none is in flight.
Note
Writes through args->out_index only when it returns EMBED_TRUE.
A EMBED_FALSE can go stale the moment the producer publishes a segment. A EMBED_TRUE cannot, since publishes only add.
Warning
args->out_index must be writable.

References mmgr_anular_seg_front().

Referenced by mmgr_anular_seg_front().

◆ mmgr_anular_seg_release()

void mmgr_anular_seg_release ( const AnularisCfg * args)

Frees the front segment.

Parameters
[in,out]argsRing to advance [BORROWS].
Note
Segments release in the order they were claimed.
Only the consumer calls this. The counter is read and written as two steps, which needs no atomicity between them only because one side owns it.
Warning
Advances regardless of whether a segment was in flight. A release past what has been published wraps the in-flight count and leaves mmgr_anular_seg_front handing out a segment that was never filled.

References mmgr_anular_seg_release().

Referenced by mmgr_anular_seg_release().

◆ mmgr_anular_seg_at()

uint8_t * mmgr_anular_seg_at ( const AnularisCfg * args)

Returns the contiguous span of segment args->index.

Parameters
[in]argsRing and the segment index [BORROWS].
Returns
Its first byte inside the ring buffer [BORROWS].
Note
The span runs for the segment size mmgr_anular_init was given, or else for the capacity divided by the segment_count it was given.
Warning
args->index is not checked against the segment count, and no assertion covers it. An index at or past that count returns a pointer outside the ring's bytes.

References mmgr_anular_seg_at().

Referenced by mmgr_anular_seg_at().

◆ mmgr_anular_loculus_ready()

embed_word mmgr_anular_loculus_ready ( const AnularisCfg * args)

Returns the loculi that are free and not pinned.

Parameters
[in]argsRing to inspect [BORROWS].
Returns
The free mask with the pinned ones cleared, bounded to MMGR_RING_LOCULI.
Note
A loculus is takeable only when it is free and not pinned, and that leaves reuse safe.
Warning
The answer is only a snapshot. The two masks are read one after the other, and a hold or a drop may land before the caller acts on it. mmgr_anular_loculus_hold settles that race, refusing a loculus another caller took first.

References mmgr_anular_loculus_ready().

Referenced by mmgr_anular_loculus_ready().

◆ mmgr_anular_loculus_next()

embed_iword mmgr_anular_loculus_next ( const AnularisCfg * args)

Returns the index of the lowest set bit of args->mask.

Parameters
[in]argsThe mask to pick from [BORROWS].
Returns
The index, or -1 when args->mask is empty.
Note
Counts and not scans, and branches on nothing but the empty mask.
Only args->mask is read. This is the single one entry that never reaches the ring.
Warning
The mask is taken as given. A bit at or above MMGR_RING_LOCULI comes back as its index, and that index names no loculus, and so mmgr_anular_loculus_hold refuses it.

References mmgr_anular_loculus_next().

Referenced by mmgr_anular_loculus_next().

◆ mmgr_anular_loculus_hold()

embed_bool mmgr_anular_loculus_hold ( const AnularisCfg * args)

Takes loculus args->index and records the args->bytes at args->src that it keeps out.

Parameters
[in,out]argsRing, the loculus, and the region to record [BORROWS].
Returns
EMBED_TRUE when this caller took it, EMBED_FALSE when args->index names none or another caller already holds it.
Note
The recorded region stays valid until mmgr_anular_loculus_drop. A reader walks it in place.
Warning
An out-of-range args->index names nothing, and so it reads as pinned and is never handed out.
args->src is kept by the ring and handed back by mmgr_anular_loculus_keepout, and so it must outlive the hold [BORROWS].

References mmgr_anular_loculus_hold().

Referenced by mmgr_anular_loculus_hold().

◆ mmgr_anular_loculus_keepout()

const mmgr_ring_span * mmgr_anular_loculus_keepout ( const AnularisCfg * args)

Returns the region loculus args->index is keeping out.

Parameters
[in]argsRing and the loculus [BORROWS].
Returns
The recorded span, or NULL when args->index is out of range [BORROWS].
Note
Handed back const. A reader walks it without moving the ring's own record.
Warning
The const covers the span and not the bytes it names. buf is reachable as a writable pointer, though the region reached mmgr_anular_loculus_hold as const.
The record comes back for any loculus in range, pinned or not. A drop leaves it standing, and so afterwards it still names the last region that loculus recorded [BORROWS].

References mmgr_anular_loculus_keepout().

Referenced by mmgr_anular_loculus_keepout().

◆ mmgr_anular_loculus_drop()

void mmgr_anular_loculus_drop ( const AnularisCfg * args)

Gives loculus args->index back.

Parameters
[in,out]argsRing and the loculus [BORROWS].
Note
Leaves the recorded span and the bytes alone. A restream can run again.
An out-of-range args->index names no bit and clears nothing, and dropping a loculus that is not pinned does nothing.

References mmgr_anular_loculus_drop().

Referenced by mmgr_anular_loculus_drop().

◆ mmgr_anular_loculus_mark()

void mmgr_anular_loculus_mark ( const AnularisCfg * args)

Marks loculus args->index free.

Parameters
[in,out]argsRing and the loculus [BORROWS].
Note
Sets the free bit. The pinned bit is mmgr_anular_loculus_drop's to clear, and a loculus is takeable only when it is free and not pinned.
mmgr_anular_init sets the free bit for every loculus and no entry clears it, and so on a ring that call has laid down this changes nothing.
An out-of-range args->index names no bit and sets nothing.

References mmgr_anular_loculus_mark().

Referenced by mmgr_anular_loculus_mark().

Variable Documentation

◆ anularis

const MemoriaAnularisNs anularis
static

Dispatch table instance named anularis, whose members are the mmgr_anular_ entries.

Definition at line 450 of file memoria_anularis.h.