1 /** 2 * Region storage allocator implementation. 3 * 4 * Copyright: Copyright (C) 2019-2021 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d, root/_region.d) 8 * Documentation: https://dlang.org/phobos/dmd_root_region.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/region.d 10 */ 11 12 module dmd.root.region; 13 14 import core.stdc.stdio; 15 import core.stdc.string; 16 import core.stdc.stdlib; 17 18 import dmd.root.rmem; 19 import dmd.root.array; 20 21 /***** 22 * Simple region storage allocator. 23 */ 24 struct Region 25 { 26 nothrow: 27 private: 28 29 Array!(void*) array; // array of chunks 30 int used; // number of chunks used in array[] 31 void[] available; // slice of chunk that's available to allocate 32 33 enum ChunkSize = 4096 * 1024; 34 enum MaxAllocSize = ChunkSize; 35 36 struct RegionPos 37 { 38 int used; 39 void[] available; 40 } 41 42 public: 43 44 /****** 45 * Allocate nbytes. Aborts on failure. 46 * Params: 47 * nbytes = number of bytes to allocate, can be 0, must be <= than MaxAllocSize 48 * Returns: 49 * allocated data, null for nbytes==0 50 */ 51 void* malloc(size_t nbytes) 52 { 53 if (!nbytes) 54 return null; 55 56 nbytes = (nbytes + 15) & ~15; 57 if (nbytes > available.length) 58 { 59 assert(nbytes <= MaxAllocSize); 60 if (used == array.length) 61 { 62 auto h = Mem.check(.malloc(ChunkSize)); 63 array.push(h); 64 } 65 66 available = array[used][0 .. MaxAllocSize]; 67 ++used; 68 } 69 70 auto p = available.ptr; 71 available = (p + nbytes)[0 .. available.length - nbytes]; 72 return p; 73 } 74 75 /**************************** 76 * Return stack position for allocations in this region. 77 * Returns: 78 * an opaque struct to be passed to `release()` 79 */ 80 RegionPos savePos() 81 { 82 return RegionPos(used, available); 83 } 84 85 /******************** 86 * Release the memory that was allocated after the respective call to `savePos()`. 87 * Params: 88 * pos = position returned by `savePos()` 89 */ 90 void release(RegionPos pos) 91 { 92 version (all) 93 { 94 /* Recycle the memory. There better not be 95 * any live pointers to it. 96 */ 97 used = pos.used; 98 available = pos.available; 99 } 100 else 101 { 102 /* Instead of recycling the memory, stomp on it 103 * to flush out any remaining live pointers to it. 104 */ 105 (cast(ubyte[])pos.available)[] = 0xFF; 106 foreach (h; array[pos.used .. used]) 107 (cast(ubyte*)h)[0 .. ChunkSize] = 0xFF; 108 } 109 } 110 111 /**************************** 112 * If pointer points into Region. 113 * Params: 114 * p = pointer to check 115 * Returns: 116 * true if it points into the region 117 */ 118 bool contains(void* p) 119 { 120 foreach (h; array[0 .. used]) 121 { 122 if (h <= p && p < h + ChunkSize) 123 return true; 124 } 125 return false; 126 } 127 128 /********************* 129 * Returns: size of Region 130 */ 131 size_t size() 132 { 133 return used * MaxAllocSize - available.length; 134 } 135 } 136 137 138 unittest 139 { 140 Region reg; 141 auto rgnpos = reg.savePos(); 142 143 void* p = reg.malloc(0); 144 assert(p == null); 145 assert(!reg.contains(p)); 146 147 p = reg.malloc(100); 148 assert(p !is null); 149 assert(reg.contains(p)); 150 memset(p, 0, 100); 151 152 p = reg.malloc(100); 153 assert(p !is null); 154 assert(reg.contains(p)); 155 memset(p, 0, 100); 156 157 assert(reg.size() > 0); 158 assert(!reg.contains(®)); 159 160 reg.release(rgnpos); 161 }