1 module lwdr.refcount; 2 3 pragma(LDC_no_moduleinfo); 4 5 import lwdr : LWDR; 6 import lwdr.tracking; 7 8 // Based on AutoMem 9 10 /// Detects if a type is ref countable (is an interface, class, pointer or dynamic array) 11 enum isRefCountable(T) = 12 is(T == interface) || // interface 13 is(T == class) || // class 14 is(T == U*, U) || // pointer to something 15 is(T == U[], U); // dynamic array 16 17 /++ 18 Reference count for item of type `T`. Intended for use with 19 interfaces, classes, pointers to structs, and arrays. 20 ++/ 21 struct RefCount(T) 22 if(isRefCountable!T) 23 { 24 /// Begin reference counting an existing item 25 this(auto ref T t) 26 { 27 version(LWDR_TrackMem) 28 { 29 bool tracking = isCurrentTracking(); 30 31 if(tracking) 32 disableMemoryTracking(); 33 34 scope(exit) 35 { 36 if(tracking) 37 enableMemoryTracking(); 38 } 39 } 40 41 interior = new Interior(t); 42 } 43 44 /// Create a new item and reference count it 45 this(Args...)(auto ref Args args) 46 { 47 version(LWDR_TrackMem) 48 { 49 bool tracking = isCurrentTracking(); 50 51 if(tracking) 52 disableMemoryTracking(); 53 54 scope(exit) 55 { 56 if(tracking) 57 enableMemoryTracking(); 58 } 59 } 60 61 static if(is(T == class)) 62 interior = new Interior(new T(args)); 63 static if(is(T == U*, U)) 64 { 65 alias root = rootType!T; 66 static if(is(root == struct) || is(root == union)) 67 interior = new Interior(new root(args)); 68 else 69 { 70 interior = new Interior(new root); 71 *interior.item = args[0]; 72 } 73 } 74 static if(is(T == U[], U)) 75 { 76 alias root = rootType!T; 77 interior = new Interior([args]); 78 } 79 } 80 81 /// Copy 82 this(this) 83 { 84 if(interior !is null) 85 inc; 86 } 87 88 ~this() 89 { 90 release; 91 } 92 93 /// Assign from an l-value RefCount 94 void opAssign(ref RefCount other) 95 { 96 if(interior == other.interior) return; 97 if(interior !is null) release; 98 interior = other.interior; 99 inc; 100 } 101 102 /// Dereference the RefCount and access the contained item 103 ref auto opUnary(string s)() inout if(s == "*") 104 { 105 return interior.item; 106 } 107 108 static if(is(T == U[], U)) // if an array 109 { 110 /// Expose array's opSlice and opIndex 111 auto ref opSlice(B, E)(auto ref B b, auto ref E e) 112 { 113 return interior.item[b .. e]; 114 } 115 /// ditto 116 auto ref opIndex(A...)(auto ref A args) 117 { 118 return interior.item[args]; 119 } 120 /// ditto 121 auto ref opIndexAssign(I, V)(auto ref V v, auto ref I i) 122 { 123 return interior.item[i] = v; 124 } 125 } 126 else 127 { 128 /// Expose item's opSlice and opIndex 129 auto ref opSlice(A...)(auto ref A args) 130 if(__traits(compiles, (rootType!T).init.opSlice(args))) 131 { 132 return interior.item.opSlice(args); 133 } 134 /// ditto 135 auto ref opIndex(A...)(auto ref A args) 136 if(__traits(compiles, (rootType!T).init.opIndex(args))) 137 { 138 return interior.item.opIndex(args); 139 } 140 /// ditto 141 auto ref opIndexAssign(A...)(auto ref A args) 142 if(__traits(compiles, (rootType!T).init.opIndexAssign(args))) 143 { 144 return interior.item.opIndexAssign(args); 145 } 146 } 147 148 /// A heap allocated struct which contains the item and number of references 149 static struct Interior 150 { 151 static if(is(T == shared)) 152 shared size_t count; 153 else 154 size_t count; 155 156 T item; 157 alias item this; 158 159 private this(T item) 160 { 161 count = 1; 162 this.item = item; 163 } 164 165 /// Increment number of references 166 private void inc() 167 { 168 static if(is(T == shared)) 169 { 170 import core.atomic : atomicOp; 171 count.atomicOp!"+="(1); 172 } 173 else 174 count++; 175 } 176 177 /// Decrement number of references 178 private void dec() 179 { 180 static if(is(T == shared)) 181 { 182 import core.atomic : atomicOp; 183 count.atomicOp!"-="(1); 184 } 185 else 186 count--; 187 } 188 } 189 190 static if(is(T == shared)) 191 shared Interior* interior; 192 else 193 Interior* interior; 194 alias interior this; 195 196 /// Release reference to the interior. If count is zero, then deallocate 197 private void release() 198 { 199 if(interior is null) return; 200 assert(interior.count > 0, "Try to release RefCount but count is <=0"); 201 202 dec; 203 204 if(interior.count == 0) 205 LWDR.free(interior.item); 206 } 207 }