1 /** 2 * Dynamic Array 3 * Copyright: © 2014 Economic Modeling Specialists, Intl. 4 * Authors: Brian Schott 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 8 module containers.dynamicarray; 9 10 /** 11 * Array that is able to grow itself when items are appended to it. Uses 12 * reference counting to manage memory and malloc/free/realloc for managing its 13 * storage. 14 * Params: 15 * T = the array element type 16 * supportGC = true if the container should support holding references to 17 * GC-allocated memory. 18 */ 19 struct DynamicArray(T, bool supportGC = true) 20 { 21 import std.traits : hasMember; 22 this(this) @disable; 23 24 ~this() 25 { 26 import std.experimental.allocator.mallocator : Mallocator; 27 import containers.internal.node : shouldAddGCRange; 28 29 if (arr is null) 30 return; 31 foreach (ref item; arr[0 .. l]) 32 { 33 static if (is(T == class)) 34 destroy(item); 35 else 36 typeid(T).destroy(&item); 37 } 38 static if (shouldAddGCRange!T) 39 { 40 import core.memory : GC; 41 GC.removeRange(arr.ptr); 42 } 43 Mallocator.instance.deallocate(arr); 44 } 45 46 /// Slice operator overload 47 T[] opSlice() @nogc 48 { 49 return arr[0 .. l]; 50 } 51 52 /// ditto 53 T[] opSlice(size_t a, size_t b) @nogc 54 { 55 return arr[a .. b]; 56 } 57 58 /// Index operator overload 59 T opIndex(size_t i) @nogc 60 { 61 return arr[i]; 62 } 63 64 /** 65 * Inserts the given value into the end of the array. 66 */ 67 void insert(T value) 68 { 69 import std.experimental.allocator.mallocator : Mallocator; 70 import containers.internal.node : shouldAddGCRange; 71 72 if (arr.length == 0) 73 { 74 arr = cast(T[]) Mallocator.instance.allocate(T.sizeof * 4); 75 static if (supportGC && shouldAddGCRange!T) 76 { 77 import core.memory: GC; 78 GC.addRange(arr.ptr, arr.length * T.sizeof); 79 } 80 } 81 else if (l >= arr.length) 82 { 83 immutable size_t c = arr.length > 512 ? arr.length + 1024 : arr.length << 1; 84 void[] a = cast(void[]) arr; 85 Mallocator.instance.reallocate(a, c * T.sizeof); 86 arr = cast(T[]) a; 87 static if (supportGC && shouldAddGCRange!T) 88 { 89 import core.memory: GC; 90 GC.removeRange(arr.ptr); 91 GC.addRange(arr.ptr, arr.length * T.sizeof); 92 } 93 } 94 arr[l++] = value; 95 } 96 97 /// ditto 98 alias put = insert; 99 100 void remove(const size_t i) 101 { 102 if (i < this.l) 103 { 104 static if (is(T == class)) 105 destroy(arr[i]); 106 else 107 typeid(T).destroy(&arr[i]); 108 109 auto next = i + 1; 110 while (next < this.l) 111 { 112 arr[next - 1] = arr[next]; 113 ++next; 114 } 115 116 --l; 117 } 118 else 119 { 120 import core.exception : RangeError; 121 throw new RangeError("Out of range index used to remove element"); 122 } 123 } 124 125 /// Index assignment support 126 void opIndexAssign(T value, size_t i) @nogc 127 { 128 arr[i] = value; 129 } 130 131 /// Slice assignment support 132 void opSliceAssign(T value) @nogc 133 { 134 arr[0 .. l] = value; 135 } 136 137 /// ditto 138 void opSliceAssign(T value, size_t i, size_t j) @nogc 139 { 140 arr[i .. j] = value; 141 } 142 143 /// Returns: the number of items in the array 144 size_t length() const nothrow pure @property @safe @nogc { return l; } 145 146 /** 147 * Returns: a slice to the underlying array. 148 * 149 * As the memory of the array may be freed, access to this array is 150 * highly unsafe. 151 */ 152 @property T* ptr() @nogc { return arr.ptr; } 153 154 private: 155 156 T[] arr; 157 size_t l; 158 } 159 160 unittest 161 { 162 import std.algorithm : equal; 163 import std.range : iota; 164 DynamicArray!int ints; 165 foreach (i; 0 .. 100) 166 ints.insert(i); 167 assert (equal(ints[], iota(100))); 168 assert (ints.length == 100); 169 ints[0] = 100; 170 assert (ints[0] == 100); 171 ints[0 .. 5] = 20; 172 foreach (i; ints[0 .. 5]) 173 assert (i == 20); 174 ints[] = 432; 175 foreach (i; ints[]) 176 assert (i == 432); 177 178 auto arr = ints.ptr; 179 arr[0] = 1337; 180 assert(arr[0] == 1337); 181 assert(ints[0] == 1337); 182 } 183 184 version(unittest) 185 { 186 class Cls 187 { 188 int* a; 189 190 this(int* a) 191 { 192 this.a = a; 193 } 194 195 ~this() 196 { 197 ++(*a); 198 } 199 } 200 } 201 202 unittest 203 { 204 int a = 0; 205 { 206 DynamicArray!(Cls) arr; 207 arr.insert(new Cls( & a)); 208 } 209 assert(a == 1); 210 } 211 212 unittest 213 { 214 import std.exception : assertThrown; 215 import core.exception : RangeError; 216 DynamicArray!int empty; 217 assertThrown!RangeError(empty.remove(1337)); 218 assert(empty.length == 0); 219 220 DynamicArray!int one; 221 one.insert(0); 222 assert(one.length == 1); 223 assertThrown!RangeError(one.remove(1337)); 224 assert(one.length == 1); 225 one.remove(0); 226 assert(one.length == 0); 227 228 DynamicArray!int two; 229 two.insert(0); 230 two.insert(1); 231 assert(two.length == 2); 232 assertThrown!RangeError(two.remove(1337)); 233 assert(two.length == 2); 234 two.remove(0); 235 assert(two.length == 1); 236 assert(two[0] == 1); 237 two.remove(0); 238 assert(two.length == 0); 239 240 two.insert(0); 241 two.insert(1); 242 assert(two.length == 2); 243 244 two.remove(1); 245 assert(two.length == 1); 246 assert(two[0] == 0); 247 assertThrown!RangeError(two.remove(1)); 248 assert(two.length == 1); 249 assert(two[0] == 0); 250 two.remove(0); 251 assert(two.length == 0); 252 } 253 254 unittest 255 { 256 int a = 0; 257 DynamicArray!(Cls,true) arr; 258 arr.insert(new Cls(&a)); 259 260 arr.remove(0); 261 assert(a == 1); 262 } 263 264 unittest 265 { 266 DynamicArray!(int*,true) arr; 267 arr.insert(new int(1)); 268 }