1 /** 2 RAII arrays 3 */ 4 module automem.unique_array; 5 6 import automem.traits: isAllocator; 7 import stdx.allocator: theAllocator; 8 9 10 /** 11 A unique array similar to C++'s std::unique_ptr<T> when T is an array 12 */ 13 struct UniqueArray(Type, Allocator = typeof(theAllocator)) if(isAllocator!Allocator) { 14 15 import std.traits: hasMember, isScalarType; 16 import std.range: isInputRange; 17 18 enum isSingleton = hasMember!(Allocator, "instance"); 19 enum isTheAllocator = is(Allocator == typeof(theAllocator)); 20 enum isGlobal = isSingleton || isTheAllocator; 21 22 static if(isGlobal) { 23 24 /** 25 The allocator is global, so no need to pass it in to the 26 constructor 27 */ 28 29 this(size_t size) { 30 makeObjects(size); 31 } 32 33 this(size_t size, Type init) { 34 makeObjects(size, init); 35 } 36 37 this(R)(R range) if(isInputRange!R) { 38 makeObjects(range); 39 } 40 41 42 } else { 43 44 /** 45 Non-singleton allocator, must be passed in 46 */ 47 48 this(Allocator allocator) { 49 _allocator = allocator; 50 } 51 52 this(Allocator allocator, size_t size) { 53 _allocator = allocator; 54 makeObjects(size); 55 } 56 57 this(Allocator allocator, size_t size, Type init) { 58 _allocator = allocator; 59 makeObjects(size, init); 60 } 61 62 this(R)(Allocator allocator, R range) if(isInputRange!R) { 63 _allocator = allocator; 64 makeObjects(range); 65 } 66 } 67 68 69 this(T)(UniqueArray!(T, Allocator) other) if(is(T: Type[])) { 70 moveFrom(other); 71 } 72 73 @disable this(this); 74 75 ~this() { 76 deleteObjects; 77 } 78 79 /** 80 Releases ownership and transfers it to the returned 81 Unique object. 82 */ 83 UniqueArray unique() { 84 import std.algorithm: move; 85 UniqueArray u; 86 move(this, u); 87 assert(_objects.length == 0 && _objects.ptr is null); 88 return u; 89 } 90 alias move = unique; 91 92 /** 93 "Truthiness" cast 94 */ 95 bool opCast(T)() const if(is(T == bool)) { 96 return _objects.ptr !is null; 97 } 98 99 void opAssign(T)(UniqueArray!(T, Allocator) other) if(is(T: Type[])) { 100 deleteObject; 101 moveFrom(other); 102 } 103 104 ref inout(Type) opIndex(long i) inout nothrow { 105 return _objects[i]; 106 } 107 108 const(Type)[] opSlice(long i, long j) const nothrow { 109 return _objects[i .. j]; 110 } 111 112 const(Type)[] opSlice() const nothrow { 113 return _objects[0 .. length]; 114 } 115 116 long opDollar() const nothrow { 117 return length; 118 } 119 120 @property long length() const nothrow { 121 return _length; 122 } 123 124 @property void length(long size) { 125 126 import stdx.allocator: expandArray, shrinkArray; 127 128 if(_objects is null) { 129 makeObjects(size); 130 } else if(size == length) { 131 return; 132 } else if(size <= _capacity && size > length) { 133 foreach(ref obj; _objects[_length .. size]) 134 obj = obj.init; 135 _length = size; 136 } else if(size < length) { 137 _length = size; 138 } else { 139 if(size > length) { 140 _allocator.expandArray(_objects, size - length); 141 setLength; 142 } else 143 assert(0); 144 } 145 } 146 147 /** 148 Dereference. const since this otherwise could be used to try 149 and append to the array, which would not be nice 150 */ 151 const(Type[]) opUnary(string s)() const if(s == "*") { 152 return this[]; 153 } 154 155 /** 156 Append to the array 157 */ 158 UniqueArray opBinary(string s)(UniqueArray other) if(s == "~") { 159 this ~= other.unique; 160 return this.unique; 161 } 162 163 /// Append to the array 164 void opOpAssign(string op)(Type other) if(op == "~") { 165 length(length + 1); 166 _objects[$ - 1] = other; 167 } 168 169 /// Append to the array 170 void opOpAssign(string op)(Type[] other) if(op == "~") { 171 const originalLength = length; 172 length(originalLength + other.length); 173 _objects[originalLength .. length] = other[]; 174 } 175 176 /// Append to the array 177 void opOpAssign(string op)(UniqueArray other) if(op == "~") { 178 this ~= other._objects; 179 } 180 181 /// Assign from a slice. 182 void opAssign(Type[] other) { 183 length = other.length; 184 _objects[0 .. length] = other[0 .. length]; 185 } 186 187 /** 188 Reserves memory to prevent too many allocations 189 */ 190 void reserve(in long size) { 191 import stdx.allocator: expandArray; 192 193 if(_objects is null) { 194 const oldLength = length; 195 makeObjects(size); // length = capacity here 196 _length = oldLength; 197 return; 198 } 199 200 if(size < _capacity) { 201 if(size < _length) _length = size; 202 return; 203 } 204 205 _capacity = size; 206 _allocator.expandArray(_objects, _capacity); 207 } 208 209 /** 210 Returns a pointer to the underlying data. @system 211 */ 212 inout(Type)* ptr() inout { 213 return _objects.ptr; 214 } 215 216 static if(isGlobal) { 217 UniqueArray dup() const { 218 return UniqueArray(_objects); 219 } 220 } else static if(isScalarType!Allocator && is(typeof(() { auto a = Allocator.init; auto b = a; }))) { 221 UniqueArray dup() const { 222 return UniqueArray(_allocator, _objects); 223 } 224 } else { 225 UniqueArray dup() { 226 return UniqueArray(_allocator, _objects); 227 } 228 } 229 230 private: 231 232 Type[] _objects; 233 long _length; 234 long _capacity; 235 236 static if(isSingleton) 237 alias _allocator = Allocator.instance; 238 else static if(isTheAllocator) 239 alias _allocator = theAllocator; 240 else 241 Allocator _allocator; 242 243 void makeObjects(size_t size) { 244 import stdx.allocator: makeArray; 245 _objects = _allocator.makeArray!Type(size); 246 setLength; 247 } 248 249 void makeObjects(size_t size, Type init) { 250 import stdx.allocator: makeArray; 251 _objects = _allocator.makeArray!Type(size, init); 252 setLength; 253 254 } 255 256 void makeObjects(R)(R range) if(isInputRange!R) { 257 import stdx.allocator: makeArray; 258 _objects = _allocator.makeArray!Type(range); 259 setLength; 260 } 261 262 void setLength() { 263 _capacity = _length = _objects.length; 264 } 265 266 void deleteObjects() { 267 import stdx.allocator: dispose; 268 import std.traits: isPointer; 269 270 static if(isPointer!Allocator) 271 assert((_objects.length == 0 && _objects.ptr is null) || _allocator !is null); 272 273 if(_objects.ptr !is null) _allocator.dispose(_objects); 274 _length = 0; 275 } 276 277 void moveFrom(T)(ref UniqueArray!(T, Allocator) other) if(is(T: Type[])) { 278 import std.algorithm: swap; 279 _object = other._object; 280 other._object = null; 281 282 swap(_length, other._length); 283 swap(_capacity, other._capacity); 284 285 static if(!isGlobal) { 286 import std.algorithm: move; 287 _allocator = other._allocator.move; 288 } 289 } 290 }