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