Sen API
Sen Libraries
Loading...
Searching...
No Matches
memory_block.h
Go to the documentation of this file.
1// === memory_block.h ==================================================================================================
2// Sen Infrastructure
3// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
4// See the LICENSE.txt file for more information.
5// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
6// =====================================================================================================================
7
8#ifndef SEN_CORE_BASE_MEMORY_BLOCK_H
9#define SEN_CORE_BASE_MEMORY_BLOCK_H
10
12#include "sen/core/base/span.h"
13
14#include <memory>
15#include <mutex>
16#include <utility>
17
18namespace sen
19{
20
23
24class FixedMemoryBlock;
25
29class FixedMemoryBlockPoolBase
30{
31 SEN_NOCOPY_NOMOVE(FixedMemoryBlockPoolBase)
32
33public:
34 virtual ~FixedMemoryBlockPoolBase() = default;
35
36public:
38 template <std::size_t size>
39 [[nodiscard]] static constexpr std::size_t minBlockSize() noexcept
40 {
41 return size < sizeof(void*) ? sizeof(void*) : size;
42 }
43
44protected:
46 explicit FixedMemoryBlockPoolBase(std::size_t blockSize): blockSize_(blockSize) {}
47 friend class FixedMemoryBlock;
48
53 [[nodiscard]] virtual uint8_t* alloc() = 0;
54
56 virtual void free(uint8_t* ptr) noexcept = 0;
57
59 [[nodiscard]] std::size_t getBlockSize() const noexcept { return blockSize_; }
60
61private:
62 std::size_t blockSize_;
63};
64
71template <std::size_t blockSize>
72class FixedMemoryBlockPool final: public FixedMemoryBlockPoolBase,
73 public std::enable_shared_from_this<FixedMemoryBlockPool<blockSize>>
74{
75 SEN_NOCOPY_NOMOVE(FixedMemoryBlockPool)
76 static_assert(blockSize >= sizeof(void*));
77
78public:
79 static constexpr std::size_t defaultBlocksPerBucket = 10U;
80 static constexpr std::size_t defaultBucketPreAlloc = 10U;
81
82public:
86 [[nodiscard]] static std::shared_ptr<FixedMemoryBlockPool> make(std::size_t blocksPerBucket = defaultBlocksPerBucket,
87 std::size_t bucketPreAlloc = defaultBucketPreAlloc);
88
89 ~FixedMemoryBlockPool() override
90 {
91 Lock lock(mutex_);
92 buckets_.clear();
93 }
94
95public:
97 [[nodiscard]] std::shared_ptr<FixedMemoryBlock> getBlockPtr() noexcept
98 {
99 return std::make_shared<FixedMemoryBlock>(this->shared_from_this(), alloc());
100 }
101
102protected: // implements FixedMemoryBlockPoolBase
103 void free(uint8_t* ptr) noexcept override
104 {
105 Lock lock(mutex_);
106 doFree(ptr);
107 }
108 [[nodiscard]] uint8_t* alloc() override
109 {
110 Lock lock(mutex_);
111 return doAlloc();
112 }
113
114private:
115 explicit FixedMemoryBlockPool(std::size_t blocksPerBucket = defaultBlocksPerBucket,
116 std::size_t bucketPreAlloc = defaultBucketPreAlloc);
117
118 void resize();
119 void doFree(uint8_t* ptr) noexcept;
120 [[nodiscard]] uint8_t* doAlloc();
121
122private:
123 class Block;
124 friend class FixedMemoryBlock;
125 using Mutex = std::recursive_mutex;
126 using Lock = std::scoped_lock<Mutex>;
127
128private:
129 std::size_t blocksPerBucket_;
130 std::vector<std::unique_ptr<std::vector<Block>>> buckets_ = {};
131 Block* firstBlock_;
132 mutable Mutex mutex_;
133};
134
135// Forward declarations
136class ResizableHeapBlock;
137
139class MemoryBlock
140{
141public:
142 SEN_NOCOPY_NOMOVE(MemoryBlock)
143
144public:
145 MemoryBlock() = default;
146 virtual ~MemoryBlock() = default;
147
148public: // basic container interface
150 [[nodiscard]] virtual uint8_t* data() noexcept = 0;
151
153 [[nodiscard]] virtual const uint8_t* data() const noexcept = 0;
154
156 [[nodiscard]] virtual std::size_t size() const noexcept = 0;
157
159 [[nodiscard]] bool empty() const noexcept { return size() == 0; }
160
162 virtual void resize(std::size_t size) = 0;
163
165 virtual void reserve(std::size_t size) = 0;
166
167public: // translation into Span of uint8_t
168 [[nodiscard]] Span<const uint8_t> getConstSpan() const noexcept { return makeConstSpan(data(), size()); }
169 [[nodiscard]] Span<uint8_t> getSpan() noexcept { return makeSpan(data(), size()); }
170
171public:
172 operator Span<const uint8_t>() const noexcept { return getConstSpan(); } // NOLINT(hicpp-explicit-conversions)
173 operator Span<uint8_t>() noexcept { return getSpan(); } // NOLINT(hicpp-explicit-conversions)
174};
175
176using MemBlockPtr = std::shared_ptr<MemoryBlock>;
177
179class FixedMemoryBlock final: public MemoryBlock
180{
181public: // special members
182 SEN_COPY_CONSTRUCT(FixedMemoryBlock) = delete;
183 SEN_COPY_ASSIGN(FixedMemoryBlock) = delete;
184 SEN_MOVE_CONSTRUCT(FixedMemoryBlock);
185 SEN_MOVE_ASSIGN(FixedMemoryBlock);
186
187public:
189 FixedMemoryBlock(std::shared_ptr<FixedMemoryBlockPoolBase> owner, uint8_t* data)
190 : owner_(std::move(owner)), data_(data)
191 {
192 }
193 ~FixedMemoryBlock() noexcept override
194 {
195 if (data_)
196 {
197 owner_->free(data_);
198 }
199 }
200
201public: // basic container interface
203 [[nodiscard]] uint8_t* data() noexcept override { return data_; }
204
206 [[nodiscard]] const uint8_t* data() const noexcept override { return data_; }
207
209 [[nodiscard]] std::size_t size() const noexcept override { return size_; }
210
213 void resize(std::size_t size) override;
214
216 void reserve(std::size_t size) override;
217
218private:
219 std::shared_ptr<FixedMemoryBlockPoolBase> owner_ {nullptr};
220 uint8_t* data_ {nullptr};
221 std::size_t size_ = 0U;
222};
223
225class ResizableHeapBlock final: public MemoryBlock
226{
227public:
228 SEN_NOCOPY_NOMOVE(ResizableHeapBlock)
229
230public:
231 ResizableHeapBlock() = default;
232 ~ResizableHeapBlock() noexcept override = default;
233
234public: // basic container interface
236 [[nodiscard]] uint8_t* data() noexcept override { return data_.data(); }
237
239 [[nodiscard]] const uint8_t* data() const noexcept override { return data_.data(); }
240
242 [[nodiscard]] std::size_t size() const noexcept override { return data_.size(); }
243
245 void resize(std::size_t size) override { data_.resize(size); }
246
248 void reserve(std::size_t size) override { data_.reserve(size); }
249
250private:
251 std::vector<uint8_t> data_;
252};
253
255
256//----------------------------------------------------------------------------------------------------------------------
257// Inline implementation
258//----------------------------------------------------------------------------------------------------------------------
259
260//--------------------------------------------------------------------------------------------------------------
261// FixedMemoryBlock
262//--------------------------------------------------------------------------------------------------------------
263
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))
268{
269}
270
272inline FixedMemoryBlock& FixedMemoryBlock::operator=(FixedMemoryBlock&& other) noexcept
273{
274 std::swap(owner_, other.owner_);
275 std::swap(data_, other.data_);
276 std::swap(size_, other.size_);
277
278 return *this;
279}
280
281//--------------------------------------------------------------------------------------------------------------
282// FixedMemoryBlockPool::Block
283//--------------------------------------------------------------------------------------------------------------
284
285template <std::size_t blockSize>
286class FixedMemoryBlockPool<blockSize>::Block
287{
288 SEN_MOVE_ONLY(Block)
289
290public:
291 Block() noexcept = default;
292 ~Block() = default;
293
294public:
295 [[nodiscard]] Block* getNext() noexcept
296 {
297 return memory_.next; // NOLINT
298 }
299
300 void setNext(Block* next) noexcept
301 {
302 memory_.next = next; // NOLINT
303 }
304
305private:
306 union Memory // NOLINT
307 {
308 std::array<uint8_t, blockSize> buffer;
309 Block* next = nullptr;
310 };
311
312 Memory memory_;
313};
314
315//--------------------------------------------------------------------------------------------------------------
316// FixedMemoryBlockPool
317//--------------------------------------------------------------------------------------------------------------
318
319template <std::size_t blockSize>
320inline std::shared_ptr<FixedMemoryBlockPool<blockSize>> FixedMemoryBlockPool<blockSize>::make(
321 std::size_t blocksPerBucket,
322 std::size_t bucketPreAlloc)
323{
324 return std::shared_ptr<FixedMemoryBlockPool>(new FixedMemoryBlockPool(blocksPerBucket, bucketPreAlloc));
325}
326
327template <std::size_t blockSize>
328inline FixedMemoryBlockPool<blockSize>::FixedMemoryBlockPool(std::size_t blocksPerBucket, std::size_t bucketPreAlloc)
329 : FixedMemoryBlockPoolBase(blockSize), blocksPerBucket_(blocksPerBucket)
330{
331 SEN_ASSERT(blocksPerBucket_ >= 2);
332
333 buckets_.reserve(bucketPreAlloc);
334 resize();
335}
336
337template <std::size_t blockSize>
338SEN_ALWAYS_INLINE uint8_t* FixedMemoryBlockPool<blockSize>::doAlloc()
339{
340 if (firstBlock_ == nullptr)
341 {
342 resize();
343 }
344
345 auto result = firstBlock_;
346 firstBlock_ = firstBlock_->getNext();
347 return reinterpret_cast<uint8_t*>(result); // NOLINT
348}
349
350template <std::size_t blockSize>
351SEN_ALWAYS_INLINE void FixedMemoryBlockPool<blockSize>::doFree(uint8_t* ptr) noexcept // NOLINT
352{
353 auto nextFree = firstBlock_;
354 firstBlock_ = new (static_cast<void*>(ptr)) Block(); // NOLINT(cppcoreguidelines-owning-memory)
355 firstBlock_->setNext(nextFree);
356}
357
358template <std::size_t blockSize>
359inline void FixedMemoryBlockPool<blockSize>::resize()
360{
361 // ensure we have reserved space for new buckets
362 if (buckets_.size() == buckets_.capacity())
363 {
364 buckets_.reserve(buckets_.capacity() * 2);
365 }
366
367 // create the list of blocks
368 auto bucketPtr = std::make_unique<std::vector<Block>>(blocksPerBucket_);
369
370 // set the _next_ pointer on all the blocks of the bucket
371 for (std::size_t i = 0; i < bucketPtr->size() - 2U; ++i)
372 {
373 (*bucketPtr)[i].setNext(&(*bucketPtr)[i + 1U]); // NOLINT
374 }
375
376 // ensure the last one is the final one
377 SEN_ENSURE(bucketPtr->back().getNext() == nullptr); // NOLINT
378
379 // make our first block point to the new bucket
380 firstBlock_ = &bucketPtr->front();
381
382 // save the list of blocks
383 buckets_.push_back(std::move(bucketPtr)); // NOLINT
384}
385
386} // namespace sen
387
388#endif // SEN_CORE_BASE_MEMORY_BLOCK_H
#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
Definition assert.h:17