MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
confinium_exclusivum_infinitas.h
1/**
2 * @brief Single-producer, single-consumer byte ring, a segment view over it, and loculus keepouts.
3 *
4 * @note Nothing of the implementation is written here: mmgr_ring is a run of opaque bytes, and the
5 * state laid into it is declared nowhere a consumer can reach.
6 * @note The caller declares the ring and supplies the bytes; every index, mask and span is the ring's.
7 * @note Exactly one producer advances head and exactly one consumer advances tail, and so ordering is
8 * all that is needed and no entry takes a lock or a read-modify-write on those two.
9 * @note Nothing is ever zeroed: a loculus keeps its bytes after a drop, which lets a restream run again.
10 */
11#ifndef MMGR_CONFINIUM_EXCLUSIVUM_INFINITAS_H
12#define MMGR_CONFINIUM_EXCLUSIVUM_INFINITAS_H
13
14#include "config/mmgr_config.h"
15
16MMGR_INCIPE_DECLS
17
18/**
19 * @brief A byte region a pinned loculus keeps out: the storage, its extent, and how far a reader has gone.
20 *
21 * @note Carried here and not taken from spatium, and so this module reaches nothing outside config.
22 */
23typedef struct
24{
25 uint8_t *buf; /**< First byte of the region [BORROWS]. */
26 size_t cap; /**< Bytes at buf. */
27 size_t pos; /**< How far a reader has walked it. */
29
30/**
31 * @brief Size of the ring storage a caller declares, counted in size_t units.
32 *
33 * @note The implementation asserts its state fits inside this. A change there fails a build
34 * instead of overrunning a caller's object.
35 * @note Most of it is the keepout array, and so raising MMGR_RING_LOCULI may require raising this too;
36 * the assertion names it when that happens. A build sets this the same way it sets the
37 * loculus count, before including this header.
38 */
39#ifndef MMGR_RING_WORDS
40
41#define MMGR_RING_WORDS 40u
42#endif
43
44/**
45 * @brief Loculi a mask can address, which is one bit per loculus in a machine word.
46 *
47 * @warning A pool wider than this needs a wider mask or a scan; the assertion below names which.
48 */
49#define MMGR_RING_LOCULI_MAX MMGR_WORD_BITS
50
51/**
52 * @brief Loculi this build reserves, which a build may set before including this header.
53 *
54 * @note The keepout spans are most of the ring's storage. A build with no use for the loculus
55 * view sets this to 0 and gets that space back. Every loculus entry then reports empty or
56 * refuses.
57 */
58#ifndef MMGR_RING_LOCULI
59
60#define MMGR_RING_LOCULI 8u
61#endif
62
63MMGR_STATIC_ASSERT(
64 MMGR_RING_LOCULI <= MMGR_RING_LOCULI_MAX,
65 "the loculus masks are one machine word; widen them or fall back to a scan past MMGR_RING_LOCULI_MAX");
66
67/**
68 * @brief Reports whether cap is a power of two.
69 *
70 * @param[in] cap Value to test.
71 * @return Non-zero when cap has at most one bit set.
72 * @warning Also reports true for 0; mmgr_infin_init rejects 0 separately.
73 */
74#define MMGR_RING_POW2(cap) (((cap) & ((cap) - 1u)) == 0u)
75
76/**
77 * @brief Wraps an index into a ring of cap bytes.
78 *
79 * @param[in] i Index to wrap.
80 * @param[in] cap Ring size.
81 * @return The index masked into range.
82 * @warning Correct only because cap is a power of two, which mmgr_infin_init enforces.
83 */
84#define MMGR_RING_WRAP(i, cap) ((i) & ((cap) - 1u))
85
86/**
87 * @brief Storage a caller declares for one ring, whose contents belong to the implementation.
88 *
89 * @note MMGR_ALIGN aligns opaque to size_t.
90 * @warning The bytes carry no documented layout; the infin calls are the only accessors.
91 */
92typedef struct
93{
94 MMGR_ALIGN(sizeof(size_t)) uint8_t opaque[MMGR_RING_WORDS * sizeof(size_t)]; /**< Private ring state. */
95} mmgr_ring;
96
97/**
98 * @brief Arguments for every infin call; each reads only what it needs.
99 *
100 * @note Every call reads ring; init adds buf, cap and nsegs.
101 * @note Members left unset are zero, and the calls that ignore them never read them.
102 */
103typedef struct
104{
105 mmgr_ring *const ring; /**< Ring to act on [BORROWS]. */
106 uint8_t *const buf; /**< Ring bytes, for init [BORROWS]. */
107 const size_t cap; /**< Bytes in buf; a non-zero power of two. */
108 const size_t nsegs; /**< Segments to divide the ring into; a power of two, at most cap. */
109 uint8_t *const dst; /**< Destination for read, read_byte and peek [BORROWS]. */
110 const uint8_t *const src; /**< Bytes put writes, or the region hold records [BORROWS]. */
111 const size_t bytes; /**< Byte count the call moves or records. */
112 const size_t off; /**< Offset ahead of the tail that peek starts at. */
113 const size_t idx; /**< Loculus or segment the call acts on. */
114 const mmgr_word mask; /**< Mask loculus_next picks the lowest set bit of. */
115 size_t *const out; /**< Set to the segment index a next or front call chose [BORROWS]. */
116} InfinCfg;
117
118/**
119 * @brief Type of the iteratio_infinita dispatch table.
120 *
121 * @note MMGR_NS_LAYOUT asserts the twenty members sit at consecutive MMGR_FP_SIZE offsets, with nothing else.
122 * @note The first eight are the byte ring, the next six the segment view, the last six the loculi.
123 */
124typedef struct
125{
126 mmgr_bool (*init)(const InfinCfg *c); /**< Lays a fresh ring into the caller's storage. */
127 size_t (*available)(const InfinCfg *c); /**< Bytes waiting to be read. */
128 size_t (*vacant)(const InfinCfg *c); /**< Bytes still free to write. */
129 mmgr_bool (*read_byte)(const InfinCfg *c); /**< Takes one byte and advances the tail. */
130 size_t (*read)(const InfinCfg *c); /**< Takes up to bytes and advances the tail once. */
131 void (*peek)(const InfinCfg *c); /**< Copies bytes out without advancing the tail. */
132 void (*consume)(const InfinCfg *c); /**< Advances the tail past bytes. */
133 mmgr_bool (*put)(const InfinCfg *c); /**< Writes a whole span, or refuses it entire. */
134 size_t (*seg_inflight)(const InfinCfg *c); /**< Segments filled and not yet released. */
135 mmgr_bool (*seg_next)(const InfinCfg *c); /**< Index of the segment the producer fills next. */
136 void (*seg_publish)(const InfinCfg *c); /**< Makes the filled segment visible to the consumer. */
137 mmgr_bool (*seg_front)(const InfinCfg *c); /**< Index of the segment the consumer takes next. */
138 void (*seg_release)(const InfinCfg *c); /**< Frees the front segment. */
139 uint8_t *(*seg_at)(const InfinCfg *c); /**< The contiguous span of one segment. */
140 mmgr_word (*loculus_ready)(const InfinCfg *c); /**< Loculi that are free and not pinned. */
141 mmgr_iword (*loculus_next)(const InfinCfg *c); /**< Lowest set bit of a mask, or -1. */
142 mmgr_bool (*loculus_hold)(const InfinCfg *c); /**< Takes a loculus and records its keepout. */
143 const mmgr_ring_span *(*loculus_keepout)(const InfinCfg *c); /**< The region a pinned loculus keeps out. */
144 void (*loculus_drop)(const InfinCfg *c); /**< Gives a loculus back, leaving its bytes alone. */
145 void (*loculus_mark)(const InfinCfg *c); /**< Marks a loculus free. */
147MMGR_NS_LAYOUT(InfinitasNs, init, available, vacant, read_byte, read, peek, consume, put, seg_inflight, seg_next,
148 seg_publish, seg_front, seg_release, seg_at, loculus_ready, loculus_next, loculus_hold, loculus_keepout,
149 loculus_drop, loculus_mark);
150
151/**
152 * @brief Lays a fresh ring into c->ring, over the bytes at c->buf.
153 *
154 * @param[in] c Storage, bytes, capacity and segment count [BORROWS].
155 * @return MMGR_TRUE when the ring is ready, MMGR_FALSE when a size was rejected.
156 * @note Refuses a cap or nsegs that is 0 or not a power of two, and an nsegs above cap.
157 * @note Marks every loculus free and none pinned.
158 * @warning c->ring and c->buf are both the caller's, and c->buf is kept by the ring [BORROWS].
159 * @warning c->buf must carry the alignment the caller's own accesses need; this call does not align it.
160 */
161mmgr_bool mmgr_infin_init(const InfinCfg *c);
162
163/**
164 * @brief Returns the bytes waiting to be read.
165 *
166 * @param[in] c Ring to inspect [BORROWS].
167 * @return Distance from the tail to the head, wrapped into the ring.
168 * @warning Only a snapshot: a concurrent producer may add more before the caller acts on it.
169 */
170size_t mmgr_infin_available(const InfinCfg *c);
171
172/**
173 * @brief Returns the bytes still free to write.
174 *
175 * @param[in] c Ring to inspect [BORROWS].
176 * @return cap minus one, minus the readable bytes.
177 * @note One byte is reserved always, as keeps a full ring apart from an empty one.
178 * @warning Only a snapshot: a concurrent consumer may free more before the caller acts on it.
179 */
180size_t mmgr_infin_vacant(const InfinCfg *c);
181
182/**
183 * @brief Takes one byte into c->dst and advances the tail past it.
184 *
185 * @param[in] c Ring and the destination byte [BORROWS].
186 * @return MMGR_TRUE when a byte was taken, MMGR_FALSE when the ring was empty.
187 * @note Writes through c->dst only when it returns MMGR_TRUE.
188 * @note One byte per call, and so the cost lies in the call; a caller drawing a
189 * run of bytes wants mmgr_infin_read, which moves them a word at a time under one tail store.
190 */
191mmgr_bool mmgr_infin_read_byte(const InfinCfg *c);
192
193/**
194 * @brief Takes up to c->bytes into c->dst and advances the tail once at the end.
195 *
196 * @param[in] c Ring, destination and the most to take [BORROWS].
197 * @return Bytes actually taken, which is 0 when the ring was empty.
198 * @note Moves the bytes in at most two runs, and so the wrap costs one extra move and not one per byte.
199 * @note Reads the tail once and publishes it once and not per byte.
200 * @warning c->dst must be writable for c->bytes.
201 */
202size_t mmgr_infin_read(const InfinCfg *c);
203
204/**
205 * @brief Copies c->bytes starting c->off ahead of the tail into c->dst, leaving the tail alone.
206 *
207 * @param[in] c Ring, destination, byte count and starting offset [BORROWS].
208 * @note Moves the bytes in at most two runs, the same way mmgr_infin_read does.
209 * @warning Copies c->bytes regardless of whether that many have arrived; read mmgr_infin_available first.
210 * @warning A c->bytes above the ring's capacity is clamped there, since one lap is all two runs express.
211 */
212void mmgr_infin_peek(const InfinCfg *c);
213
214/**
215 * @brief Advances the tail past c->bytes.
216 *
217 * @param[in] c Ring and the byte count to drop [BORROWS].
218 * @warning Advances regardless of whether that many have arrived; read mmgr_infin_available first.
219 */
220void mmgr_infin_consume(const InfinCfg *c);
221
222/**
223 * @brief Writes c->bytes of c->src into the ring, or refuses the whole span.
224 *
225 * @param[in] c Ring, the bytes to write and their count [BORROWS].
226 * @return MMGR_TRUE when the span was written, MMGR_FALSE when it would not fit.
227 * @note Checks the whole span against mmgr_infin_vacant first. A partial write never happens.
228 * @note Advances a local head across the wrap and publishes it once, and so no half span is ever visible.
229 * @warning c->src must be readable for c->bytes.
230 */
231mmgr_bool mmgr_infin_put(const InfinCfg *c);
232
233/**
234 * @brief Returns the segments filled and not yet released.
235 *
236 * @param[in] c Ring to inspect [BORROWS].
237 * @return The distance between the claim and release counters.
238 */
239size_t mmgr_infin_seg_inflight(const InfinCfg *c);
240
241/**
242 * @brief Reports the index of the segment the producer fills next.
243 *
244 * @param[in] c Ring, and where to write the index [BORROWS].
245 * @return MMGR_FALSE when every segment is in flight.
246 * @note Publishing is separate. A half-filled segment is never visible to the consumer.
247 */
248mmgr_bool mmgr_infin_seg_next(const InfinCfg *c);
249
250/**
251 * @brief Makes the filled segment visible to the consumer.
252 *
253 * @param[in] c Ring to advance [BORROWS].
254 */
255void mmgr_infin_seg_publish(const InfinCfg *c);
256
257/**
258 * @brief Reports the index of the segment the consumer takes next.
259 *
260 * @param[in] c Ring, and where to write the index [BORROWS].
261 * @return MMGR_FALSE when none is in flight.
262 */
263mmgr_bool mmgr_infin_seg_front(const InfinCfg *c);
264
265/**
266 * @brief Frees the front segment.
267 *
268 * @param[in] c Ring to advance [BORROWS].
269 * @note Segments release in the order they were claimed.
270 */
271void mmgr_infin_seg_release(const InfinCfg *c);
272
273/**
274 * @brief Returns the contiguous span of segment c->idx.
275 *
276 * @param[in] c Ring and the segment index [BORROWS].
277 * @return Its first byte inside the ring buffer [BORROWS].
278 */
279uint8_t *mmgr_infin_seg_at(const InfinCfg *c);
280
281/**
282 * @brief Returns the loculi that are free and not pinned.
283 *
284 * @param[in] c Ring to inspect [BORROWS].
285 * @return The free mask with the pinned ones cleared, bounded to MMGR_RING_LOCULI.
286 * @note A loculus is takeable only when both hold, and that leaves reuse safe.
287 */
288mmgr_word mmgr_infin_loculus_ready(const InfinCfg *c);
289
290/**
291 * @brief Returns the index of the lowest set bit of c->mask.
292 *
293 * @param[in] c The mask to pick from [BORROWS].
294 * @return The index, or -1 when c->mask is empty.
295 * @note Counts and not scans, and branches on nothing but the empty mask.
296 */
297mmgr_iword mmgr_infin_loculus_next(const InfinCfg *c);
298
299/**
300 * @brief Takes loculus c->idx and records the c->bytes at c->src that it keeps out.
301 *
302 * @param[in] c Ring, the loculus, and the region to record [BORROWS].
303 * @return MMGR_TRUE when this caller took it, MMGR_FALSE when it was already pinned.
304 * @note The recorded region stays valid until mmgr_infin_loculus_drop. A reader walks it in place.
305 * @warning An out-of-range c->idx names nothing, and so it reads as pinned and is never handed out.
306 */
307mmgr_bool mmgr_infin_loculus_hold(const InfinCfg *c);
308
309/**
310 * @brief Returns the region loculus c->idx is keeping out.
311 *
312 * @param[in] c Ring and the loculus [BORROWS].
313 * @return The recorded span, or NULL when c->idx is out of range [BORROWS].
314 * @note Handed back const. A reader walks it without moving the ring's own record.
315 */
316const mmgr_ring_span *mmgr_infin_loculus_keepout(const InfinCfg *c);
317
318/**
319 * @brief Gives loculus c->idx back.
320 *
321 * @param[in] c Ring and the loculus [BORROWS].
322 * @note Leaves the recorded span and the bytes alone. A restream can run again.
323 */
324void mmgr_infin_loculus_drop(const InfinCfg *c);
325
326/**
327 * @brief Marks loculus c->idx free.
328 *
329 * @param[in] c Ring and the loculus [BORROWS].
330 */
331void mmgr_infin_loculus_mark(const InfinCfg *c);
332
333MMGR_NS InfinitasNs iteratio_infinita MMGR_UNUSED = {
334 .init = mmgr_infin_init,
335 .available = mmgr_infin_available,
336 .vacant = mmgr_infin_vacant,
337 .read_byte = mmgr_infin_read_byte,
338 .read = mmgr_infin_read,
339 .peek = mmgr_infin_peek,
340 .consume = mmgr_infin_consume,
341 .put = mmgr_infin_put,
342 .seg_inflight = mmgr_infin_seg_inflight,
343 .seg_next = mmgr_infin_seg_next,
344 .seg_publish = mmgr_infin_seg_publish,
345 .seg_front = mmgr_infin_seg_front,
346 .seg_release = mmgr_infin_seg_release,
347 .seg_at = mmgr_infin_seg_at,
348 .loculus_ready = mmgr_infin_loculus_ready,
349 .loculus_next = mmgr_infin_loculus_next,
350 .loculus_hold = mmgr_infin_loculus_hold,
351 .loculus_keepout = mmgr_infin_loculus_keepout,
352 .loculus_drop = mmgr_infin_loculus_drop,
353 .loculus_mark = mmgr_infin_loculus_mark,
354};
355
356MMGR_FINIS_DECLS
357
358#endif
#define MMGR_RING_WORDS
Size of the ring storage a caller declares, counted in size_t units.
Arguments for every infin call; each reads only what it needs.
const uint8_t *const src
Type of the iteratio_infinita dispatch table.
size_t(*) read(const InfinCfg *c)
void(*) seg_publish(const InfinCfg *c)
mmgr_iword(*) loculus_next(const InfinCfg *c)
void(*) seg_release(const InfinCfg *c)
mmgr_bool(*) read_byte(const InfinCfg *c)
size_t(*) seg_inflight(const InfinCfg *c)
mmgr_bool(*) loculus_hold(const InfinCfg *c)
mmgr_bool(*) init(const InfinCfg *c)
mmgr_bool(*) seg_next(const InfinCfg *c)
void(*) consume(const InfinCfg *c)
void(*) loculus_mark(const InfinCfg *c)
mmgr_word(*) loculus_ready(const InfinCfg *c)
size_t(*) available(const InfinCfg *c)
mmgr_bool(*) seg_front(const InfinCfg *c)
mmgr_bool(*) put(const InfinCfg *c)
void(*) loculus_drop(const InfinCfg *c)
void(*) peek(const InfinCfg *c)
size_t(*) vacant(const InfinCfg *c)
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.
MMGR_ALIGN(sizeof(size_t)) uint8_t opaque[MMGR_RING_WORDS *sizeof(size_t)]
uint8_t opaque[MMGR_RING_WORDS *sizeof(size_t)]