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