LibSWOC++ 1.5.14
Solid Wall of C++
Loading...
Searching...
No Matches
MemArena.h
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright Verizon Media 2020
7
8#pragma once
9
10#include <mutex>
11#include <memory>
12#include <utility>
13#include <new>
14#if __has_include(<memory_resource>)
15#include <memory_resource>
16#endif
17
18#include "swoc/MemSpan.h"
19#include "swoc/Scalar.h"
20#include "swoc/IntrusiveDList.h"
21#include "swoc/TextView.h"
22
23namespace swoc { inline namespace SWOC_VERSION_NS {
24
34#if __has_include(<memory_resource>)
35 : public std::pmr::memory_resource
36#endif
37{
38 using self_type = MemArena;
39
40public:
41 static constexpr size_t DEFAULT_ALIGNMENT{1};
42
45 static void (*destroyer)(self_type *);
46
57 using unique_ptr = std::unique_ptr<self_type, void (*)(self_type *)>;
58
60 struct Block {
62 static constexpr size_t MIN_FREE_SPACE = 16;
63
65 char *data();
66
68 const char *data() const;
69
72
74 const char *allocated_data_end() const;
75
77 size_t remaining() const;
78
85 static size_t align_padding(void const *ptr, size_t align);
86
93 bool satisfies(size_t n, size_t align) const;
94
97
105
113
119 bool contains(const void *ptr) const;
120
122 bool is_full() const;
123
124 protected:
125 friend MemArena;
126
135 static void operator delete(void *ptr) noexcept;
136
152 static void operator delete([[maybe_unused]] void *ptr, void *place) noexcept;
153
160 explicit Block(size_t n) noexcept;
161
162 size_t size;
163 size_t allocated{0};
164
165 struct Linkage {
167 Block *_next{nullptr};
168 Block *_prev{nullptr};
169
170 static Block *&next_ptr(Block *);
171
172 static Block *&prev_ptr(Block *);
174 } _link;
175 };
176
178 using BlockList = IntrusiveDList<Block::Linkage>;
179
193 explicit MemArena(size_t n = DEFAULT_BLOCK_SIZE);
194
206 explicit MemArena(MemSpan<void> static_block);
207
209 MemArena(self_type const &that) = delete;
210
212 MemArena(self_type &&that) noexcept;
213
215 ~MemArena();
216
218 self_type &operator=(self_type const &that) = delete;
219
221 self_type &operator=(self_type &&that) noexcept;
222
244 static self_type *construct_self_contained(size_t n = DEFAULT_BLOCK_SIZE);
245
256 MemSpan<void> alloc(size_t n, size_t align = DEFAULT_ALIGNMENT);
257
272 template <typename T> MemSpan<T> alloc_span(size_t n);
273
290 template <typename T, typename... Args> T *make(Args &&...args);
291
298
304 MemSpan<char> localize(char const * s);
305
315
324 MemSpan<char> localize_c(char const * s);
325
336 MemArena &freeze(size_t n = 0);
337
344 self_type &thaw();
345
358 MemArena &clear(size_t hint = 0);
359
378
389 MemArena &discard(size_t hint = 0);
390
392 size_t size() const;
393
395 size_t remaining() const;
396
406 template <typename T> MemSpan<T> remnant_span(size_t n);
407
410
419 MemSpan<void> remnant(size_t n, size_t align = DEFAULT_ALIGNMENT);
420
430 self_type &require(size_t n, size_t align = DEFAULT_ALIGNMENT);
431
433 size_t allocated_size() const;
434
440 bool contains(const void *ptr) const;
441
445 size_t reserved_size() const;
446
447 using const_iterator = BlockList::const_iterator;
449
452
455
458
461
462protected:
468 Block *make_block(size_t n);
469
471 void destroy_frozen();
472
474 void destroy_active();
475
479
480 static constexpr size_t ALLOC_HEADER_SIZE = 16;
483
484 size_t _active_allocated = 0;
485 size_t _active_reserved = 0;
488 size_t _frozen_reserved = 0;
489
492 size_t _reserve_hint = 0;
493
496
499
500 // Note on _active block list - blocks that become full are moved to the end of the list.
501 // This means that when searching for a block with space, the first full block encountered
502 // marks the last block to check. This keeps the set of blocks to check short.
503
504private:
505#if __has_include(<memory_resource>)
506 // PMR support methods.
507
509 void *do_allocate(std::size_t bytes, std::size_t align) override;
510
513 void do_deallocate(void *, size_t, size_t) override;
514
517 bool do_is_equal(std::pmr::memory_resource const &that) const noexcept override;
518#endif
519};
520
528template <typename T> class FixedArena {
529 using self_type = FixedArena;
530protected:
532 struct Item {
534 };
535
536 Item _list{nullptr};
538
539public:
545
552 template <typename... Args> T *make(Args... args);
553
560 void destroy(T *t);
561
563 void clear();
564
567};
568
569// --- Implementation ---
571
572inline auto
573MemArena::Block::Linkage::next_ptr(Block *b) -> Block *& {
574 return b->_link._next;
575}
576
577inline auto
578MemArena::Block::Linkage::prev_ptr(Block *b) -> Block *& {
579 return b->_link._prev;
580}
581
582inline MemArena::Block::Block(size_t n) noexcept : size(n) {}
583
584inline char *
586 return reinterpret_cast<char *>(this + 1);
587}
588
589inline const char *
590MemArena::Block::data() const {
591 return reinterpret_cast<const char *>(this + 1);
592}
593
594inline char *
596 return this->data() + allocated;
597}
598
599inline const char *
601 return this->data() + allocated;
602}
603
604inline bool
605MemArena::Block::contains(const void *ptr) const {
606 const char *base = this->data();
607 return base <= ptr && ptr < base + size;
608}
609
610inline size_t
612 return size - allocated;
613}
614
615inline bool
617 return this->remaining() < MIN_FREE_SPACE;
618}
619
620inline MemSpan<void>
621MemArena::Block::alloc(size_t n, size_t align) {
622 auto base = this->data() + allocated;
623 auto pad = align_padding(base, align);
624 if ((n + pad) > this->remaining()) {
625 throw(std::invalid_argument{"MemArena::Block::alloc size is more than remaining."});
626 }
627 MemSpan<void> zret = this->remnant().prefix(n + pad);
628 zret.remove_prefix(pad);
629 allocated += n + pad;
630 return zret;
631}
632
633inline MemSpan<void>
635 return {this->data() + allocated, this->remaining()};
636}
637
638inline MemArena::Block &
640 allocated = 0;
641 return *this;
642}
643
644inline void
645MemArena::Block::operator delete(void *ptr) noexcept {
646 ::free(ptr);
647}
648inline void
649MemArena::Block::operator delete([[maybe_unused]] void *ptr, void *place) noexcept {
650 ::free(place);
651}
652
653inline size_t
654MemArena::Block::align_padding(void const *ptr, size_t align) {
655 if (auto delta = uintptr_t(ptr) & (align - 1); delta > 0) {
656 return align - delta;
657 }
658 return 0;
659}
660
661inline MemArena::MemArena(size_t n) : _reserve_hint(n) {}
662
663template <typename T>
665MemArena::alloc_span(size_t n) {
666 return this->alloc(sizeof(T) * n, alignof(T)).rebind<T>();
667}
668
669template <typename T, typename... Args>
670T *
671MemArena::make(Args &&...args) {
672 return new (this->alloc(sizeof(T), alignof(T)).data()) T(std::forward<Args>(args)...);
673}
674
676 auto span = this->alloc(s.size()).rebind<char>();
677 memcpy(span.data(), s.data(), span.size());
678 return { span.data(), span.size() };
679}
680
681inline MemSpan<char> MemArena::localize(char const * s) {
682 return this->localize(MemSpan<char const>(s, strlen(s)));
683}
684
686 auto span = this->alloc(s.size() + 1).rebind<char>();
687 memcpy(span.data(), s.data(), span.size());
688 span[s.size()] = '\0';
689 return { span.data(), span.size() };
690}
691
692inline MemSpan<char> MemArena::localize_c(char const * s) {
693 return this->localize_c(MemSpan<char const>(s, strlen(s)));
694}
695
696template <typename T>
698MemArena::remnant_span(size_t n) {
699 auto span = this->require(sizeof(T) * n, alignof(T)).remnant();
700 return span.remove_prefix(Block::align_padding(span.data(), alignof(T))).rebind<T>();
701}
702
703template <>
704inline MemSpan<void>
706 return this->require(n).remnant().prefix(n);
707}
708
709inline MemSpan<void>
710MemArena::remnant(size_t n, size_t align) {
711 return this->require(n, align).remnant().prefix(n);
712}
713
714inline size_t
715MemArena::size() const {
716 return _active_allocated;
717}
718
719inline size_t
722}
723
724inline size_t
725MemArena::remaining() const {
726 return _active.empty() ? 0 : _active.head()->remaining();
727}
728
729inline MemSpan<void>
731 return _active.empty() ? MemSpan<void>() : _active.head()->remnant();
732}
733
734inline size_t
737}
738
739inline auto
740MemArena::begin() const -> const_iterator {
741 return _active.begin();
742}
743
744inline auto
745MemArena::end() const -> const_iterator {
746 return _active.end();
747}
748
749inline auto
750MemArena::frozen_begin() const -> const_iterator {
751 return _frozen.begin();
752}
753
754inline auto
755MemArena::frozen_end() const -> const_iterator {
756 return _frozen.end();
757}
758
759template <typename T> FixedArena<T>::FixedArena(MemArena &arena) : _arena(arena) {
760 static_assert(sizeof(T) >= sizeof(T *));
761}
762
763template <typename T>
764template <typename... Args>
765T *
766FixedArena<T>::make(Args... args) {
767 if (_list._next) {
768 void *t = _list._next;
769 _list._next = _list._next->_next;
770 return new (t) T(std::forward<Args>(args)...);
771 }
772 return _arena.template make<T>(std::forward<Args>(args)...);
773}
774
775template <typename T>
776void
778 if (t) {
779 t->~T(); // destructor.
780 auto item = reinterpret_cast<Item *>(t);
781 item->_next = _list._next;
782 _list._next = item;
783 }
784}
785
786template <typename T>
787void
789 _list._next = nullptr;
790}
791
792template <typename T>
793MemArena &
795 return _arena;
796}
797
799
800}} // namespace swoc::SWOC_VERSION_NS
MemArena(size_t n=DEFAULT_BLOCK_SIZE)
MemArena & _arena
Memory source.
Definition MemArena.h:537
MemArena & arena()
Access the wrapped arena directly.
Item _list
List of dead instances.
Definition MemArena.h:536
void destroy(T *t)
FixedArena(MemArena &arena)
void clear()
Drop all items in the free list.
T * make(Args... args)
bool contains(const void *ptr) const
Definition MemArena.cc:145
IntrusiveDList< Block::Linkage > BlockList
Intrusive list of blocks.
Definition MemArena.h:178
self_type & thaw()
Definition MemArena.cc:133
size_t size() const
size_t _active_allocated
Total allocations in the active generation.
Definition MemArena.h:484
const_iterator end() const
After Last active block.
MemSpan< void > alloc(size_t n, size_t align=DEFAULT_ALIGNMENT)
Definition MemArena.cc:103
self_type & operator=(self_type const &that)=delete
No copy assignment.
size_t _frozen_reserved
Total frozen reserved memory.
Definition MemArena.h:488
MemSpan< char > localize_c(char const *s)
const_iterator begin() const
First active block.
void destroy_frozen()
Clean up the frozen list.
Definition MemArena.cc:190
size_t reserved_size() const
MemSpan< char > localize(char const *s)
MemSpan< char > localize_c(MemSpan< char const > s)
BlockList _active
Current generation. Allocate here.
Definition MemArena.h:495
static constexpr size_t DEFAULT_ALIGNMENT
Default memory alignment.
Definition MemArena.h:41
size_t _active_reserved
Definition MemArena.h:485
size_t _reserve_hint
Definition MemArena.h:492
static constexpr size_t ALLOC_HEADER_SIZE
Definition MemArena.h:480
static constexpr size_t DEFAULT_BLOCK_SIZE
Initial block size to allocate if not specified via API.
Definition MemArena.h:482
MemArena & discard(MemSpan< void const > span)
Definition MemArena.cc:213
MemSpan< char > localize(MemSpan< char const > s)
MemSpan< T > remnant_span(size_t n)
BlockList::const_iterator const_iterator
Constant element iteration.
Definition MemArena.h:447
MemSpan< void > remnant()
MemArena(self_type const &that)=delete
no copying
const_iterator iterator
Element iteration.
Definition MemArena.h:448
Block * _static_block
Static block, if any.
Definition MemArena.h:498
Block * make_block(size_t n)
Definition MemArena.cc:73
MemArena & freeze(size_t n=0)
Definition MemArena.cc:118
T * make(Args &&...args)
static self_type * construct_self_contained(size_t n=DEFAULT_BLOCK_SIZE)
Definition MemArena.cc:54
void destroy_active()
Clean up the active list.
Definition MemArena.cc:178
self_type & require(size_t n, size_t align=DEFAULT_ALIGNMENT)
Definition MemArena.cc:152
MemSpan< void > remnant(size_t n, size_t align=DEFAULT_ALIGNMENT)
Scalar< 4096 > Page
Size for rounding block sizes.
Definition MemArena.h:476
const_iterator frozen_end() const
After last frozen block.
size_t remaining() const
BlockList _frozen
Previous generation, frozen memory.
Definition MemArena.h:494
MemArena & clear(size_t hint=0)
Definition MemArena.cc:202
Scalar< Page::SCALE/4 > QuarterPage
Quarter page - unit for sub page sizes.
Definition MemArena.h:477
const_iterator frozen_begin() const
First frozen block.
static void(* destroyer)(self_type *)
Definition MemArena.h:45
MemArena(size_t n=DEFAULT_BLOCK_SIZE)
MemSpan< T > alloc_span(size_t n)
Scalar< 16 > Paragraph
Minimum unit of memory allocation.
Definition MemArena.h:478
std::unique_ptr< self_type, void(*)(self_type *)> unique_ptr
Definition MemArena.h:57
size_t _frozen_allocated
Total allocations in the previous generation. This is only non-zero while the arena is frozen.
Definition MemArena.h:487
size_t allocated_size() const
constexpr value_type * data() const
Pointer to memory in the span.
Definition MemSpan.h:1467
MemSpan< U > rebind() const
Definition MemSpan.h:1658
self_type prefix(size_t n) const
Definition MemSpan.h:1512
self_type & remove_prefix(size_t count)
Definition MemSpan.h:1525
static constexpr intmax_t SCALE
Definition Scalar.h:177
For template deduction guides.
Definition ArenaWriter.cc:9
MemSpan(std::array< T, N > &) -> MemSpan< T >
Deduction guides.
Scalar_INTERNAL constexpr detail::scalar_unit_round_up_t< C > round_up(C n)
Definition Scalar.h:559
bool is_full() const
char * allocated_data_end()
MemSpan< void > alloc(size_t n, size_t=DEFAULT_ALIGNMENT)
Block(size_t n) noexcept
size_t remaining() const
Amount of unallocated storage.
MemSpan< void > remnant()
Span of unallocated storage.
Block & discard()
static size_t align_padding(void const *ptr, size_t align)
char * data()
Get the start of the data in this block.
bool contains(const void *ptr) const
Rebinding type for instances on the free list.
Definition MemArena.h:532
Item * _next
Next item in the free list.
Definition MemArena.h:533
Simple internal arena block of memory. Maintains the underlying memory.
Definition MemArena.h:60
bool is_full() const
MemSpan< void > alloc(size_t n, size_t=DEFAULT_ALIGNMENT)
static constexpr size_t MIN_FREE_SPACE
A block must have at least this much free space to not be "full".
Definition MemArena.h:62
Block(size_t n) noexcept
size_t remaining() const
Amount of unallocated storage.
MemSpan< void > remnant()
Span of unallocated storage.
const char * allocated_data_end() const
const char * data() const
Get the start of the data in this block.
static size_t align_padding(void const *ptr, size_t align)
bool satisfies(size_t n, size_t align) const
Definition MemArena.cc:16
size_t allocated
Current allocated (in use) bytes.
Definition MemArena.h:163
size_t size
Actual block size.
Definition MemArena.h:162
friend MemArena
Container.
Definition MemArena.h:125
char * data()
Get the start of the data in this block.
bool contains(const void *ptr) const