1 /** 2 Stack allocator for temporary allocations. 3 4 Copyright: Auburn Sounds 2019. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module dplug.core.stackallocator; 8 9 import core.stdc.stdlib: malloc, free; 10 import dplug.core.vec; 11 12 struct StackAllocator 13 { 14 private: 15 Vec!(ubyte*) bucketArray; 16 uint numUsedPages; 17 uint currentPageFreeBytes; 18 enum PAGE_SIZE = 1024 * 1024 * 1024; // 1MB 19 20 struct State 21 { 22 uint savedNumUsedPages; 23 uint savedCurrentPageFreeBytes; 24 } 25 26 public: 27 28 @disable this(this); // non copyable 29 30 ~this() 31 { 32 foreach(ubyte* bucket; bucketArray) 33 free(bucket); 34 } 35 36 /// Save allocation state 37 State saveState() 38 { 39 return State(numUsedPages, currentPageFreeBytes); 40 } 41 42 /// Pop allocation state 43 void restoreState(State state) 44 { 45 numUsedPages = state.savedNumUsedPages; 46 currentPageFreeBytes = state.savedCurrentPageFreeBytes; 47 } 48 49 /// return pointer to len x T.sizeof bytes of uninitialized memory 50 T[] makeArray(T)(size_t len) 51 { 52 size_t allocSize = len * T.sizeof; 53 assert(allocSize <= PAGE_SIZE, "Requested size is bigger that page size"); 54 55 if (currentPageFreeBytes < allocSize) 56 setupNextPage; 57 58 size_t nextByte = PAGE_SIZE - currentPageFreeBytes; 59 currentPageFreeBytes -= allocSize; 60 61 ubyte* pagePtr = bucketArray[numUsedPages-1]; 62 ubyte[] bytes = pagePtr[nextByte..nextByte+allocSize]; 63 64 return cast(T[])bytes; 65 } 66 67 private void setupNextPage() 68 { 69 if (numUsedPages == bucketArray.length) 70 { 71 ubyte* newBucket = cast(ubyte*)malloc(PAGE_SIZE); 72 bucketArray.pushBack(newBucket); 73 } 74 // alloc from new page 75 ++numUsedPages; 76 currentPageFreeBytes = PAGE_SIZE; 77 } 78 } 79 80 unittest 81 { 82 StackAllocator allocator; 83 auto saved = allocator.saveState; 84 uint[] arr = allocator.makeArray!uint(10); 85 arr[] = 42; 86 assert(arr.length == 10); 87 allocator.restoreState(saved); 88 89 uint[] arr2 = allocator.makeArray!uint(10); 90 arr2[] = 48; 91 92 assert(arr[0] == 48); 93 94 // multiple allocations 95 uint[] arr3 = allocator.makeArray!uint(10); 96 arr3[] = 60; 97 98 // doesn't overwrite arr2 99 assert(arr2[0] == 48); 100 assert(arr2[$-1] == 48); 101 102 allocator.restoreState(saved); 103 104 // test new page allocation 105 allocator.makeArray!uint(1); 106 allocator.makeArray!uint(StackAllocator.PAGE_SIZE / uint.sizeof); 107 108 allocator.restoreState(saved); 109 110 // test page reuse 111 allocator.makeArray!uint(StackAllocator.PAGE_SIZE / uint.sizeof); 112 }