Nebula
Loading...
Searching...
No Matches
rangeallocator.h
Go to the documentation of this file.
1#pragma once
2//------------------------------------------------------------------------------
13//------------------------------------------------------------------------------
14#include "core/types.h"
15#include "util/bit.h"
16#include "util/array.h"
17#include <utility>
18namespace Memory
19{
20
24uint BinFromSize(uint size, uint bucket);
25
27{
30
31 static constexpr uint OOM = 0xFFFFFFFF;
32};
33
34static constexpr uint NUM_BUCKETS = 0x20;
35static constexpr uint NUM_BINS_PER_BUCKET = 0x10;
36
38{
39public:
43 RangeAllocator(uint size, SizeT maxNumAllocs);
46
48 void Clear();
49
51 bool Empty();
52
54 RangeAllocation Alloc(uint size, uint alignment = 1);
56 void Dealloc(const RangeAllocation& allocation);
57
58private:
60 uint InsertNode(uint size, uint offset);
62 void RemoveNode(uint nodeIndex);
63
65 {
67 resident(false)
68 , size(0)
69 , offset(0)
70 , binPrev(END)
71 , binNext(END)
72 , blockPrev(END)
73 , blockNext(END)
74 {};
78
79 static constexpr uint END = 0xFFFFFFFF;
82 };
83
85 {
86 BinIndex() : index(0) {};
87 struct
88 {
91 };
93 };
94
96 static BinIndex IndexFromSize(uint size, bool round = false);
97
101
105
108};
109
110//------------------------------------------------------------------------------
113inline
118
119//------------------------------------------------------------------------------
122inline
124{
125 this->size = size;
126 this->freeNodes.Resize(maxNumAllocs);
127 this->nodes.Resize(maxNumAllocs);
128
129 // Clear the allocator
130 this->Clear();
131}
132
133//------------------------------------------------------------------------------
136inline
140
141//------------------------------------------------------------------------------
144inline void
146{
147 // Reset bit masks
148 this->bucketUsageMask = 0x0;
149 memset(this->binMasks, 0x0, sizeof(this->binMasks));
150
151 // Reset bin heads
152 for (uint i = 0; i < NUM_BUCKETS * NUM_BINS_PER_BUCKET; i++)
153 {
155 }
156
157 // Clear nodes
158 for (uint i = 0; i < this->nodes.Size(); i++)
159 {
160 this->freeNodes[i] = this->nodes.Size() - i - 1;
161 }
162 this->freeNodeIterator = this->nodes.Size() - 1;
163 this->freeStorage = 0;
164
165 // Insert node covering the whole range
166 if (this->size > 0)
167 this->InsertNode(this->size, 0);
168}
169
170//------------------------------------------------------------------------------
173inline bool
175{
176 return this->freeStorage == this->size;
177}
178
179//------------------------------------------------------------------------------
182inline RangeAllocation
184{
185 // We are not allowed any more allocations
186 uint alignedSize = size + alignment - 1;
187 if (this->freeStorage < alignedSize)
188 {
190 }
191
192 BinIndex minIndex = IndexFromSize(alignedSize, true);
193 uint bin = 0xFFFFFFFF;
194 uint bucket = minIndex.bucket;
195
196 if (this->bucketUsageMask & (1 << bucket))
197 {
198 bin = Util::Lsb(this->binMasks[bucket], minIndex.bin);
199 }
200
201 if (bin == 0xFFFFFFFF)
202 {
203 // Find next bit set after bucket in the usage mask
204 bucket = Util::Lsb(this->bucketUsageMask, bucket + 1);
205
206 // If this means we get 32, it means we're out of buckets that fit
207 if (bucket == 0xFFFFFFFF)
208 {
210 }
211
212 // Find any bit, since this bucket has to fit
213 bin = Util::FirstOne(this->binMasks[bucket]);
214 }
215
216 BinIndex binIndex;
217 binIndex.bucket = bucket;
218 binIndex.bin = bin;
219
220 // Get linked list head
221 uint nodeIndex = this->binHeads[binIndex.index];
222 RangeAllocatorNode& node = this->nodes[nodeIndex];
223 n_assert(!node.resident);
224
225 // Save total size of node
226 uint totalSize = node.size;
227 uint baseOffset = node.offset;
228 node.size = alignedSize;
229 node.resident = true;
230 node.offset = Math::align(node.offset, alignment);
231
232 // Bump head of bin to next node
233 this->binHeads[binIndex.index] = node.binNext;
235 this->nodes[node.binNext].binPrev = RangeAllocatorNode::END;
236
237 // If bin head is empty after we grab the node, unmark the bin
238 if (this->binHeads[binIndex.index] == RangeAllocatorNode::END)
239 {
240 this->binMasks[binIndex.bucket] &= ~(1 << binIndex.bin);
241
242 // If all bins are busy, unmark the bucket bits
243 if (this->binMasks[binIndex.bucket] == 0)
244 {
245 this->bucketUsageMask &= ~(1 << binIndex.bucket);
246 }
247 }
248
249 // We subtract the entire size of the node
250 this->freeStorage -= totalSize;
251
252 // The remainder in the front is then added back
253 uint remainder = totalSize - node.size;
254 if (remainder > 0)
255 {
256 uint newNodeIndex = this->InsertNode(remainder, node.offset + size);
257 if (newNodeIndex != RangeAllocatorNode::END)
258 {
259 RangeAllocatorNode& newNode = this->nodes[newNodeIndex];
260
261 // If the current node has a next, repoint it to the new block
263 {
264 this->nodes[node.blockNext].blockPrev = newNodeIndex;
265 }
266 newNode.blockPrev = nodeIndex;
267 newNode.blockNext = node.blockNext;
268 node.blockNext = newNodeIndex;
269 }
270 }
271
272 return { node.offset, nodeIndex };
273}
274
275//------------------------------------------------------------------------------
278inline void
280{
281 RangeAllocatorNode& node = this->nodes[allocation.node];
282 n_assert(node.resident);
283 node.resident = false;
284
285 uint size = node.size;
286 uint offset = node.offset;
287
288 // Try to merge with left
290 {
291 RangeAllocatorNode& prev = this->nodes[node.blockPrev];
292 if (!prev.resident)
293 {
294 offset = prev.offset;
295 size += prev.size;
296
297 this->RemoveNode(node.blockPrev);
298 node.blockPrev = prev.blockPrev;
299 }
300 }
301
303 {
304 RangeAllocatorNode& next = this->nodes[node.blockNext];
305 if (!next.resident)
306 {
307 size += next.size;
308 this->RemoveNode(node.blockNext);
309 node.blockNext = next.blockNext;
310 }
311 }
312
313 uint leftNode = node.blockPrev;
314 uint rightNode = node.blockNext;
315 this->freeNodes[++this->freeNodeIterator] = allocation.node;
316
317 // Add merged node
318 uint mergedNode = this->InsertNode(size, offset);
319
320 // Connect adjacent nodes with newly inserted node
321 if (leftNode != RangeAllocatorNode::END)
322 {
323 this->nodes[leftNode].blockNext = mergedNode;
324 this->nodes[mergedNode].blockPrev = leftNode;
325 }
326
327 if (rightNode != RangeAllocatorNode::END)
328 {
329 this->nodes[rightNode].blockPrev = mergedNode;
330 this->nodes[mergedNode].blockNext = rightNode;
331 }
332}
333
334//------------------------------------------------------------------------------
337inline uint
339{
340 if (this->freeNodeIterator == 0xFFFFFFFF)
342
343 BinIndex index = IndexFromSize(size);
344
345 if (this->binHeads[index.index] == RangeAllocatorNode::END)
346 {
347 // Bin isn't used, flip bit for bucket and bin
348 this->binMasks[index.bucket] |= (1 << index.bin);
349 this->bucketUsageMask |= (1 << index.bucket);
350 }
351
352 // Get new node
353 uint headNodeIndex = this->binHeads[index.index];
354 uint newNodeIndex = this->freeNodes[this->freeNodeIterator--];
355 RangeAllocatorNode& newNode = this->nodes[newNodeIndex];
356
357 // Set the size and offset of the node memory
358 newNode.size = size;
359 newNode.offset = offset;
360 newNode.binNext = headNodeIndex;
362
363 this->freeStorage += size;
364
365 // Get node at head of bin
366 if (headNodeIndex != RangeAllocatorNode::END)
367 {
368 // If valid, repoint head node to point to the new node in the bin
369 RangeAllocatorNode& headNode = this->nodes[headNodeIndex];
370 headNode.binPrev = newNodeIndex;
371 }
372 this->binHeads[index.index] = newNodeIndex;
373
374 return newNodeIndex;
375}
376
377//------------------------------------------------------------------------------
380inline void
382{
383 // Get node to free
384 RangeAllocatorNode& node = this->nodes[nodeIndex];
385 node.resident = false;
386
387 // Add back the storage to the allocator
388 this->freeStorage -= node.size;
389
391 {
392 BinIndex index = IndexFromSize(node.size);
393
394 // Point head of
395 this->binHeads[index.index] = node.binNext;
397 {
398 this->nodes[node.binNext].binPrev = RangeAllocatorNode::END;
399 }
400
402 {
403 // If we are deleting the last node, make sure to unmark the bin
404 this->binMasks[index.bucket] &= ~(1 << index.bin);
405
406 if (this->binMasks[index.bucket] == 0x0)
407 {
408 // If all bins have been cleared, clear the entire bucket
409 this->bucketUsageMask &= ~(1 << index.bucket);
410 }
411 }
412 }
413 else
414 {
415 this->nodes[node.binPrev].binNext = node.binNext;
417 {
418 this->nodes[node.binNext].binPrev = node.binPrev;
419 }
420 }
421
422 this->freeNodes[++this->freeNodeIterator] = nodeIndex;
423}
424
425//------------------------------------------------------------------------------
428inline uint
430{
431#if __WIN32__
432 DWORD count = 0;
433 _BitScanReverse(&count, size);
434#else
435 int count = 31 - __builtin_clz(size);
436#endif
437 return count;
438}
439
440//------------------------------------------------------------------------------
443inline uint
444BinFromSize(uint size, uint bucket)
445{
446 uint mask = (size >> (bucket - 4)) & (NUM_BINS_PER_BUCKET - 1u);
447 return mask;
448}
449
450//------------------------------------------------------------------------------
453inline RangeAllocator::BinIndex
455{
458 ret.bin = BinFromSize(size, ret.bucket);
459 if (round)
460 {
461 uint mask = (1 << ret.bucket) | (ret.bin << (ret.bucket - 4));
462 if ((~mask & size) != 0)
463 ret.bin++;
464 }
465 return ret;
466}
467
468} // namespace Memory
Implements helper functions for checking bits.
Definition rangeallocator.h:38
~RangeAllocator()
Destructor.
Definition rangeallocator.h:137
RangeAllocator()
Default constructor.
Definition rangeallocator.h:114
void Clear()
Clear allocator.
Definition rangeallocator.h:145
uint size
Definition rangeallocator.h:98
Util::Array< RangeAllocatorNode > nodes
Definition rangeallocator.h:106
void RemoveNode(uint nodeIndex)
Remove node from bin.
Definition rangeallocator.h:381
uint InsertNode(uint size, uint offset)
Insert node at bin location, return node index.
Definition rangeallocator.h:338
uint bucketUsageMask
Definition rangeallocator.h:102
bool Empty()
Empty.
Definition rangeallocator.h:174
uint freeNodeIterator
Definition rangeallocator.h:100
static BinIndex IndexFromSize(uint size, bool round=false)
Get bin index from size.
Definition rangeallocator.h:454
RangeAllocation Alloc(uint size, uint alignment=1)
Allocate memory.
Definition rangeallocator.h:183
uint binMasks[NUM_BUCKETS]
Definition rangeallocator.h:103
uint freeStorage
Definition rangeallocator.h:99
Util::Array< uint > freeNodes
Definition rangeallocator.h:107
void Dealloc(const RangeAllocation &allocation)
Deallocate memory.
Definition rangeallocator.h:279
uint binHeads[NUM_BUCKETS *NUM_BINS_PER_BUCKET]
Definition rangeallocator.h:104
Nebula's dynamic array class.
Definition array.h:60
void Resize(SizeT num)
Set size. Grows array if num is greater than capacity. Calls destroy on all objects at index > num!
Definition array.h:1604
const SizeT Size() const
get number of elements in array
Definition array.h:878
#define n_assert(exp)
Definition debug.h:50
__forceinline unsigned int align(unsigned int alignant, unsigned int alignment)
Definition scalar.h:722
Allocates memory using the TLSF method (http://www.gii.upv.es/tlsf/files/ecrts04_tlsf....
Definition arenaallocator.h:31
static constexpr uint NUM_BINS_PER_BUCKET
Definition rangeallocator.h:35
static constexpr uint NUM_BUCKETS
Definition rangeallocator.h:34
uint BinFromSize(uint size, uint bucket)
Get bin from index.
Definition rangeallocator.h:444
uint BucketFromSize(uint size)
Get bucket from index.
Definition rangeallocator.h:429
uint Lsb(uint value, byte bit)
Definition bit.h:229
uint FirstOne(uint value)
Definition bit.h:169
Definition rangeallocator.h:27
uint offset
Definition rangeallocator.h:28
static constexpr uint OOM
Definition rangeallocator.h:31
uint node
Definition rangeallocator.h:29
Definition rangeallocator.h:65
uint offset
Definition rangeallocator.h:77
bool resident
Definition rangeallocator.h:75
static constexpr uint END
Definition rangeallocator.h:79
uint blockNext
Definition rangeallocator.h:81
uint size
Definition rangeallocator.h:76
RangeAllocatorNode()
Definition rangeallocator.h:66
uint blockPrev
Definition rangeallocator.h:81
uint binPrev
Definition rangeallocator.h:80
uint binNext
Definition rangeallocator.h:80
int SizeT
Definition types.h:49
unsigned int uint
Definition types.h:31
uint16_t uint16
Definition types.h:40
Definition rangeallocator.h:85
uint16 index
Definition rangeallocator.h:92
uint16 bucket
Definition rangeallocator.h:90
BinIndex()
Definition rangeallocator.h:86
uint16 bin
Definition rangeallocator.h:89