Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2024 ChilliBits. All rights reserved. | ||
2 | |||
3 | #pragma once | ||
4 | |||
5 | #include <vector> | ||
6 | |||
7 | #include <exception/CompilerError.h> | ||
8 | #include <util/CommonUtil.h> | ||
9 | #include <util/Memory.h> | ||
10 | |||
11 | namespace spice::compiler { | ||
12 | |||
13 | template <typename Base> class BlockAllocator { | ||
14 | public: | ||
15 | // Constructors | ||
16 | 393 | explicit BlockAllocator(const MemoryManager &memoryManager, size_t blockSize = CommonUtil::getSystemPageSize()) | |
17 | 393 | : memoryManager(memoryManager), blockSize(blockSize) { | |
18 |
1/2✓ Branch 1 taken 393 times.
✗ Branch 2 not taken.
|
393 | allocateNewBlock(); |
19 | 393 | } | |
20 | 393 | ~BlockAllocator() { | |
21 | // Destruct all objects | ||
22 |
2/2✓ Branch 5 taken 1128806 times.
✓ Branch 6 taken 393 times.
|
1129199 | for (Base *ptr : allocatedObjects) |
23 | 1128806 | ptr->~Base(); | |
24 | 393 | allocatedObjects.clear(); | |
25 | |||
26 | // Free memory | ||
27 |
2/2✓ Branch 5 taken 66520 times.
✓ Branch 6 taken 393 times.
|
66913 | for (byte *ptr : memoryBlocks) |
28 | 66520 | memoryManager.deallocate(ptr); | |
29 | 393 | memoryBlocks.clear(); | |
30 | 393 | } | |
31 | |||
32 | // Public methods | ||
33 | 2156605 | template <typename T, typename... Args> T *allocate(Args &&...args) { | |
34 | static_assert(std::is_base_of_v<Base, T>, "T must be derived from Base"); | ||
35 | 2156605 | constexpr size_t objSize = sizeof(T); | |
36 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1128807 times.
|
2156605 | assert(objSize <= blockSize && "Object size exceeds block size"); |
37 | |||
38 | // Check if we need a new block | ||
39 |
2/2✓ Branch 0 taken 66128 times.
✓ Branch 1 taken 1062679 times.
|
2156605 | if (offsetInBlock + objSize >= blockSize) |
40 | 107004 | allocateNewBlock(); | |
41 | |||
42 | // Construct object at the offset address | ||
43 | 2156604 | byte *destAddr = memoryBlocks.back() + offsetInBlock; | |
44 |
1/4✓ Branch 3 taken 152441 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
2156604 | T *ptr = new (destAddr) T(std::forward<Args>(args)...); |
45 |
1/2✓ Branch 1 taken 1128806 times.
✗ Branch 2 not taken.
|
2156604 | allocatedObjects.push_back(ptr); |
46 | |||
47 | #ifndef NDEBUG | ||
48 |
1/2✓ Branch 2 taken 1128806 times.
✗ Branch 3 not taken.
|
2156604 | ++allocatedClassStatistic[typeid(T).name()]; |
49 | #endif | ||
50 | |||
51 | // Update offset to be ready to store the next object | ||
52 | 2156604 | offsetInBlock += objSize; | |
53 | 2156604 | return ptr; | |
54 | } | ||
55 | |||
56 | 2 | [[nodiscard]] size_t getTotalAllocatedSize() const { return memoryBlocks.size() * blockSize; } | |
57 | 2 | [[nodiscard]] size_t getAllocationCount() const { return allocatedObjects.size(); } | |
58 | #ifndef NDEBUG | ||
59 | ✗ | void printAllocatedClassStatistic() const { | |
60 | ✗ | std::vector<std::pair<const char *, size_t>> elements(allocatedClassStatistic.begin(), allocatedClassStatistic.end()); | |
61 | ✗ | std::sort(elements.begin(), elements.end(), [](const auto &left, const auto &right) { return left.second > right.second; }); | |
62 | ✗ | for (const auto &[mangledName, count] : elements) | |
63 | ✗ | std::cout << CommonUtil::demangleTypeName(mangledName) << ": " << count << std::endl; | |
64 | ✗ | } | |
65 | #endif | ||
66 | |||
67 | private: | ||
68 | // Private members | ||
69 | const MemoryManager &memoryManager; | ||
70 | std::vector<byte *> memoryBlocks; | ||
71 | std::vector<Base *> allocatedObjects; | ||
72 | #ifndef NDEBUG | ||
73 | std::unordered_map<const char *, size_t> allocatedClassStatistic; | ||
74 | #endif | ||
75 | size_t blockSize; | ||
76 | size_t offsetInBlock = 0; | ||
77 | |||
78 | // Private methods | ||
79 | 66521 | void allocateNewBlock() { | |
80 | // Allocate new block | ||
81 |
1/2✓ Branch 1 taken 66521 times.
✗ Branch 2 not taken.
|
66521 | byte *ptr = memoryManager.allocate(blockSize); |
82 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 66520 times.
|
66521 | if (!ptr) |
83 |
4/8✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
|
1 | throw CompilerError(OOM, "Could not allocate memory for BlockAllocator. Already allocated " + |
84 | std::to_string(memoryBlocks.size()) + " blocks."); | ||
85 | |||
86 | // Store pointer and reset offset | ||
87 |
1/2✓ Branch 1 taken 66520 times.
✗ Branch 2 not taken.
|
66520 | memoryBlocks.push_back(ptr); |
88 | 66520 | offsetInBlock = 0; | |
89 | 66520 | } | |
90 | }; | ||
91 | |||
92 | } // namespace spice::compiler | ||
93 |