1 | // [Blend2D] |
2 | // 2D Vector Graphics Powered by a JIT Compiler. |
3 | // |
4 | // [License] |
5 | // Zlib - See LICENSE.md file in the package. |
6 | |
7 | #include "./blapi-build_p.h" |
8 | #include "./blsupport_p.h" |
9 | #include "./blzoneallocator_p.h" |
10 | |
11 | // ============================================================================ |
12 | // [BLZoneAllocator - Statics] |
13 | // ============================================================================ |
14 | |
15 | // Zero size block used by `BLZoneAllocator` that doesn't have any memory allocated. |
16 | // Should be allocated in read-only memory and should never be modified. |
17 | const BLZoneAllocator::Block BLZoneAllocator::_zeroBlock = { nullptr, nullptr, 0 }; |
18 | |
19 | // ============================================================================ |
20 | // [BLZoneAllocator - Init / Reset] |
21 | // ============================================================================ |
22 | |
23 | void BLZoneAllocator::_init(size_t blockSize, size_t blockAlignment, void* staticData, size_t staticSize) noexcept { |
24 | BL_ASSERT(blockSize >= kMinBlockSize); |
25 | BL_ASSERT(blockSize <= kMaxBlockSize); |
26 | BL_ASSERT(blockAlignment <= 64); |
27 | |
28 | _assignZeroBlock(); |
29 | _blockSize = blockSize & blTrailingBitMask<size_t>(blBitSizeOf<size_t>() - 4); |
30 | _hasStaticBlock = staticData != nullptr; |
31 | _blockAlignmentShift = blBitCtz(blockAlignment) & 0x7; |
32 | |
33 | // Setup the first [temporary] block, if necessary. |
34 | if (staticData) { |
35 | Block* block = static_cast<Block*>(staticData); |
36 | block->prev = nullptr; |
37 | block->next = nullptr; |
38 | |
39 | BL_ASSERT(staticSize >= kBlockSize); |
40 | block->size = staticSize - kBlockSize; |
41 | |
42 | _assignBlock(block); |
43 | } |
44 | } |
45 | |
46 | void BLZoneAllocator::reset() noexcept { |
47 | // Can't be altered. |
48 | Block* cur = _block; |
49 | if (cur == &_zeroBlock) |
50 | return; |
51 | |
52 | Block* initial = const_cast<BLZoneAllocator::Block*>(&_zeroBlock); |
53 | _ptr = initial->data(); |
54 | _end = initial->data(); |
55 | _block = initial; |
56 | |
57 | // Since cur can be in the middle of the double-linked list, we have to |
58 | // traverse both directions (`prev` and `next`) separately to visit all. |
59 | Block* next = cur->next; |
60 | do { |
61 | Block* prev = cur->prev; |
62 | |
63 | // If this is the first block and this BLZoneAllocatorTmp is temporary then |
64 | // the first block is statically allocated. We cannot free it and it makes |
65 | // sense to keep it even when this is hard reset. |
66 | if (prev == nullptr && _hasStaticBlock) { |
67 | cur->prev = nullptr; |
68 | cur->next = nullptr; |
69 | _assignBlock(cur); |
70 | break; |
71 | } |
72 | |
73 | free(cur); |
74 | cur = prev; |
75 | } while (cur); |
76 | |
77 | cur = next; |
78 | while (cur) { |
79 | next = cur->next; |
80 | free(cur); |
81 | cur = next; |
82 | } |
83 | } |
84 | |
85 | // ============================================================================ |
86 | // [BLZoneAllocator - Alloc] |
87 | // ============================================================================ |
88 | |
89 | void* BLZoneAllocator::_alloc(size_t size, size_t alignment) noexcept { |
90 | Block* curBlock = _block; |
91 | Block* next = curBlock->next; |
92 | |
93 | size_t rawBlockAlignment = blockAlignment(); |
94 | size_t minimumAlignment = blMax<size_t>(alignment, rawBlockAlignment); |
95 | |
96 | // If the `BLZoneAllocator` has been cleared the current block doesn't have to be the |
97 | // last one. Check if there is a block that can be used instead of allocating |
98 | // a new one. If there is a `next` block it's completely unused, we don't have |
99 | // to check for remaining bytes in that case. |
100 | if (next) { |
101 | uint8_t* ptr = blAlignUp(next->data(), minimumAlignment); |
102 | uint8_t* end = blAlignDown(next->data() + next->size, rawBlockAlignment); |
103 | |
104 | if (size <= (size_t)(end - ptr)) { |
105 | _block = next; |
106 | _ptr = ptr + size; |
107 | _end = blAlignDown(next->data() + next->size, rawBlockAlignment); |
108 | return static_cast<void*>(ptr); |
109 | } |
110 | } |
111 | |
112 | size_t blockAlignmentOverhead = alignment - blMin<size_t>(alignment, BL_ALLOC_ALIGNMENT); |
113 | size_t newSize = blMax(blockSize(), size); |
114 | |
115 | // Prevent arithmetic overflow. |
116 | if (BL_UNLIKELY(newSize > SIZE_MAX - kBlockSize - blockAlignmentOverhead)) |
117 | return nullptr; |
118 | |
119 | // Allocate new block - we add alignment overhead to `newSize`, which becomes the |
120 | // new block size, and we also add `kBlockOverhead` to the allocator as it includes |
121 | // members of `BLZoneAllocator::Block` structure. |
122 | newSize += blockAlignmentOverhead; |
123 | Block* newBlock = static_cast<Block*>(malloc(newSize + kBlockSize)); |
124 | |
125 | if (BL_UNLIKELY(!newBlock)) |
126 | return nullptr; |
127 | |
128 | // Align the pointer to `minimumAlignment` and adjust the size of this block |
129 | // accordingly. It's the same as using `minimumAlignment - blAlignUpDiff()`, |
130 | // just written differently. |
131 | { |
132 | newBlock->prev = nullptr; |
133 | newBlock->next = nullptr; |
134 | newBlock->size = newSize; |
135 | |
136 | if (curBlock != &_zeroBlock) { |
137 | newBlock->prev = curBlock; |
138 | curBlock->next = newBlock; |
139 | |
140 | // Does only happen if there is a next block, but the requested memory |
141 | // can't fit into it. In this case a new buffer is allocated and inserted |
142 | // between the current block and the next one. |
143 | if (next) { |
144 | newBlock->next = next; |
145 | next->prev = newBlock; |
146 | } |
147 | } |
148 | |
149 | uint8_t* ptr = blAlignUp(newBlock->data(), minimumAlignment); |
150 | uint8_t* end = blAlignDown(newBlock->data() + newSize, rawBlockAlignment); |
151 | |
152 | _ptr = ptr + size; |
153 | _end = end; |
154 | _block = newBlock; |
155 | |
156 | BL_ASSERT(_ptr <= _end); |
157 | return static_cast<void*>(ptr); |
158 | } |
159 | } |
160 | |
161 | void* BLZoneAllocator::allocZeroed(size_t size, size_t alignment) noexcept { |
162 | void* p = alloc(size, alignment); |
163 | if (BL_UNLIKELY(!p)) |
164 | return p; |
165 | return memset(p, 0, size); |
166 | } |
167 | |