1 /// Wrappers for automatically managing memory. 2 module tern.typecons.automem; 3 4 import tern.typecons.security; 5 import tern.traits; 6 import tern.meta; 7 import std.experimental.allocator; 8 import std.typecons; 9 import std.conv; 10 11 private alias NULL = typeof(null); 12 /// Automatically releases a pointer of type `T` when exiting scope, assumes no ownership is passed. 13 public class Unique(T, alias FREE = typeof(null)) 14 { 15 T* ptr; 16 alias ptr this; 17 18 public: 19 final: 20 this(P : U*, U)(P ptr) 21 { 22 this.ptr = cast(T*)ptr; 23 } 24 25 auto opAssign(A)(A ahs) 26 { 27 static assert(0, "Cannot reassign a Unique!"); 28 } 29 30 /// Releases/frees this manager. Implementation defined. 31 void release() 32 { 33 void[] arr; 34 (cast(size_t*)&arr)[0] = T.sizeof; 35 (cast(void**)&arr)[1] = cast(void*)ptr; 36 37 if (theAllocator.owns(arr) == Ternary.yes) 38 theAllocator.deallocate(arr); 39 else if (!is(FREE == NULL)) 40 FREE(ptr); 41 else 42 { 43 if (ptr !is null) 44 destroy(*ptr); 45 46 destroy(ptr); 47 } 48 } 49 50 ~this() 51 { 52 release(); 53 } 54 } 55 56 /// Helper function for creating a unique. 57 Unique!T unique(T : U*, U)(T ptr) 58 { 59 return new Unique!T(ptr); 60 } 61 62 /// Automatically releases a pointer of type `T` when exiting scope, allows any kind of ownership, but does not ref count. 63 public class Scoped(T, alias FREE = typeof(null)) 64 { 65 T* ptr; 66 alias ptr this; 67 68 public: 69 final: 70 this(P : U*, U)(P ptr) 71 { 72 this.ptr = cast(T*)ptr; 73 } 74 75 /// Releases/frees this manager. Implementation defined. 76 void release() 77 { 78 void[] arr; 79 (cast(size_t*)&arr)[0] = T.sizeof; 80 (cast(void**)&arr)[1] = cast(void*)ptr; 81 82 if (theAllocator.owns(arr) == Ternary.yes) 83 theAllocator.deallocate(arr); 84 else if (!is(FREE == NULL)) 85 FREE(ptr); 86 else 87 { 88 if (ptr !is null) 89 destroy(*ptr); 90 91 destroy(ptr); 92 } 93 } 94 95 ~this() 96 { 97 release(); 98 } 99 } 100 101 /// Helper function for creating a scoped. 102 Scoped!T scoped(T : U*, U)(T ptr) 103 { 104 return new Scoped!T(ptr); 105 } 106 107 /// Counts references to the origin `RefCounted` and only releases if all references have also destructed. 108 public class RefCounted(T, alias FREE = typeof(null)) 109 { 110 T* ptr; 111 alias ptr this; 112 113 public: 114 final: 115 shared Atomic!ptrdiff_t refs; 116 shared(Atomic!(ptrdiff_t))* pref; 117 118 this(P : U*, U)(P ptr) 119 { 120 this.ptr = cast(T*)ptr; 121 this.refs = 1; 122 this.pref = &refs; 123 } 124 125 this(P : RefCounted, U)(P ptr) 126 { 127 this.ptr = cast(T*)ptr.ptr; 128 this.pref = ptr.pref; 129 (*pref)++; 130 } 131 132 auto opUnary(string op)() 133 { 134 static assert (op != "*" || !is(T == class), "Cannot dereference a RefCounted(T) where T is a class!"); 135 return mixin(op~"ptr"); 136 } 137 138 auto opAssign(A)(A ahs) 139 if (is(A : U*, U) || is(A : RefCounted, U)) 140 { 141 static if (is(A == RefCounted)) 142 { 143 this.ptr = cast(T*)ahs.ptr; 144 this.pref = ahs.pref; 145 (*pref)++; 146 } 147 else 148 { 149 this.ptr = cast(T*)ahs; 150 this.refs = 1; 151 this.pref = &refs; 152 } 153 return this; 154 } 155 156 /// Releases/frees this manager. Implementation defined. 157 void release() 158 { 159 (*pref)--; 160 if (*pref <= 0) 161 { 162 void[] arr; 163 (cast(size_t*)&arr)[0] = T.sizeof; 164 (cast(void**)&arr)[1] = cast(void*)ptr; 165 166 if (theAllocator.owns(arr) == Ternary.yes) 167 theAllocator.deallocate(arr); 168 else if (!is(FREE == NULL)) 169 FREE(ptr); 170 else 171 { 172 if (ptr !is null) 173 destroy(*ptr); 174 175 destroy(ptr); 176 } 177 } 178 } 179 180 ~this() 181 { 182 release(); 183 } 184 } 185 186 /// Helper function for creating a ref counted. 187 RefCounted!T refCounted(T : U*, U)(T ptr) 188 { 189 return RefCounted!T(ptr); 190 } 191 192 private void*[] tracking; 193 /// Stores all pointers and sweeps through the array after a `Tracked` destructs. 194 public class Tracked(T, alias FREE = typeof(null)) 195 if (is(T == class)) 196 { 197 T* ptr; 198 alias ptr this; 199 200 public: 201 final: 202 this(P : U*, U)(P ptr) 203 { 204 this.ptr = cast(T*)ptr; 205 tracking ~= cast(void*)ptr; 206 } 207 208 ~this() 209 { 210 foreach (ref ptr; tracking) 211 { 212 if (ptr != null && ptr.isNull) 213 { 214 void[] arr; 215 (cast(size_t*)&arr)[0] = T.sizeof; 216 (cast(void**)&arr)[1] = cast(void*)ptr; 217 218 if (theAllocator.owns(arr) == Ternary.yes) 219 theAllocator.deallocate(arr); 220 else if (!is(FREE == NULL)) 221 FREE(ptr); 222 else 223 { 224 if (ptr !is null) 225 destroy(*ptr); 226 227 destroy(ptr); 228 } 229 } 230 ptr = null; 231 } 232 } 233 } 234 235 /// Helper function for creating a tracked. 236 Tracked!T tracked(T : U*, U)(T ptr) 237 if (is(T == class)) 238 { 239 return new Tracked!T(ptr); 240 } 241 242 /// Automatically disposing wrapper, will attempt to dispose by calling `close`, `dispose`, or `destroy` if all else fails. 243 public class Disposable(T) 244 { 245 T value; 246 alias value this; 247 248 public: 249 final: 250 this(T val) 251 { 252 value = val; 253 } 254 255 /// Releases/frees this manager. Implementation defined. 256 void release() 257 { 258 static if (seqContains!("close", Functions!T)) 259 { 260 static assert(Parameters!(TypeOf!(T, "close")).length == 0, "Close function expected to have no parameters!"); 261 mixin("value.close();"); 262 } 263 else static if (seqContains!("dispose", Functions!T)) 264 { 265 static assert(Parameters!(TypeOf!(T, "dispose")).length == 0, "Dispose function expected to have no parameters!"); 266 mixin("value.dispose();"); 267 } 268 else 269 destroy(value); 270 } 271 272 ~this() 273 { 274 release(); 275 } 276 } 277 278 /// Helper function for creating a disposable. 279 Disposable!T disposable(T)(T val) 280 { 281 return new Disposable!T(val); 282 }