MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
LiveArena.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux {
6
7namespace internal {
8
9 /**
10 * @brief Maximum number of objects that can be registered in the live arena.
11 */
12 inline constexpr std::size_t LIVE_ARENA_MAX_ENTRIES = 256;
13
14 /**
15 * @brief Maximum length of a live arena key, including null terminator.
16 */
17 inline constexpr std::size_t LIVE_ARENA_KEY_MAX = 64;
18
19 /**
20 * @brief Default capacity of the live arena in bytes.
21 */
22 inline constexpr std::size_t LIVE_ARENA_CAPACITY = static_cast<const std::size_t>(16 * 1024 * 1024);
23
24 // =========================================================================
25
26 /**
27 * @brief Single directory entry mapping a string key to a location within the arena.
28 *
29 * offset == UINT32_MAX indicates an externally owned object whose pointer
30 * is stored in g_live_arena_shared_ptrs at the same index.
31 */
34 uint32_t offset;
35 uint32_t size;
37 };
38
39 /**
40 * @brief POD header block placed at the start of the arena storage.
41 *
42 * The directory array lives here. All offsets in LiveArenaEntry are
43 * relative to the first byte of object storage that follows this header.
44 */
49
50 // =========================================================================
51
52 /**
53 * @brief Raw backing storage for the live arena with external linkage.
54 *
55 * Exported so the JIT can locate it via DynamicLibrarySearchGenerator.
56 * The first sizeof(LiveArenaHeader) bytes are the directory.
57 * Object storage begins immediately after.
58 */
59 alignas(std::max_align_t) extern MAYAFLUX_API unsigned char g_live_arena_storage[LIVE_ARENA_CAPACITY];
60
61 /**
62 * @brief Byte offset of the bump pointer from the start of the object region.
63 */
64 extern MAYAFLUX_API uint32_t g_live_arena_bump;
65
66 /**
67 * @brief Shared ownership table for objects registered via expose_live.
68 *
69 * Stores shared_ptr<void> so live_cast can reconstruct a typed shared_ptr<T>
70 * via static_pointer_cast. Null for bump-allocated entries.
71 */
72 extern MAYAFLUX_API std::shared_ptr<void> g_live_arena_shared_ptrs[LIVE_ARENA_MAX_ENTRIES];
73
74 // =========================================================================
75
76 /**
77 * @brief Returns a pointer to the header block at the start of arena storage.
78 */
80 {
81 return reinterpret_cast<LiveArenaHeader*>(g_live_arena_storage);
82 }
83
84 /**
85 * @brief Returns a pointer to the object region (immediately after the header).
86 */
87 inline unsigned char* live_arena_object_region() noexcept
88 {
90 }
91
92 /**
93 * @brief Aligns @p value up to the next multiple of @p align.
94 */
95 inline uint32_t align_up(uint32_t value, uint32_t align) noexcept
96 {
97 return (value + align - 1U) & ~(align - 1U);
98 }
99
100 /**
101 * @brief Locates an existing directory entry by key.
102 * @return Pointer to the matching entry, or nullptr if not found.
103 */
104 inline LiveArenaEntry* live_arena_find(const char* key) noexcept
105 {
106 auto* hdr = live_arena_header();
107 for (uint32_t i = 0; i < hdr->entry_count; ++i) {
108 if (hdr->entries[i].occupied
109 && std::strncmp(hdr->entries[i].key, key, LIVE_ARENA_KEY_MAX) == 0) {
110 return &hdr->entries[i];
111 }
112 }
113 return nullptr;
114 }
115
116 /**
117 * @brief Allocates @p size bytes from the arena with @p alignment.
118 *
119 * Writes a directory entry under @p key. Returns nullptr if the arena
120 * is full, the key is already registered, or capacity is exceeded.
121 */
122 inline void* live_arena_alloc(const char* key, uint32_t size, uint32_t alignment) noexcept
123 {
124 auto* hdr = live_arena_header();
125
126 if (hdr->entry_count >= LIVE_ARENA_MAX_ENTRIES) {
127 return nullptr;
128 }
129
130 if (live_arena_find(key)) {
131 return nullptr;
132 }
133
134 const auto object_region_capacity = static_cast<uint32_t>(LIVE_ARENA_CAPACITY - sizeof(LiveArenaHeader));
135
136 const uint32_t aligned_bump = align_up(g_live_arena_bump, alignment);
137
138 if (aligned_bump + size > object_region_capacity) {
139 return nullptr;
140 }
141
142 const uint32_t idx = hdr->entry_count;
143 auto& entry = hdr->entries[idx];
144 std::strncpy(entry.key, key, LIVE_ARENA_KEY_MAX - 1);
145 entry.key[LIVE_ARENA_KEY_MAX - 1] = '\0';
146 entry.offset = aligned_bump;
147 entry.size = size;
148 entry.occupied = true;
149
150 g_live_arena_bump = aligned_bump + size;
151 ++hdr->entry_count;
152
153 return live_arena_object_region() + aligned_bump;
154 }
155
156 /**
157 * @brief Registers an externally owned shared_ptr under @p key.
158 *
159 * Stores shared_ptr<void> in g_live_arena_shared_ptrs at the entry index.
160 * offset is set to UINT32_MAX to distinguish from bump-allocated entries.
161 *
162 * @return true on success, false if the key is already registered or
163 * the directory is full.
164 */
165 inline bool live_arena_expose(const char* key, std::shared_ptr<void> shared) noexcept
166 {
167 auto* hdr = live_arena_header();
168
169 if (hdr->entry_count >= LIVE_ARENA_MAX_ENTRIES) {
170 return false;
171 }
172
173 if (live_arena_find(key)) {
174 return false;
175 }
176
177 const uint32_t idx = hdr->entry_count;
178 auto& entry = hdr->entries[idx];
179 std::strncpy(entry.key, key, LIVE_ARENA_KEY_MAX - 1);
180 entry.key[LIVE_ARENA_KEY_MAX - 1] = '\0';
181 entry.offset = UINT32_MAX;
182 entry.size = UINT32_MAX;
183 entry.occupied = true;
184
185 g_live_arena_shared_ptrs[idx] = std::move(shared);
186
187 ++hdr->entry_count;
188 return true;
189 }
190
191 /**
192 * @brief Extracts the unqualified type name from a compiler-generated function signature.
193 *
194 * Parses __PRETTY_FUNCTION__ to extract the final component of a fully qualified
195 * type name. Used by MF_LIVE_EXPOSE_AUTO to derive stable per-type key prefixes
196 * without requiring the caller to supply a string literal.
197 *
198 * @tparam T Type to name.
199 * @return string_view into static storage valid for the process lifetime.
200 */
201 template <typename T>
202 constexpr std::string_view live_type_name() noexcept
203 {
204 return ::MayaFlux::Reflect::short_type_name<T>();
205 }
206
207 /**
208 * @brief Formats a live arena key as "TypeName_N" into @p buf.
209 *
210 * @param buf Destination buffer of at least LIVE_ARENA_KEY_MAX bytes.
211 * @param name Unqualified type name, typically from live_type_name<T>().
212 * @param count Per-type monotonic counter value.
213 */
214 inline void live_format_key(char* buf, std::string_view name, uint32_t count) noexcept
215 {
216 std::snprintf(buf, LIVE_ARENA_KEY_MAX, "%.*s_%u",
217 static_cast<int>(name.size()), name.data(), count);
218 }
219
220} // namespace MayaFlux::internal
221
222/**
223 * @brief Exposes @p ptr to the live arena under a user-supplied key when MAYAFLUX_LIVE is defined.
224 *
225 * Expands to a no-op when MAYAFLUX_LIVE is not defined.
226 */
227#ifdef MAYAFLUX_LIVE
228#define MF_LIVE_EXPOSE(key, ptr) ::MayaFlux::expose_live((key), (ptr))
229#else
230#define MF_LIVE_EXPOSE(key, ptr) ((void)0)
231#endif
232
233/**
234 * @brief Auto-expose variant that deduces the key prefix from the shared_ptr element type.
235 *
236 * Generates keys of the form "TypeName_N" where N is a per-type monotonic counter.
237 * The static counter is local to each template instantiation so each type maintains
238 * its own independent sequence: Sine_0, Sine_1, Phasor_0, Phasor_1, etc.
239 *
240 * Use when no explicit name is available at the call site (e.g. read_* methods).
241 *
242 * @param ptr shared_ptr whose element_type drives both the key prefix and the counter.
243 */
244#ifdef MAYAFLUX_LIVE
245#define MF_LIVE_EXPOSE_AUTO(ptr) \
246 do { \
247 using _MfT = typename decltype(ptr)::element_type; \
248 static std::atomic<uint32_t> s_live_counter { 0 }; \
249 char _mf_key[::MayaFlux::internal::LIVE_ARENA_KEY_MAX]; \
250 ::MayaFlux::internal::live_format_key( \
251 _mf_key, \
252 ::MayaFlux::internal::live_type_name<_MfT>(), \
253 s_live_counter.fetch_add(1)); \
254 ::MayaFlux::expose_live(_mf_key, (ptr)); \
255 } while (0)
256#else
257#define MF_LIVE_EXPOSE_AUTO(ptr) ((void)0)
258#endif
259
260/**
261 * @brief Auto-expose variant with an explicit name prefix and per-name counter.
262 *
263 * Generates keys of the form "name_N" where N is a monotonic counter local to
264 * the call site's template instantiation. Use inside Creator macro expansions
265 * where the method name is available via the stringified macro parameter.
266 *
267 * @param name String literal key prefix, typically #method_name.
268 * @param ptr shared_ptr to expose.
269 */
270#ifdef MAYAFLUX_LIVE
271#define MF_LIVE_EXPOSE_NAMED(name, ptr) \
272 do { \
273 static std::atomic<uint32_t> s_live_counter { 0 }; \
274 char _mf_key[::MayaFlux::internal::LIVE_ARENA_KEY_MAX]; \
275 ::MayaFlux::internal::live_format_key( \
276 _mf_key, \
277 std::string_view { (name) }, \
278 s_live_counter.fetch_add(1)); \
279 ::MayaFlux::expose_live(_mf_key, (ptr)); \
280 } while (0)
281#else
282#define MF_LIVE_EXPOSE_NAMED(name, ptr) ((void)0)
283#endif
284// =============================================================================
285
286/**
287 * @brief Constructs a T in-place inside the live arena and registers it under @p key.
288 *
289 * The returned shared_ptr is stable for the process lifetime and participates
290 * in normal shared_ptr ref-counting. The arena owns the underlying storage;
291 * the shared_ptr uses a no-op deleter so the arena remains the sole allocator.
292 *
293 * Reachable from JIT'd code immediately via live_cast<T>(key).
294 *
295 * @tparam T Type to construct.
296 * @param key Null-terminated string used to locate the object from the JIT.
297 * Must be unique within the arena.
298 * @param args Arguments forwarded to T's constructor.
299 * @return shared_ptr to the constructed object, or nullptr if the arena is
300 * full or @p key is already registered.
301 */
302template <typename T, typename... Args>
303std::shared_ptr<T> make_live(const char* key, Args&&... args)
304{
305 void* mem = internal::live_arena_alloc(
306 key,
307 static_cast<uint32_t>(sizeof(T)),
308 static_cast<uint32_t>(alignof(T)));
309
310 if (!mem) {
311 return nullptr;
312 }
313
314 T* obj = ::new (mem) T(std::forward<Args>(args)...);
315 return std::shared_ptr<T>(obj, [](T*) { });
316}
317
318/**
319 * @brief Exposes an existing shared_ptr-managed object to the live arena under @p key.
320 *
321 * The shared_ptr ref-count is incremented; the object remains alive at least
322 * as long as the arena entry exists. Reachable from JIT'd code via live_cast<T>(key).
323 *
324 * @tparam T Type of the object.
325 * @param key Null-terminated string used to locate the object from the JIT.
326 * @param obj shared_ptr to the existing object.
327 * @return true on success, false if @p key is already registered or the
328 * directory is full.
329 */
330template <typename T>
331bool expose_live(const char* key, const std::shared_ptr<T>& obj) noexcept
332{
333 return internal::live_arena_expose(key, std::static_pointer_cast<void>(obj));
334}
335
336/**
337 * @brief Exposes an existing raw pointer to the live arena under @p key.
338 *
339 * Lifetime management remains entirely the caller's responsibility.
340 * The arena stores a no-op shared_ptr so live_cast returns a valid
341 * shared_ptr without affecting the object's actual lifetime.
342 *
343 * @tparam T Type of the object.
344 * @param key Null-terminated string used to locate the object from the JIT.
345 * @param ptr Raw pointer to the existing object.
346 * @return true on success, false if @p key is already registered or the
347 * directory is full.
348 */
349template <typename T>
350bool expose_live(const char* key, T* ptr) noexcept
351{
352 return internal::live_arena_expose(key, std::shared_ptr<void>(ptr, [](void*) { }));
353}
354
355/**
356 * @brief Internal implementation for live_cast. Resolves through the export
357 * table so JIT'd code can reach arena globals across DLL boundaries.
358 *
359 * @param key Null-terminated arena key.
360 * @return Raw pointer to the registered object, or nullptr.
361 */
362MAYAFLUX_API void* live_cast_impl(const char* key) noexcept;
363
364/**
365 * @brief Retrieves a live-arena object by key as a shared_ptr<T>.
366 *
367 * Delegates the directory walk to live_cast_impl so the call resolves
368 * through the DLL export table on all platforms. The returned shared_ptr
369 * uses a no-op deleter; lifetime is managed by the arena or the original
370 * expose_live caller.
371 *
372 * @tparam T Expected type of the registered object.
373 * @param key Null-terminated string used at make_live or expose_live time.
374 * @return shared_ptr<T>, or nullptr if the key is not found.
375 */
376template <typename T>
377std::shared_ptr<T> live_cast(const char* key) noexcept
378{
379 void* ptr = live_cast_impl(key);
380 if (!ptr) {
381 return nullptr;
382 }
383 return std::shared_ptr<T>(static_cast<T*>(ptr), [](T*) { });
384}
385
386/**
387 * @brief Dumps all live arena entries to stderr.
388 *
389 * Prints each occupied entry's index, key, and offset, followed by
390 * the total entry count and bump pointer value. Intended for diagnostic
391 * use during development; output goes to stderr unconditionally.
392 */
393MAYAFLUX_API void live_arena_dump() noexcept;
394
395} // namespace MayaFlux
size_t count
const uint8_t * ptr
constexpr std::size_t LIVE_ARENA_KEY_MAX
Maximum length of a live arena key, including null terminator.
Definition LiveArena.hpp:17
uint32_t align_up(uint32_t value, uint32_t align) noexcept
Aligns value up to the next multiple of align.
Definition LiveArena.hpp:95
constexpr std::size_t LIVE_ARENA_MAX_ENTRIES
Maximum number of objects that can be registered in the live arena.
Definition LiveArena.hpp:12
constexpr std::string_view live_type_name() noexcept
Extracts the unqualified type name from a compiler-generated function signature.
uint32_t g_live_arena_bump
Byte offset of the bump pointer from the start of the object region.
Definition LiveArena.cpp:8
constexpr std::size_t LIVE_ARENA_CAPACITY
Default capacity of the live arena in bytes.
Definition LiveArena.hpp:22
void live_format_key(char *buf, std::string_view name, uint32_t count) noexcept
Formats a live arena key as "TypeName_N" into buf.
LiveArenaHeader * live_arena_header() noexcept
Returns a pointer to the header block at the start of arena storage.
Definition LiveArena.hpp:79
unsigned char * live_arena_object_region() noexcept
Returns a pointer to the object region (immediately after the header).
Definition LiveArena.hpp:87
bool live_arena_expose(const char *key, std::shared_ptr< void > shared) noexcept
Registers an externally owned shared_ptr under key.
LiveArenaEntry * live_arena_find(const char *key) noexcept
Locates an existing directory entry by key.
void * live_arena_alloc(const char *key, uint32_t size, uint32_t alignment) noexcept
Allocates size bytes from the arena with alignment.
unsigned char g_live_arena_storage[LIVE_ARENA_CAPACITY]
Raw backing storage for the live arena with external linkage.
Definition LiveArena.cpp:7
std::shared_ptr< void > g_live_arena_shared_ptrs[LIVE_ARENA_MAX_ENTRIES]
Shared ownership table for objects registered via expose_live.
Definition LiveArena.cpp:9
MAYAFLUX_API void * live_cast_impl(const char *key) noexcept
Internal implementation for live_cast.
Definition LiveArena.cpp:13
bool expose_live(const char *key, const std::shared_ptr< T > &obj) noexcept
Exposes an existing shared_ptr-managed object to the live arena under key.
MAYAFLUX_API void live_arena_dump() noexcept
Dumps all live arena entries to stderr.
Definition LiveArena.cpp:32
std::shared_ptr< T > live_cast(const char *key) noexcept
Retrieves a live-arena object by key as a shared_ptr<T>.
std::shared_ptr< T > make_live(const char *key, Args &&... args)
Constructs a T in-place inside the live arena and registers it under key.
Main namespace for the Maya Flux audio engine.
Definition Runtime.cpp:12
char key[LIVE_ARENA_KEY_MAX]
Definition LiveArena.hpp:33
Single directory entry mapping a string key to a location within the arena.
Definition LiveArena.hpp:32
LiveArenaEntry entries[LIVE_ARENA_MAX_ENTRIES]
Definition LiveArena.hpp:47
POD header block placed at the start of the arena storage.
Definition LiveArena.hpp:45