MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
memoria_anularis.h
Go to the documentation of this file.
1/* MMgr - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2 * SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Commercial OR LicenseRef-Educational
3 *
4 * Every use falls under AGPL-3.0-or-later unless you hold explicit permission, which is either a
5 * negotiated commercial licensing contract or an educator's license issued to you personally.
6 */
7/**
8 * @file memoria_anularis.h
9 * @brief Single-producer, single-consumer byte ring, a segment view over it, and loculus keepouts.
10 * @author dstroy0 (Douglas Quigg) <dquigg123@gmail.com>
11 * @date 2026-08-29
12 *
13 * @note Nothing of the implementation is written here. mmgr_ring is a run of opaque bytes, and the
14 * state laid into it is declared nowhere a consumer can reach. That lets the layout
15 * change without a consumer recompiling against a different struct.
16 * @note The caller declares the ring and supplies the bytes; every index, mask and span is the ring's.
17 * @note Exactly one producer advances head and exactly one consumer advances tail, and so ordering is
18 * all that is needed and no entry takes a lock or a read-modify-write on those two.
19 * @note No byte of the ring is ever scrubbed, and a drop leaves the loculus record alone. A
20 * restream can run again. Only mmgr_anular_init clears those records.
21 */
22#ifndef MMGR_MEMORIA_ANULARIS_H
23#define MMGR_MEMORIA_ANULARIS_H
24
25#include "mmgr.h"
26
27EMBED_BEGIN_DECLS
28
29/**
30 * @brief A byte region a pinned loculus keeps out: the storage, its extent, and how far a reader has gone.
31 *
32 * @note Carried here and not taken from spatium, and so this module reaches nothing outside config.
33 */
34typedef struct
35{
36 uint8_t *buf; /**< First byte of the region [BORROWS]. */
37 size_t bytes; /**< Bytes at buf. */
38 size_t read_offset; /**< How far a reader has walked it, counted in bytes from buf. */
40
41/**
42 * @brief Size of the ring storage a caller declares, counted in size_t units.
43 *
44 * @note The implementation asserts its state fits inside this. A change there fails a build
45 * instead of overrunning a caller's object.
46 * @note Most of the state is the keepout array, and so raising MMGR_RING_LOCULI grows it and may leave
47 * this short. The assertion below RingState in memoria_anularis.c is the single one that
48 * fails then.
49 * @note A build sets this before including this header, the same way it sets the loculus count.
50 */
51#ifndef MMGR_RING_WORDS
52
53#define MMGR_RING_WORDS 40u
54#endif
55
56/**
57 * @brief Loculi a mask can address, which is one bit per loculus in a machine word.
58 *
59 * @warning A build declaring more loculi than this has no bit left to name them. Widening the two
60 * masks or replacing them with a scan is what raises the ceiling. The assertion below fails
61 * until one of those happens.
62 */
63#define MMGR_RING_LOCULI_MAX EMBED_WORD_BITS
64
65/**
66 * @brief Loculi this build reserves, which a build may set before including this header.
67 *
68 * @note The keepout spans are most of the ring's state. A build with no use for the loculus view
69 * sets this to 0 and can then lower MMGR_RING_WORDS. Setting this alone shrinks the state. The
70 * storage a caller declares follows MMGR_RING_WORDS alone.
71 * @note At 0, mmgr_anular_loculus_ready reports an empty mask, mmgr_anular_loculus_hold refuses every
72 * index, and mmgr_anular_loculus_keepout returns NULL. mmgr_anular_loculus_drop and
73 * mmgr_anular_loculus_mark leave both masks unchanged.
74 */
75#ifndef MMGR_RING_LOCULI
76
77#define MMGR_RING_LOCULI 8u
78#endif
79
80/**
81 * @brief Asserts the loculi a build declares fit in one machine word.
82 *
83 * @note The free and pinned masks are each one embed_word, one bit per loculus. A count past
84 * MMGR_RING_LOCULI_MAX has no bit to sit in. ring_loculus_bit in memoria_anularis.c
85 * builds that bit by shifting, and this bound keeps the shift inside the word.
86 */
87EMBED_STATIC_ASSERT(
88 MMGR_RING_LOCULI <= MMGR_RING_LOCULI_MAX,
89 "the loculus masks are one machine word; widen them or fall back to a scan past MMGR_RING_LOCULI_MAX");
90
91/**
92 * @brief Reports whether count_ is a power of two.
93 *
94 * @param[in] count_ Count to test, either a byte capacity or a segment count.
95 * @return Non-zero when count_ has at most one bit set.
96 * @warning Also reports non-zero for a count_ of 0. mmgr_anular_init tests for 0 on a line of its own.
97 * @warning count_ appears twice in the expansion. An argument with a side effect is evaluated twice.
98 */
99#define MMGR_RING_POW2(count_) (((count_) & ((count_) - 1u)) == 0u)
100
101/**
102 * @brief Wraps a ring offset back into a ring of capacity_ bytes.
103 *
104 * @param[in] index_ Offset to wrap, which may have run past the end of the buffer.
105 * @param[in] capacity_ Ring size in bytes, a power of two.
106 * @return index_ masked into the range 0 through capacity_ minus one.
107 * @warning Correct only because capacity_ is a power of two, which mmgr_anular_init enforces.
108 */
109#define MMGR_RING_WRAP(index_, capacity_) ((index_) & ((capacity_) - 1u))
110
111/**
112 * @brief Storage a caller declares for one ring, whose contents belong to the implementation.
113 *
114 * @note EMBED_ALIGN aligns opaque to size_t.
115 * @note From mmgr_anular_init onward these bytes hold the buffer that call was given, which must
116 * outlive the ring [BORROWS].
117 * @warning The bytes carry no layout a consumer can reach. RingState is defined in
118 * memoria_anularis.c and nowhere else, and so the mmgr_anular_ entries are the
119 * only way into them.
120 */
121typedef struct
122{
123 EMBED_ALIGN(sizeof(size_t)) uint8_t opaque[MMGR_RING_WORDS * sizeof(size_t)]; /**< Private ring state. */
124} mmgr_ring;
125
126/**
127 * @brief Declares a ring's storage and claims the pool it is laid over.
128 *
129 * @param[in] name_ Name the ring is reached by.
130 * @param[in] pool_ Pool the ring covers, as ParsMemoriaeInternae or ParsMemoriaeExternum declared it
131 * [BORROWS].
132 * @note The claim keeps one pool from being dressed twice. A pool already holding a cellblock
133 * and then laid out as a ring carries two sets of records over the same bytes, each written
134 * without regard to the other, and nothing at run time reports it.
135 * @note A declaration, because MMGR_PARS_CLAIMED_ONCE emits an enumerator and an enumerator inside a
136 * function body is block scoped. Put at the mmgr_anular_init call it would shadow the
137 * cellblock's and never collide.
138 * @note Emits the storage and nothing in it. mmgr_anular_init lays the state into those bytes and
139 * still has to run, since the sizes it is handed can be refused.
140 * @warning The pool is named here and named again at mmgr_anular_init. Nothing holds those two to the
141 * same pool. A ring declared over one pool and initialized over another compiles.
142 */
143#define MemoriaAnularis(name_, pool_) \
144 MMGR_PARS_CLAIMED_ONCE(pool_); \
145 static mmgr_ring name_
146
147/**
148 * @brief Arguments for every mmgr_anular_ entry, each reading only the members it needs.
149 *
150 * @note Every entry but mmgr_anular_loculus_next reads ring. mmgr_anular_init reads buf, capacity,
151 * segment_count and segment_bytes on top of it.
152 * @note Members left unset are zero, and the entries that ignore them never read them.
153 */
154typedef struct
155{
156 mmgr_ring *const ring; /**< Ring to act on [BORROWS]. */
157 uint8_t *const buf; /**< Ring bytes, taken by mmgr_anular_init [BORROWS]. */
158 const size_t capacity; /**< Bytes at buf, non-zero, a power of two where segment_bytes is unset. */
159 const size_t segment_count; /**< Segments to divide the ring into, a power of two no larger than capacity. */
160 const size_t segment_bytes; /**< Bytes per segment where set, else capacity divided by segment_count. */
161 uint8_t *const dst; /**< Destination for read, read_byte and peek [BORROWS]. */
162 const uint8_t *const src; /**< Bytes put writes, or the region loculus_hold records [BORROWS]. */
163 const size_t bytes; /**< Byte count the call moves or records. */
164 const size_t offset; /**< Offset ahead of the tail that peek starts at. */
165 const size_t index; /**< Loculus or segment the call acts on. */
166 const embed_word mask; /**< Mask loculus_next picks the lowest set bit of. */
167 size_t *const out_index; /**< Segment index seg_next and seg_front write into [BORROWS]. */
169
170/**
171 * @brief Type of the anularis dispatch table.
172 *
173 * @note EMBED_TABLE_LAYOUT asserts the twenty members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets, with
174 * nothing else.
175 * @note The twenty are mmgr_anular_init and three layers over the same bytes, and that call lays all
176 * three down. Seven members follow it for the byte ring, six for the segment view, and six for
177 * the loculi.
178 */
179typedef struct
180{
181 embed_bool (*init)(const AnularisCfg *args); /**< Lays a fresh ring into the caller's storage. */
182 size_t (*available)(const AnularisCfg *args); /**< Bytes waiting to be read. */
183 size_t (*vacant)(const AnularisCfg *args); /**< Bytes still free to write. */
184 embed_bool (*read_byte)(const AnularisCfg *args); /**< Takes one byte and advances the tail. */
185 size_t (*read)(const AnularisCfg *args); /**< Takes up to bytes and advances the tail once. */
186 void (*peek)(const AnularisCfg *args); /**< Copies bytes out without advancing the tail. */
187 void (*consume)(const AnularisCfg *args); /**< Advances the tail past bytes. */
188 embed_bool (*put)(const AnularisCfg *args); /**< Writes a whole span, or refuses it entire. */
189 size_t (*seg_inflight)(const AnularisCfg *args); /**< Segments filled and not yet released. */
190 embed_bool (*seg_next)(const AnularisCfg *args); /**< Index of the segment the producer fills next. */
191 void (*seg_publish)(const AnularisCfg *args); /**< Makes the filled segment visible to the consumer. */
192 embed_bool (*seg_front)(const AnularisCfg *args); /**< Index of the segment the consumer takes next. */
193 void (*seg_release)(const AnularisCfg *args); /**< Frees the front segment. */
194 uint8_t *(*seg_at)(const AnularisCfg *args); /**< The contiguous span of one segment. */
195 embed_word (*loculus_ready)(const AnularisCfg *args); /**< Loculi that are free and not pinned. */
196 embed_iword (*loculus_next)(const AnularisCfg *args); /**< Lowest set bit of a mask, or -1. */
197 embed_bool (*loculus_hold)(const AnularisCfg *args); /**< Takes a loculus and records its keepout. */
198 const mmgr_ring_span *(*loculus_keepout)(const AnularisCfg *args); /**< The region a pinned loculus keeps out. */
199 void (*loculus_drop)(const AnularisCfg *args); /**< Gives a loculus back, leaving its bytes alone. */
200 void (*loculus_mark)(const AnularisCfg *args); /**< Marks a loculus free. */
202EMBED_TABLE_LAYOUT(MemoriaAnularisNs, init, available, vacant, read_byte, read, peek, consume, put, seg_inflight,
203 seg_next, seg_publish, seg_front, seg_release, seg_at, loculus_ready, loculus_next, loculus_hold,
204 loculus_keepout, loculus_drop, loculus_mark);
205
206/**
207 * @brief Lays a fresh ring into args->ring, over the bytes at args->buf.
208 *
209 * @param[in,out] args Storage, bytes, capacity and segment count [BORROWS].
210 * @return EMBED_TRUE when the ring is ready, EMBED_FALSE when a size was rejected.
211 * @note Refuses a capacity or a segment_count that is 0 or not a power of two, and a segment_count
212 * above capacity.
213 * @note Given args->segment_bytes, the capacity may be any size that holds segment_count segments of it,
214 * and the ring has segments and loculi and no byte stream: the vacant bytes read 0, a put is refused
215 * and a read takes nothing. The segments' counters wrap by segment_count, and a segment's place is its
216 * index times its size, and so no size needs to be a power of two.
217 * @note Marks every loculus free and none pinned, and clears the recorded keepouts.
218 * @warning args->ring and args->buf are both the caller's, and args->buf is kept by the ring [BORROWS].
219 * @warning Neither may be null, and nothing holds them there outside a MMGR_DEBUG_CHECKS build. A null
220 * ring is written through as soon as the sizes pass, and a null buf is taken and not noticed
221 * until the first move.
222 * @warning args->buf must carry the alignment the caller's own accesses need. This call does not
223 * align it.
224 */
225embed_bool mmgr_anular_init(const AnularisCfg *args);
226
227/**
228 * @brief Returns the bytes waiting to be read.
229 *
230 * @param[in] args Ring to inspect [BORROWS].
231 * @return Distance from the tail to the head, wrapped into the ring.
232 * @warning The answer is only a snapshot. A concurrent producer may add more before the caller acts
233 * on it.
234 */
236
237/**
238 * @brief Returns the bytes still free to write.
239 *
240 * @param[in] args Ring to inspect [BORROWS].
241 * @return The capacity mmgr_anular_init was given, less one, less the readable bytes.
242 * @note One byte is reserved always, as keeps a full ring apart from an empty one.
243 * @warning The answer is only a snapshot. A concurrent consumer may free more before the caller acts
244 * on it.
245 */
246size_t mmgr_anular_vacant(const AnularisCfg *args);
247
248/**
249 * @brief Takes one byte into args->dst and advances the tail past it.
250 *
251 * @param[in,out] args Ring and the destination byte [BORROWS].
252 * @return EMBED_TRUE when a byte was taken, EMBED_FALSE when the ring was empty.
253 * @note Writes through args->dst only when it returns EMBED_TRUE.
254 * @note One byte per call, and so the cost is the call. A caller drawing a run of bytes
255 * wants mmgr_anular_read, which moves them a word at a time under one tail store.
256 * @warning args->dst must be writable for one byte.
257 */
258embed_bool mmgr_anular_read_byte(const AnularisCfg *args);
259
260/**
261 * @brief Takes up to args->bytes into args->dst and advances the tail once at the end.
262 *
263 * @param[in,out] args Ring, destination and the most to take [BORROWS].
264 * @return Bytes actually taken, which is 0 when the ring was empty.
265 * @note Moves the bytes in at most two runs, and so the wrap costs one extra move and not one per byte.
266 * @note Reads the tail once and publishes it once and not per byte.
267 * @warning args->dst must be writable for args->bytes.
268 */
269size_t mmgr_anular_read(const AnularisCfg *args);
270
271/**
272 * @brief Copies args->bytes starting args->offset ahead of the tail into args->dst, leaving the tail
273 * alone.
274 *
275 * @param[in,out] args Ring, destination, byte count and starting offset [BORROWS].
276 * @note Moves the bytes in at most two runs, the same way mmgr_anular_read does.
277 * @warning Copies args->bytes regardless of whether that many have arrived. Read mmgr_anular_available first.
278 * @warning An args->bytes above the ring's capacity is clamped there. Two runs cover one lap at most.
279 * @warning args->dst must be writable for args->bytes.
280 */
282
283/**
284 * @brief Advances the tail past args->bytes.
285 *
286 * @param[in,out] args Ring and the byte count to drop [BORROWS].
287 * @warning Advances regardless of whether that many have arrived. Read mmgr_anular_available first.
288 */
290
291/**
292 * @brief Writes args->bytes of args->src into the ring, or refuses the whole span.
293 *
294 * @param[in,out] args Ring, the bytes to write and their count [BORROWS].
295 * @return EMBED_TRUE when the span was written, EMBED_FALSE when it would not fit.
296 * @note Checks the whole span against the vacant bytes first. A partial write never happens.
297 * @note Advances a local head across the wrap and publishes it once, and so no half span is ever visible.
298 * @warning args->src must be readable for args->bytes.
299 */
300embed_bool mmgr_anular_put(const AnularisCfg *args);
301
302/**
303 * @brief Returns the segments filled and not yet released.
304 *
305 * @param[in] args Ring to inspect [BORROWS].
306 * @return The distance between the claim and release counters.
307 * @warning The two counters are read one after the other, and so the answer is only a snapshot. A publish
308 * or a release may land between the reads, or after both.
309 */
311
312/**
313 * @brief Reports the index of the segment the producer fills next.
314 *
315 * @param[in,out] args Ring, and where to write the index [BORROWS].
316 * @return EMBED_TRUE with the index in args->out_index, EMBED_FALSE when every segment is
317 * in flight.
318 * @note Writes through args->out_index only when it returns EMBED_TRUE.
319 * @note Publishing is separate. A half-filled segment is never visible to the consumer.
320 * @note A EMBED_FALSE can go stale the moment the consumer releases a segment. A EMBED_TRUE cannot,
321 * since releases only make room.
322 * @warning args->out_index must be writable.
323 */
324embed_bool mmgr_anular_seg_next(const AnularisCfg *args);
325
326/**
327 * @brief Makes the filled segment visible to the consumer.
328 *
329 * @param[in,out] args Ring to advance [BORROWS].
330 * @note Only the producer calls this. The counter is read and written as two steps, which needs no
331 * atomicity between them only because one side owns it.
332 * @warning Advances regardless of whether a segment was filled. A publish with no matching
333 * mmgr_anular_seg_next puts more in flight than the ring holds, and that call then refuses
334 * until releases catch up.
335 */
337
338/**
339 * @brief Reports the index of the segment the consumer takes next.
340 *
341 * @param[in,out] args Ring, and where to write the index [BORROWS].
342 * @return EMBED_TRUE with the index in args->out_index, EMBED_FALSE when none is in flight.
343 * @note Writes through args->out_index only when it returns EMBED_TRUE.
344 * @note A EMBED_FALSE can go stale the moment the producer publishes a segment. A EMBED_TRUE cannot,
345 * since publishes only add.
346 * @warning args->out_index must be writable.
347 */
348embed_bool mmgr_anular_seg_front(const AnularisCfg *args);
349
350/**
351 * @brief Frees the front segment.
352 *
353 * @param[in,out] args Ring to advance [BORROWS].
354 * @note Segments release in the order they were claimed.
355 * @note Only the consumer calls this. The counter is read and written as two steps, which needs no
356 * atomicity between them only because one side owns it.
357 * @warning Advances regardless of whether a segment was in flight. A release past what has been published
358 * wraps the in-flight count and leaves mmgr_anular_seg_front handing out a segment that was
359 * never filled.
360 */
362
363/**
364 * @brief Returns the contiguous span of segment args->index.
365 *
366 * @param[in] args Ring and the segment index [BORROWS].
367 * @return Its first byte inside the ring buffer [BORROWS].
368 * @note The span runs for the segment size mmgr_anular_init was given, or else for the capacity divided
369 * by the segment_count it was given.
370 * @warning args->index is not checked against the segment count, and no assertion covers it. An index
371 * at or past that count returns a pointer outside the ring's bytes.
372 */
373uint8_t *mmgr_anular_seg_at(const AnularisCfg *args);
374
375/**
376 * @brief Returns the loculi that are free and not pinned.
377 *
378 * @param[in] args Ring to inspect [BORROWS].
379 * @return The free mask with the pinned ones cleared, bounded to MMGR_RING_LOCULI.
380 * @note A loculus is takeable only when it is free and not pinned, and that leaves reuse safe.
381 * @warning The answer is only a snapshot. The two masks are read one after the other, and a hold or a
382 * drop may land before the caller acts on it. mmgr_anular_loculus_hold settles that race,
383 * refusing a loculus another caller took first.
384 */
386
387/**
388 * @brief Returns the index of the lowest set bit of args->mask.
389 *
390 * @param[in] args The mask to pick from [BORROWS].
391 * @return The index, or -1 when args->mask is empty.
392 * @note Counts and not scans, and branches on nothing but the empty mask.
393 * @note Only args->mask is read. This is the single one entry that never reaches the ring.
394 * @warning The mask is taken as given. A bit at or above MMGR_RING_LOCULI comes back as its index,
395 * and that index names no loculus, and so mmgr_anular_loculus_hold refuses it.
396 */
397embed_iword mmgr_anular_loculus_next(const AnularisCfg *args);
398
399/**
400 * @brief Takes loculus args->index and records the args->bytes at args->src that it keeps out.
401 *
402 * @param[in,out] args Ring, the loculus, and the region to record [BORROWS].
403 * @return EMBED_TRUE when this caller took it, EMBED_FALSE when args->index names none or
404 * another caller already holds it.
405 * @note The recorded region stays valid until mmgr_anular_loculus_drop. A reader walks it in place.
406 * @warning An out-of-range args->index names nothing, and so it reads as pinned and is never handed out.
407 * @warning args->src is kept by the ring and handed back by mmgr_anular_loculus_keepout, and so it must
408 * outlive the hold [BORROWS].
409 */
410embed_bool mmgr_anular_loculus_hold(const AnularisCfg *args);
411
412/**
413 * @brief Returns the region loculus args->index is keeping out.
414 *
415 * @param[in] args Ring and the loculus [BORROWS].
416 * @return The recorded span, or NULL when args->index is out of range [BORROWS].
417 * @note Handed back const. A reader walks it without moving the ring's own record.
418 * @warning The const covers the span and not the bytes it names. buf is reachable as a writable
419 * pointer, though the region reached mmgr_anular_loculus_hold as const.
420 * @warning The record comes back for any loculus in range, pinned or not. A drop leaves it standing, and so
421 * afterwards it still names the last region that loculus recorded [BORROWS].
422 */
424
425/**
426 * @brief Gives loculus args->index back.
427 *
428 * @param[in,out] args Ring and the loculus [BORROWS].
429 * @note Leaves the recorded span and the bytes alone. A restream can run again.
430 * @note An out-of-range args->index names no bit and clears nothing, and dropping a loculus that is
431 * not pinned does nothing.
432 */
434
435/**
436 * @brief Marks loculus args->index free.
437 *
438 * @param[in,out] args Ring and the loculus [BORROWS].
439 * @note Sets the free bit. The pinned bit is mmgr_anular_loculus_drop's to clear, and a loculus is
440 * takeable only when it is free and not pinned.
441 * @note mmgr_anular_init sets the free bit for every loculus and no entry clears it, and so on a ring that
442 * call has laid down this changes nothing.
443 * @note An out-of-range args->index names no bit and sets nothing.
444 */
446
447/**
448 * @brief Dispatch table instance named anularis, whose members are the mmgr_anular_ entries.
449 */
450EMBED_TABLE_STORAGE MemoriaAnularisNs anularis EMBED_UNUSED = {
451 .init = mmgr_anular_init,
452 .available = mmgr_anular_available,
453 .vacant = mmgr_anular_vacant,
454 .read_byte = mmgr_anular_read_byte,
455 .read = mmgr_anular_read,
456 .peek = mmgr_anular_peek,
457 .consume = mmgr_anular_consume,
458 .put = mmgr_anular_put,
459 .seg_inflight = mmgr_anular_seg_inflight,
460 .seg_next = mmgr_anular_seg_next,
461 .seg_publish = mmgr_anular_seg_publish,
462 .seg_front = mmgr_anular_seg_front,
463 .seg_release = mmgr_anular_seg_release,
464 .seg_at = mmgr_anular_seg_at,
465 .loculus_ready = mmgr_anular_loculus_ready,
466 .loculus_next = mmgr_anular_loculus_next,
467 .loculus_hold = mmgr_anular_loculus_hold,
468 .loculus_keepout = mmgr_anular_loculus_keepout,
469 .loculus_drop = mmgr_anular_loculus_drop,
470 .loculus_mark = mmgr_anular_loculus_mark,
471};
472
473EMBED_END_DECLS
474
475#endif
void mmgr_anular_consume(const AnularisCfg *args)
Advances the tail past args->bytes.
void mmgr_anular_loculus_drop(const AnularisCfg *args)
Gives loculus args->index back.
void mmgr_anular_peek(const AnularisCfg *args)
Copies args->bytes starting args->offset ahead of the tail into args->dst, leaving the tail alone.
embed_bool mmgr_anular_put(const AnularisCfg *args)
Writes args->bytes of args->src into the ring, or refuses the whole span.
void mmgr_anular_seg_release(const AnularisCfg *args)
Frees the front segment.
size_t mmgr_anular_read(const AnularisCfg *args)
Takes up to args->bytes into args->dst and advances the tail once at the end.
embed_bool mmgr_anular_init(const AnularisCfg *args)
Lays a fresh ring into args->ring, over the bytes at args->buf.
#define MMGR_RING_WORDS
Size of the ring storage a caller declares, counted in size_t units.
void mmgr_anular_seg_publish(const AnularisCfg *args)
Makes the filled segment visible to the consumer.
size_t mmgr_anular_seg_inflight(const AnularisCfg *args)
Returns the segments filled and not yet released.
const mmgr_ring_span * mmgr_anular_loculus_keepout(const AnularisCfg *args)
Returns the region loculus args->index is keeping out.
void mmgr_anular_loculus_mark(const AnularisCfg *args)
Marks loculus args->index free.
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_vacant(const AnularisCfg *args)
Returns the bytes still free to write.
size_t mmgr_anular_available(const AnularisCfg *args)
Returns the bytes waiting to be read.
embed_word mmgr_anular_loculus_ready(const AnularisCfg *args)
Returns the loculi that are free and not pinned.
static const MemoriaAnularisNs anularis
Dispatch table instance named anularis, whose members are the mmgr_anular_ entries.
embed_iword mmgr_anular_loculus_next(const AnularisCfg *args)
Returns the index of the lowest set bit of args->mask.
embed_bool mmgr_anular_seg_front(const AnularisCfg *args)
Reports the index of the segment the consumer takes next.
embed_bool mmgr_anular_seg_next(const AnularisCfg *args)
Reports the index of the segment the producer fills next.
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.
uint8_t * mmgr_anular_seg_at(const AnularisCfg *args)
Returns the contiguous span of segment args->index.
The embedded_types headers every module builds on.
Arguments for every mmgr_anular_ entry, each reading only the members it needs.
const size_t bytes
size_t *const out_index
const uint8_t *const src
mmgr_ring *const ring
const size_t segment_bytes
const size_t offset
const embed_word mask
const size_t index
const size_t segment_count
uint8_t *const buf
const size_t capacity
uint8_t *const dst
Type of the anularis dispatch table.
embed_bool(*) loculus_hold(const AnularisCfg *args)
embed_bool(*) put(const AnularisCfg *args)
embed_bool(*) seg_next(const AnularisCfg *args)
void(*) loculus_drop(const AnularisCfg *args)
void(*) seg_publish(const AnularisCfg *args)
embed_bool(*) read_byte(const AnularisCfg *args)
size_t(*) seg_inflight(const AnularisCfg *args)
size_t(*) read(const AnularisCfg *args)
embed_word(*) loculus_ready(const AnularisCfg *args)
size_t(*) vacant(const AnularisCfg *args)
size_t(*) available(const AnularisCfg *args)
void(*) seg_release(const AnularisCfg *args)
void(*) consume(const AnularisCfg *args)
void(*) peek(const AnularisCfg *args)
embed_bool(*) seg_front(const AnularisCfg *args)
void(*) loculus_mark(const AnularisCfg *args)
embed_bool(*) init(const AnularisCfg *args)
embed_iword(*) loculus_next(const AnularisCfg *args)
Single-producer, single-consumer byte ring, a segment view over it, and loculus keepouts.
Storage a caller declares for one ring, whose contents belong to the implementation.
uint8_t opaque[MMGR_RING_WORDS *sizeof(size_t)]