8#ifndef SEN_CORE_BASE_MEMORY_BLOCK_H
9#define SEN_CORE_BASE_MEMORY_BLOCK_H
24class FixedMemoryBlock;
29class FixedMemoryBlockPoolBase
31 SEN_NOCOPY_NOMOVE(FixedMemoryBlockPoolBase)
34 virtual ~FixedMemoryBlockPoolBase() =
default;
38 template <std::
size_t size>
39 [[nodiscard]]
static constexpr std::size_t minBlockSize() noexcept
41 return size <
sizeof(
void*) ?
sizeof(
void*) : size;
46 explicit FixedMemoryBlockPoolBase(std::size_t blockSize): blockSize_(blockSize) {}
47 friend class FixedMemoryBlock;
53 [[nodiscard]]
virtual uint8_t* alloc() = 0;
56 virtual void free(uint8_t* ptr)
noexcept = 0;
59 [[nodiscard]] std::size_t getBlockSize() const noexcept {
return blockSize_; }
62 std::size_t blockSize_;
71template <std::
size_t blockSize>
72class FixedMemoryBlockPool final:
public FixedMemoryBlockPoolBase,
73 public std::enable_shared_from_this<FixedMemoryBlockPool<blockSize>>
75 SEN_NOCOPY_NOMOVE(FixedMemoryBlockPool)
76 static_assert(blockSize >=
sizeof(
void*));
79 static constexpr std::size_t defaultBlocksPerBucket = 10U;
80 static constexpr std::size_t defaultBucketPreAlloc = 10U;
86 [[nodiscard]]
static std::shared_ptr<FixedMemoryBlockPool> make(std::size_t blocksPerBucket = defaultBlocksPerBucket,
87 std::size_t bucketPreAlloc = defaultBucketPreAlloc);
89 ~FixedMemoryBlockPool()
override
97 [[nodiscard]] std::shared_ptr<FixedMemoryBlock> getBlockPtr() noexcept
99 return std::make_shared<FixedMemoryBlock>(this->shared_from_this(), alloc());
103 void free(uint8_t* ptr)
noexcept override
108 [[nodiscard]] uint8_t* alloc()
override
115 explicit FixedMemoryBlockPool(std::size_t blocksPerBucket = defaultBlocksPerBucket,
116 std::size_t bucketPreAlloc = defaultBucketPreAlloc);
119 void doFree(uint8_t* ptr)
noexcept;
120 [[nodiscard]] uint8_t* doAlloc();
124 friend class FixedMemoryBlock;
125 using Mutex = std::recursive_mutex;
126 using Lock = std::scoped_lock<Mutex>;
129 std::size_t blocksPerBucket_;
130 std::vector<std::unique_ptr<std::vector<Block>>> buckets_ = {};
132 mutable Mutex mutex_;
136class ResizableHeapBlock;
142 SEN_NOCOPY_NOMOVE(MemoryBlock)
145 MemoryBlock() =
default;
146 virtual ~MemoryBlock() =
default;
150 [[nodiscard]]
virtual uint8_t* data() noexcept = 0;
153 [[nodiscard]] virtual const uint8_t* data() const noexcept = 0;
156 [[nodiscard]] virtual std::
size_t size() const noexcept = 0;
159 [[nodiscard]]
bool empty() const noexcept {
return size() == 0; }
162 virtual void resize(std::size_t size) = 0;
165 virtual void reserve(std::size_t size) = 0;
168 [[nodiscard]] Span<const uint8_t> getConstSpan() const noexcept {
return makeConstSpan(data(), size()); }
169 [[nodiscard]] Span<uint8_t> getSpan() noexcept {
return makeSpan(data(), size()); }
172 operator Span<const uint8_t>() const noexcept {
return getConstSpan(); }
173 operator Span<uint8_t>() noexcept {
return getSpan(); }
176using MemBlockPtr = std::shared_ptr<MemoryBlock>;
179class FixedMemoryBlock final:
public MemoryBlock
182 SEN_COPY_CONSTRUCT(FixedMemoryBlock) =
delete;
183 SEN_COPY_ASSIGN(FixedMemoryBlock) =
delete;
184 SEN_MOVE_CONSTRUCT(FixedMemoryBlock);
185 SEN_MOVE_ASSIGN(FixedMemoryBlock);
189 FixedMemoryBlock(std::shared_ptr<FixedMemoryBlockPoolBase> owner, uint8_t* data)
190 : owner_(std::move(owner)), data_(data)
193 ~FixedMemoryBlock() noexcept
override
203 [[nodiscard]] uint8_t* data() noexcept
override {
return data_; }
206 [[nodiscard]]
const uint8_t* data() const noexcept
override {
return data_; }
209 [[nodiscard]] std::size_t size() const noexcept
override {
return size_; }
213 void resize(std::size_t size)
override;
216 void reserve(std::size_t size)
override;
219 std::shared_ptr<FixedMemoryBlockPoolBase> owner_ {
nullptr};
220 uint8_t* data_ {
nullptr};
221 std::size_t size_ = 0U;
225class ResizableHeapBlock final:
public MemoryBlock
228 SEN_NOCOPY_NOMOVE(ResizableHeapBlock)
231 ResizableHeapBlock() =
default;
232 ~ResizableHeapBlock() noexcept override = default;
236 [[nodiscard]] uint8_t* data() noexcept
override {
return data_.data(); }
239 [[nodiscard]]
const uint8_t* data() const noexcept
override {
return data_.data(); }
242 [[nodiscard]] std::size_t size() const noexcept
override {
return data_.size(); }
245 void resize(std::size_t size)
override { data_.resize(size); }
248 void reserve(std::size_t size)
override { data_.reserve(size); }
251 std::vector<uint8_t> data_;
264inline FixedMemoryBlock::FixedMemoryBlock(FixedMemoryBlock&& other) noexcept
265 : owner_(std::exchange(other.owner_,
nullptr))
266 , data_(std::exchange(other.data_,
nullptr))
267 , size_(std::exchange(other.size_, 0U))
272inline FixedMemoryBlock& FixedMemoryBlock::operator=(FixedMemoryBlock&& other)
noexcept
274 std::swap(owner_, other.owner_);
275 std::swap(data_, other.data_);
276 std::swap(size_, other.size_);
285template <std::
size_t blockSize>
286class FixedMemoryBlockPool<blockSize>::Block
291 Block() noexcept = default;
295 [[nodiscard]] Block* getNext() noexcept
300 void setNext(Block* next)
noexcept
308 std::array<uint8_t, blockSize> buffer;
309 Block* next =
nullptr;
319template <std::
size_t blockSize>
320inline std::shared_ptr<FixedMemoryBlockPool<blockSize>> FixedMemoryBlockPool<blockSize>::make(
321 std::size_t blocksPerBucket,
322 std::size_t bucketPreAlloc)
324 return std::shared_ptr<FixedMemoryBlockPool>(
new FixedMemoryBlockPool(blocksPerBucket, bucketPreAlloc));
327template <std::
size_t blockSize>
328inline FixedMemoryBlockPool<blockSize>::FixedMemoryBlockPool(std::size_t blocksPerBucket, std::size_t bucketPreAlloc)
329 : FixedMemoryBlockPoolBase(blockSize), blocksPerBucket_(blocksPerBucket)
333 buckets_.reserve(bucketPreAlloc);
337template <std::
size_t blockSize>
338SEN_ALWAYS_INLINE uint8_t* FixedMemoryBlockPool<blockSize>::doAlloc()
340 if (firstBlock_ ==
nullptr)
345 auto result = firstBlock_;
346 firstBlock_ = firstBlock_->getNext();
347 return reinterpret_cast<uint8_t*
>(result);
350template <std::
size_t blockSize>
351SEN_ALWAYS_INLINE
void FixedMemoryBlockPool<blockSize>::doFree(uint8_t* ptr)
noexcept
353 auto nextFree = firstBlock_;
354 firstBlock_ =
new (
static_cast<void*
>(ptr)) Block();
355 firstBlock_->setNext(nextFree);
358template <std::
size_t blockSize>
359inline void FixedMemoryBlockPool<blockSize>::resize()
362 if (buckets_.size() == buckets_.capacity())
364 buckets_.reserve(buckets_.capacity() * 2);
368 auto bucketPtr = std::make_unique<std::vector<Block>>(blocksPerBucket_);
371 for (std::size_t i = 0; i < bucketPtr->size() - 2U; ++i)
373 (*bucketPtr)[i].setNext(&(*bucketPtr)[i + 1U]);
377 SEN_ENSURE(bucketPtr->back().getNext() ==
nullptr);
380 firstBlock_ = &bucketPtr->front();
383 buckets_.push_back(std::move(bucketPtr));
#define SEN_ASSERT(expr)
Checks an intermediate result produced by a procedure (not an input or output). NOLINTNEXTLINE.
Definition assert.h:39
#define SEN_ENSURE(expr)
Checks a post-condition of a procedure (function return value for example). NOLINTNEXTLINE.
Definition assert.h:42
constexpr Span< T > makeSpan(T *ptr, std::size_t size) noexcept
Takes in a type that can be passed to a contiguous range and returns a Span.
Definition span.h:201
constexpr Span< const T > makeConstSpan(const T *ptr, std::size_t size) noexcept
Takes in a type that can be passed to a contiguous range and returns a Span. The element types will b...
Definition span.h:248