libbio
Loading...
Searching...
No Matches
PointerArena.h
Go to the documentation of this file.
1/*
2 * This file is a part of the Biology project by eons LLC.
3 * Biology (aka Develop Biology) is a framework for approaching software
4 * development from a natural sciences perspective.
5 *
6 * Copyright (C) 2026 Séon O'Shannon & eons LLC
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as
10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#pragma once
23
25#include <cstddef>
26#include <stdint.h>
27#include <new>
28#include <cstdio>
29#include <cstdlib>
30//@formatter:off
31#if BIO_CPP_VERSION >= 11
32 #include <utility>
33#endif
34//@formatter:on
35
36namespace bio {
37
38typedef uint32_t Index;
39
40template < typename TYPE, bool MEMCPY_SAFE >
41class Arrangement;
42
43namespace pointer_arena {
44
46
47static const Handle NullHandle = 0;
48static const Handle AddressMask = 0x3fffffffu;
49static const Handle GenerationMask = 0xc0000000u;
50static const uint32_t GenerationShift = 30u;
51static const uint32_t GenerationModulo = 3u;
52static const uint16_t ForeignGeneration = 3u;
53static const ::std::size_t WordShift = 3;
54static const ::std::size_t WordSize = (1u << WordShift);
55static const uint32_t NullPageFreeSlot = 0xffffffffu;
56static const uint16_t AllocationFlagFree = 0x0001u;
57static const uint16_t AllocationFlagSpan = 0x0004u;
58static const uint16_t PageFlagSmall = 0x0001u;
59
76
77inline bool operator==(
78 const MemoryAllocation& lhs,
79 const MemoryAllocation& rhs)
80{
81 return
82 lhs.mOffsetWords == rhs.mOffsetWords &&
83 lhs.mCapacityWords == rhs.mCapacityWords &&
84 lhs.mRequestedBytes == rhs.mRequestedBytes &&
85 lhs.mGeneration == rhs.mGeneration &&
86 lhs.mFlags == rhs.mFlags;
87}
88
113
114inline bool operator==(
115 const MemoryPage& lhs,
116 const MemoryPage& rhs)
117{
118 return
119 lhs.mOffsetWords == rhs.mOffsetWords &&
120 lhs.mPageBytes == rhs.mPageBytes &&
121 lhs.mSlotBytes == rhs.mSlotBytes &&
122 lhs.mFlags == rhs.mFlags &&
123 lhs.mFirstFreeWordOffset == rhs.mFirstFreeWordOffset &&
124 lhs.mFreeSlots == rhs.mFreeSlots &&
125 lhs.mTotalSlots == rhs.mTotalSlots &&
126 lhs.mNextFreshSlot == rhs.mNextFreshSlot &&
127 lhs.mSlotStates == rhs.mSlotStates;
128}
129
142 public ThreadSafe
143{
144 friend void DeleteObjectStorage(
145 void* raw,
146 ::std::size_t bytes);
147 friend void FreeBytes(
148 void* raw,
149 ::std::size_t bytes);
150
151public:
152 static PointerArena& Instance();
153
154 PointerArena();
156
157 void* AllocateBytes(
158 ::std::size_t bytes,
159 ::std::size_t align);
160 void* ReallocateBytes(
161 void* raw,
162 ::std::size_t oldBytes,
163 ::std::size_t newBytes,
164 ::std::size_t align);
166 void* raw,
167 ::std::size_t bytes);
168
169 Handle HandleFor(const void* raw);
170 Handle ArenaHandleFor(const void* raw) const;
171 const void* Resolve(Handle handle) const;
172
173 bool Owns(const void* raw) const;
174 bool IsArenaHandle(Handle handle) const;
175 bool IsForeignHandle(Handle handle) const;
176
177 const void* GetBase() const;
178 ::std::size_t GetCapacityBytes() const;
179 ::std::size_t GetBytesHandedOut() const;
180 ::std::size_t GetForeignCount() const;
181 ::std::size_t GetAllocationRecordCount() const;
182 ::std::size_t GetSmallAllocationCount() const;
183 ::std::size_t GetPageRecordCount() const;
184
185private:
186 unsigned char* mBase;
187 ::std::size_t mCapacityBytes;
188 ::std::size_t mNextByte;
189 bool mReservedWithMmap;
190 // Stable (never-moving) per-word generation table for the checked-compact deref:
191 // one byte per arena word, MAP_NORESERVE-reserved once alongside mBase. Read
192 // lock-free by ResolveFast so racey multi-thread derefs only ever touch
193 // never-moving memory (never the growable mPages/mAllocations, which the deref no
194 // longer reads). Set on alloc, bumped under the arena lock on free.
195 unsigned char* mSlotGenTable;
196 ::std::size_t mSlotGenTableBytes;
197 bool mSlotGenTableMmap;
201 Index* mSmallPageLookup;
202 ::std::size_t mSmallPageLookupCount;
203 ::std::size_t mSmallAllocationCount;
204 struct FreeBlock
205 {
206 unsigned char* mRaw;
207 ::std::size_t mBytes;
208 uint16_t mGeneration;
209 };
210 struct ForeignCacheEntry
211 {
212 const void* mRaw;
213 Handle mHandle;
214 };
215 // Persistent per-address foreign dedup entry (C3). Bounds mForeign to the number of
216 // UNIQUE foreign addresses: a re-import of an address that was evicted from the small
217 // direct-mapped mForeignCache is resolved to its existing handle here instead of
218 // appending a duplicate permanent mForeign entry.
219 struct ForeignMapEntry
220 {
221 const void* mRaw;
222 Handle mHandle;
223 };
224 struct SmallPageCacheEntry
225 {
226 uint16_t mSlotBytes;
227 Index mPageIndex;
228 };
229 // Initial (and growth-base) capacity of the free-block store. The store is now a
230 // growable heap array (mFreeBlocks / mFreeBlockCapacity) so a freed span is NEVER
231 // dropped when full (C4).
232 static const ::std::size_t FreeBlockInitialCapacity = 4096;
233 static const ::std::size_t ForeignCacheCapacity = 4096;
234 static const ::std::size_t ForeignCacheProbeCount = 8;
235 static const ::std::size_t SmallPageCacheCapacity = 64;
236 static const ::std::size_t SmallPageDirectCacheCapacity = 513;
237 // Growable free-block store: pointer + capacity, doubled under the arena lock when
238 // full (C4). ONLY accessed under the arena lock, so growing/moving it is safe.
239 FreeBlock* mFreeBlocks;
240 ForeignCacheEntry mForeignCache[ForeignCacheCapacity];
241 SmallPageCacheEntry mSmallPageCache[SmallPageCacheCapacity];
242 Index mSmallPageDirectCache[SmallPageDirectCacheCapacity];
243 // Persistent open-addressing dedup map (raw address -> foreign handle) for C3.
244 // malloc-backed, arena-lock-only, never read by the deref path.
245 ForeignMapEntry* mForeignMap;
246 ::std::size_t mForeignMapCapacity;
247 ::std::size_t mForeignMapCount;
248 ::std::size_t mFreeBlockCount;
249 ::std::size_t mFreeBlockCapacity;
250 ::std::size_t mForeignCacheVictim;
251 ::std::size_t mSmallPageCacheVictim;
252
253 void EnsureReserved();
254 void* TakeFreeBlock(
255 ::std::size_t bytes,
256 ::std::size_t align,
257 ::std::size_t* capacityBytes,
258 uint16_t* generation);
259 void* AllocateSmallBytes(
260 ::std::size_t bytes,
261 ::std::size_t align);
262 void* AllocateSpanBytes(
263 ::std::size_t bytes,
264 ::std::size_t align);
265 Index CreateSmallPage(
266 ::std::size_t slotBytes,
267 ::std::size_t align);
268 Index FindSmallPage(
269 ::std::size_t slotBytes) const;
270 Index FindCachedSmallPage(
271 ::std::size_t slotBytes) const;
272 void RecordSmallPageCache(
273 ::std::size_t slotBytes,
274 Index pageIndex);
275 bool EnsureSmallPageLookup();
276 void RecordSmallPageLookup(
277 Index pageIndex,
278 ::std::size_t offsetBytes,
279 ::std::size_t pageBytes);
280 Index LookupSmallPageForRaw(
281 const void* raw) const;
282 Index FindPageForRaw(
283 const void* raw) const;
284 Index AddAllocation(
285 ::std::size_t offsetBytes,
286 ::std::size_t capacityBytes,
287 ::std::size_t requestedBytes,
288 uint16_t flags,
289 uint16_t generation);
290 Index FindAllocationByOffset(
291 ::std::size_t offsetBytes) const;
292 Index FindAllocationContainingOffset(
293 ::std::size_t offsetBytes) const;
294 bool RetireAllocation(
295 ::std::size_t offsetBytes,
296 MemoryAllocation* retiredAllocation = NULL);
297 void RecordFreeBlock(
298 void* raw,
299 ::std::size_t bytes,
300 uint16_t generation);
301 bool CacheSmallBytes(
302 void* raw,
303 ::std::size_t bytes);
304 const void* ResolveArena(Handle handle) const;
305 Handle AddForeign(const void* raw);
306 ::std::size_t ForeignCacheIndex(const void* raw) const;
307 Handle FindRecentForeign(const void* raw) const;
308 void RecordRecentForeign(
309 const void* raw,
310 Handle handle);
311 // Persistent foreign dedup map (C3): consulted by AddForeign before appending so a
312 // re-imported address returns its existing handle instead of a duplicate entry.
313 Handle FindForeignInMap(const void* raw) const;
314 void InsertForeignInMap(
315 const void* raw,
316 Handle handle);
317 const void* ResolveForeign(Handle handle) const;
318
320 PointerArena& operator=(const PointerArena&);
321};
322
323void* Allocate(
324 ::std::size_t bytes,
325 ::std::size_t align);
326void* Reallocate(
327 void* raw,
328 ::std::size_t oldBytes,
329 ::std::size_t newBytes,
330 ::std::size_t align);
331void DeleteObjectStorage(void* raw);
333 void* raw,
334 ::std::size_t bytes);
335void FreeBytes(void* raw);
336void FreeBytes(
337 void* raw,
338 ::std::size_t bytes);
342
343Handle HandleFor(const void* raw);
344const void* Resolve(Handle handle);
345
346bool Owns(const void* raw);
347bool IsArenaHandle(Handle handle);
348bool IsForeignHandle(Handle handle);
349const void* ArenaBase();
350::std::size_t ArenaBytesHandedOut();
351::std::size_t ForeignHandleCount();
352::std::size_t ArenaAllocationRecordCount();
353::std::size_t ArenaSmallAllocationCount();
354::std::size_t ArenaPageRecordCount();
355
356//@formatter:off
357#if BIO_COMPACT_POINTER_STORAGE
358extern const unsigned char* gPointerArenaBase;
359#endif
360
364{
365 const Handle offsetField = static_cast< Handle >(wordOffset + 1u);
366 if (!offsetField || offsetField > AddressMask)
367 {
368 return NullHandle;
369 }
370 return
371 (static_cast< Handle >(generation % GenerationModulo) << GenerationShift) |
373}
374
376{
377 return handle & AddressMask;
378}
379
381{
382 return static_cast< uint16_t >((handle & GenerationMask) >> GenerationShift);
383}
384
386{
387 return handle && HandleGeneration(handle) == ForeignGeneration;
388}
389
391{
393 if (!offsetField)
394 {
395 return 0;
396 }
397 return offsetField - 1u;
398}
399
400#if BIO_ARENA_CHECKED
401// Stable per-word generation table, reserved once alongside gPointerArenaBase (see
402// PointerArena.cpp EnsureReserved). It NEVER moves, so ResolveFast reads it lock-free
403// and racey multi-thread derefs never touch the growable mPages/mAllocations.
404extern unsigned char* gSlotGenTable;
405
406// Loud failure for a dereference of a stale/dangling arena handle in a checked
407// build (C1): the handle outlived its slot's free/reuse and the arena liveness +
408// generation check rejected it, so resolving raw would hand back reused memory.
409// Abort instead of silently corrupting. NOT NDEBUG-gated (the check is opt-in, so
410// when on it always fires). Returns to keep ResolveFast's return type well-formed.
411inline const void* OnStaleHandle(Handle handle, const char* file, int line)
412{
413 ::std::fprintf(stderr,
414 "BIO_ARENA_CHECKED: dereference of a stale/dangling arena handle 0x%08lx at %s:%d\n",
415 static_cast< unsigned long >(handle), file, line);
416 ::std::abort();
417 return NULL;
418}
419#endif
420
421#if BIO_COMPACT_POINTER_STORAGE
422inline const void* ResolveFast(Handle handle)
423{
424 if (!handle)
425 {
426 return NULL;
427 }
428 if (IsForeignHandleValue(handle))
429 {
430 return Resolve(handle);
431 }
432 //@formatter:off
433 #if BIO_ARENA_CHECKED
434 // Checked path (C1 + C2): validate the (mod-3) generation via the STABLE per-word
435 // generation table. That table and gPointerArenaBase never move, so this deref
436 // reads ONLY never-moving memory -- it is safe under lock-free racey reads from
437 // many threads while other threads allocate (which grow mPages/mAllocations, no
438 // longer touched here). A generation mismatch means the handle outlived its slot's
439 // free/reuse -> abort rather than resolve to reused memory.
440 {
441 const uint32_t offsetField = HandleOffsetField(handle);
442 if (!offsetField || !gPointerArenaBase || !gSlotGenTable)
443 {
444 return NULL;
445 }
446 const ::std::size_t wordOffset = static_cast< ::std::size_t >(offsetField - 1u);
447 if (gSlotGenTable[wordOffset] != HandleGeneration(handle))
448 {
449 return OnStaleHandle(handle, __FILE__, __LINE__);
450 }
451 return gPointerArenaBase + (wordOffset << WordShift);
452 }
453 #else
454 const uint32_t offsetField = HandleOffsetField(handle);
455 if (!offsetField || !gPointerArenaBase)
456 {
457 return NULL;
458 }
459 return gPointerArenaBase + (static_cast< ::std::size_t >(offsetField - 1u) << WordShift);
460 #endif
461 //@formatter:on
462}
463#endif
464//@formatter:on
465
466template < typename T >
467inline ::std::size_t AlignmentOf()
468{
469 //@formatter:off
470 #if defined(__GNUC__) || defined(__clang__)
471 return __alignof__(T);
472 #else
473 return sizeof(void*);
474 #endif
475 //@formatter:on
476}
477
478template < typename T >
479inline T* New()
480{
481 void* storage = Allocate(sizeof(T), AlignmentOf< T >());
482 return new (storage) T();
483}
484
485template < typename T >
486inline T* NewCopy(const T& value)
487{
488 void* storage = Allocate(sizeof(T), AlignmentOf< T >());
489 return new (storage) T(value);
490}
491
492//@formatter:off
493#if BIO_CPP_VERSION >= 11
494template < typename T, typename... Args >
495inline T* NewWith(Args&&... args)
496{
497 void* storage = Allocate(sizeof(T), AlignmentOf< T >());
498 return new (storage) T(::std::forward< Args >(args)...);
499}
500#else
501template < typename T, typename A1 >
502inline T* NewWith(const A1& a1)
503{
504 void* storage = Allocate(sizeof(T), AlignmentOf< T >());
505 return new (storage) T(a1);
506}
507
508template < typename T, typename A1, typename A2 >
509inline T* NewWith(const A1& a1, A2& a2)
510{
511 void* storage = Allocate(sizeof(T), AlignmentOf< T >());
512 return new (storage) T(a1, a2);
513}
514
515template < typename T, typename A1, typename A2 >
516inline T* NewWith(const A1& a1, const A2& a2)
517{
518 void* storage = Allocate(sizeof(T), AlignmentOf< T >());
519 return new (storage) T(a1, a2);
520}
521#endif
522//@formatter:on
523
524template < typename T >
525inline void Delete(T* raw)
526{
527 if (!raw)
528 {
529 return;
530 }
531 raw->~T();
532 DeleteObjectStorage(raw, sizeof(T));
533}
534
540template < typename T >
542{
543public:
545 :
546 mObject(New< T >())
547 {
548 }
549
550 explicit Object(const T& value)
551 :
552 mObject(NewCopy< T >(value))
553 {
554 }
555
556 //@formatter:off
557 #if BIO_CPP_VERSION >= 11
558 template < typename... Args >
559 explicit Object(Args&&... args)
560 :
561 mObject(NewWith< T >(::std::forward< Args >(args)...))
562 {
563 }
564 #else
565 template < typename A1 >
566 explicit Object(const A1& a1)
567 :
568 mObject(NewWith< T >(a1))
569 {
570 }
571
572 template < typename A1, typename A2 >
573 Object(const A1& a1, A2& a2)
574 :
575 mObject(NewWith< T >(a1, a2))
576 {
577 }
578
579 template < typename A1, typename A2 >
580 Object(const A1& a1, const A2& a2)
581 :
582 mObject(NewWith< T >(a1, a2))
583 {
584 }
585 #endif
586 //@formatter:on
587
589 {
590 if (mObject)
591 {
592 mObject->~T();
593 mObject = NULL;
594 }
595 }
596
598 {
599 return mObject;
600 }
601
602 const T* Get() const
603 {
604 return mObject;
605 }
606
607private:
608 T* mObject;
609
610 Object(const Object&);
611 Object& operator=(const Object&);
612};
613
614} //pointer_arena namespace
615} //bio namespace
616
617//@formatter:off
618#if BIO_COMPACT_POINTER_STORAGE
619 #define BIO_POINTER_ARENA_ALLOCATED(TYPE) \
620 static void* operator new(::std::size_t bytes) \
621 { \
622 void* storage = ::bio::pointer_arena::Allocate( \
623 bytes, \
624 ::bio::pointer_arena::AlignmentOf< TYPE >()); \
625 if (!storage) \
626 { \
627 throw ::std::bad_alloc(); \
628 } \
629 return storage; \
630 } \
631 static void* operator new(::std::size_t, void* raw) \
632 { \
633 return raw; \
634 } \
635 static void operator delete(void* raw) \
636 { \
637 ::bio::pointer_arena::DeleteObjectStorage(raw); \
638 } \
639 static void operator delete(void*, void*) \
640 { \
641 }
642#else
643 #define BIO_POINTER_ARENA_ALLOCATED(TYPE)
644#endif
645//@formatter:on
double value
Definition Potential.cpp:41
Definition Pointer.h:115
Definition ThreadSafe.h:57
Definition PointerArena.h:542
const T * Get() const
Definition PointerArena.h:602
Object(const T &value)
Definition PointerArena.h:550
Object(const A1 &a1)
Definition PointerArena.h:566
Object()
Definition PointerArena.h:544
Object(const A1 &a1, const A2 &a2)
Definition PointerArena.h:580
Object(const A1 &a1, A2 &a2)
Definition PointerArena.h:573
T * Get()
Definition PointerArena.h:597
~Object()
Definition PointerArena.h:588
Definition PointerArena.h:143
::std::size_t GetSmallAllocationCount() const
Definition PointerArena.cpp:2147
::std::size_t GetAllocationRecordCount() const
Definition PointerArena.cpp:2142
::std::size_t GetPageRecordCount() const
Definition PointerArena.cpp:2152
friend void FreeBytes(void *raw, ::std::size_t bytes)
Definition PointerArena.cpp:2242
PointerArena()
Definition PointerArena.cpp:647
bool IsArenaHandle(Handle handle) const
Definition PointerArena.cpp:2112
const void * GetBase() const
Definition PointerArena.cpp:2122
void * AllocateBytes(::std::size_t bytes, ::std::size_t align)
Definition PointerArena.cpp:1496
::std::size_t GetForeignCount() const
Definition PointerArena.cpp:2137
Handle HandleFor(const void *raw)
Definition PointerArena.cpp:2002
Handle ArenaHandleFor(const void *raw) const
Definition PointerArena.cpp:1768
const void * Resolve(Handle handle) const
Definition PointerArena.cpp:2099
~PointerArena()
Definition PointerArena.cpp:687
::std::size_t GetBytesHandedOut() const
Definition PointerArena.cpp:2132
::std::size_t GetCapacityBytes() const
Definition PointerArena.cpp:2127
friend void DeleteObjectStorage(void *raw, ::std::size_t bytes)
Definition PointerArena.cpp:2204
static PointerArena & Instance()
Definition PointerArena.cpp:641
void * ReallocateBytes(void *raw, ::std::size_t oldBytes, ::std::size_t newBytes, ::std::size_t align)
Definition PointerArena.cpp:1541
bool IsForeignHandle(Handle handle) const
Definition PointerArena.cpp:2117
bool Owns(const void *raw) const
Definition PointerArena.cpp:1758
bool Owns(const void *raw)
Definition PointerArena.cpp:2302
const void * Resolve(Handle handle)
Definition PointerArena.cpp:2307
T * NewWith(const A1 &a1)
Definition PointerArena.h:502
void Delete(T *raw)
Definition PointerArena.h:525
const void * ArenaBase()
Definition PointerArena.cpp:2340
bool IsArenaHandle(Handle handle)
Definition PointerArena.cpp:2330
::std::size_t ArenaBytesHandedOut()
Definition PointerArena.cpp:2345
bool operator==(const MemoryAllocation &lhs, const MemoryAllocation &rhs)
Definition PointerArena.h:77
uint32_t HandleOffsetField(Handle handle)
Definition PointerArena.h:375
bool UseSystemContainerStorage()
Definition PointerArena.cpp:2259
inline ::std::size_t AlignmentOf()
Definition PointerArena.h:467
bool IsForeignHandle(Handle handle)
Definition PointerArena.cpp:2335
void PushSystemContainerStorage()
Definition PointerArena.cpp:2264
T * New()
Definition PointerArena.h:479
Handle MakeHandle(uint32_t wordOffset, uint16_t generation)
Definition PointerArena.h:361
::std::size_t ForeignHandleCount()
Definition PointerArena.cpp:2350
void DeleteObjectStorage(void *raw)
Definition PointerArena.cpp:2188
::std::size_t ArenaPageRecordCount()
Definition PointerArena.cpp:2368
uint16_t HandleGeneration(Handle handle)
Definition PointerArena.h:380
::std::size_t ArenaSmallAllocationCount()
Definition PointerArena.cpp:2362
void PopSystemContainerStorage()
Definition PointerArena.cpp:2269
uint32_t Handle
Definition PointerArena.h:45
uint32_t HandleWordOffset(Handle handle)
Definition PointerArena.h:390
T * NewCopy(const T &value)
Definition PointerArena.h:486
void * Reallocate(void *raw, ::std::size_t oldBytes, ::std::size_t newBytes, ::std::size_t align)
Definition PointerArena.cpp:2178
Handle HandleFor(const void *raw)
Definition PointerArena.cpp:2278
void * Allocate(::std::size_t bytes, ::std::size_t align)
Definition PointerArena.cpp:2157
void FreeBytes(void *raw)
Definition PointerArena.cpp:2226
bool IsForeignHandleValue(Handle handle)
Definition PointerArena.h:385
::std::size_t ArenaAllocationRecordCount()
Definition PointerArena.cpp:2356
Definition FinalCell.h:29
uint32_t Index
Definition common/container/common/Types.h:31
Definition PointerArena.h:61
uint16_t mFlags
Definition PointerArena.h:66
uint16_t mGeneration
Definition PointerArena.h:65
MemoryAllocation()
Definition PointerArena.cpp:57
uint32_t mOffsetWords
Definition PointerArena.h:62
uint32_t mRequestedBytes
Definition PointerArena.h:64
uint32_t mCapacityWords
Definition PointerArena.h:63
Definition PointerArena.h:90
uint32_t mTotalSlots
Definition PointerArena.h:97
uint32_t mNextFreshSlot
Definition PointerArena.h:98
uint32_t mFirstFreeWordOffset
Definition PointerArena.h:95
uint32_t mOffsetWords
Definition PointerArena.h:91
unsigned char * mSlotStates
Definition PointerArena.h:99
uint32_t mPageBytes
Definition PointerArena.h:92
MemoryPage()
Definition PointerArena.cpp:82
uint16_t mFlags
Definition PointerArena.h:94
uint16_t mSlotBytes
Definition PointerArena.h:93
uint32_t mFreeSlots
Definition PointerArena.h:96