1 module mutils.container.vector; 2 3 import core.bitop; 4 import core.stdc.stdlib : free, malloc; 5 import core.stdc..string : memcpy, memset; 6 import std.algorithm : swap; 7 import std.conv : emplace; 8 import std.traits : hasMember, isCopyable, TemplateOf, Unqual; 9 10 import mutils.stdio; 11 12 @nogc @safe nothrow pure size_t nextPow2(size_t num) { 13 return 1 << bsr(num) + 1; 14 } 15 16 __gshared size_t gVectorsCreated = 0; 17 __gshared size_t gVectorsDestroyed = 0; 18 19 struct Vector(T) { 20 T[] array; 21 size_t used; 22 public: 23 24 this()(T t) { 25 add(t); 26 } 27 28 this(X)(X[] t) if (is(Unqual!X == Unqual!T)) { 29 add(t); 30 31 } 32 33 static if (isCopyable!T) { 34 this(this) { 35 T[] tmp = array[0 .. used]; 36 array = null; 37 used = 0; 38 add(tmp); 39 } 40 } else { 41 @disable this(this); 42 } 43 44 ~this() { 45 clear(); 46 } 47 48 void clear() { 49 removeAll(); 50 } 51 52 void removeAll() { 53 if (array !is null) { 54 foreach (ref el; array[0 .. used]) { 55 destroy(el); 56 } 57 freeData(cast(void[]) array); 58 gVectorsDestroyed++; 59 } 60 array = null; 61 used = 0; 62 } 63 64 bool empty() { 65 return (used == 0); 66 } 67 68 size_t length() { 69 return used; 70 } 71 72 void length(size_t newLength) { 73 if (newLength > used) { 74 reserve(newLength); 75 foreach (ref el; array[used .. newLength]) { 76 emplace(&el); 77 } 78 } else { 79 foreach (ref el; array[newLength .. used]) { 80 destroy(el); 81 } 82 } 83 used = newLength; 84 } 85 86 void reset() { 87 used = 0; 88 } 89 90 void reserve(size_t numElements) { 91 if (numElements > array.length) { 92 extend(numElements); 93 } 94 } 95 96 size_t capacity() { 97 return array.length - used; 98 } 99 100 void extend(size_t newNumOfElements) { 101 auto oldArray = manualExtend(array, newNumOfElements); 102 if (oldArray !is null) { 103 freeData(oldArray); 104 } 105 } 106 107 @nogc void freeData(void[] data) { 108 // 0x0F probably invalid value for pointers and other types 109 memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD 110 free(data.ptr); 111 } 112 113 static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) { 114 if (newNumOfElements == 0) 115 newNumOfElements = 2; 116 if (array.length == 0) 117 gVectorsCreated++; 118 T[] oldArray = array; 119 size_t oldSize = oldArray.length * T.sizeof; 120 size_t newSize = newNumOfElements * T.sizeof; 121 T* memory = cast(T*) malloc(newSize); 122 memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize); 123 array = memory[0 .. newNumOfElements]; 124 return cast(void[]) oldArray; 125 } 126 127 Vector!T copy()() { 128 Vector!T duplicate; 129 duplicate.reserve(used); 130 duplicate ~= array[0 .. used]; 131 return duplicate; 132 } 133 134 bool canAddWithoutRealloc(uint elemNum = 1) { 135 return used + elemNum <= array.length; 136 } 137 138 void add()(T t) { 139 if (used >= array.length) { 140 extend(nextPow2(used + 1)); 141 } 142 emplace(&array[used], t); 143 used++; 144 } 145 146 /// Add element at given position moving others 147 void add()(T t, size_t pos) { 148 assert(pos <= used); 149 if (used >= array.length) { 150 extend(array.length * 2); 151 } 152 foreach_reverse (size_t i; pos .. used) { 153 swap(array[i + 1], array[i]); 154 } 155 emplace(&array[pos], t); 156 used++; 157 } 158 159 void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) { 160 if (used + t.length > array.length) { 161 extend(nextPow2(used + t.length)); 162 } 163 foreach (i; 0 .. t.length) { 164 emplace(&array[used + i], t[i]); 165 } 166 used += t.length; 167 } 168 169 void remove(size_t elemNum) { 170 destroy(array[elemNum]); 171 swap(array[elemNum], array[used - 1]); 172 used--; 173 } 174 175 void removeStable()(size_t elemNum) { 176 used--; 177 foreach (i; 0 .. used) { 178 array[i] = array[i + 1]; 179 } 180 } 181 182 bool tryRemoveElement()(T elem) { 183 foreach (i, ref el; array[0 .. used]) { 184 if (el == elem) { 185 remove(i); 186 return true; 187 } 188 } 189 return false; 190 } 191 192 void removeElement()(T elem) { 193 bool ok = tryRemoveElement(elem); 194 assert(ok, "There is no such an element in vector"); 195 } 196 197 ref T opIndex(size_t elemNum) { 198 assert(elemNum < used, "Range violation [index]"); 199 return array.ptr[elemNum]; 200 } 201 202 auto opSlice() { 203 return array.ptr[0 .. used]; 204 } 205 206 T[] opSlice(size_t x, size_t y) { 207 assert(y <= used); 208 return array.ptr[x .. y]; 209 } 210 211 size_t opDollar() { 212 return used; 213 } 214 215 void opAssign(X)(X[] slice) { 216 reset(); 217 this ~= slice; 218 } 219 220 void opOpAssign(string op)(T obj) { 221 static assert(op == "~"); 222 add(obj); 223 } 224 225 void opOpAssign(string op, X)(X[] obj) { 226 static assert(op == "~"); 227 add(obj); 228 } 229 230 void opIndexAssign()(T obj, size_t elemNum) { 231 assert(elemNum < used, "Range viloation"); 232 array[elemNum] = obj; 233 } 234 235 void opSliceAssign()(T[] obj, size_t a, size_t b) { 236 assert(b <= used && a <= b, "Range viloation"); 237 array.ptr[a .. b] = obj; 238 } 239 240 bool opEquals()(auto ref const Vector!(T) r) const { 241 return used == r.used && array.ptr[0 .. used] == r.array.ptr[0 .. r.used]; 242 } 243 244 size_t toHash() const nothrow @trusted { 245 return hashOf(cast(Unqual!(T)[]) array.ptr[0 .. used]); 246 } 247 248 import std.format : FormatSpec, formatValue; 249 250 /** 251 * Preety print 252 */ 253 void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) { 254 static if (__traits(compiles, formatValue(sink, array[0 .. used], fmt))) { 255 formatValue(sink, array[0 .. used], fmt); 256 } 257 } 258 259 } 260 261 // Helper to avoid GC 262 private T[n] s(T, size_t n)(auto ref T[n] array) pure nothrow @nogc @safe { 263 return array; 264 } 265 266 @nogc nothrow unittest { 267 Vector!int vec; 268 assert(vec.empty); 269 vec.add(0); 270 vec.add(1); 271 vec.add(2); 272 vec.add(3); 273 vec.add(4); 274 vec.add(5); 275 assert(vec.length == 6); 276 assert(vec[3] == 3); 277 assert(vec[5] == 5); 278 assert(vec[] == [0, 1, 2, 3, 4, 5].s); 279 assert(!vec.empty); 280 vec.remove(3); 281 assert(vec.length == 5); 282 assert(vec[] == [0, 1, 2, 5, 4].s); //unstable remove 283 } 284 285 @nogc nothrow unittest { 286 Vector!int vec; 287 assert(vec.empty); 288 vec ~= [0, 1, 2, 3, 4, 5].s; 289 assert(vec[] == [0, 1, 2, 3, 4, 5].s); 290 assert(vec.length == 6); 291 vec ~= 6; 292 assert(vec[] == [0, 1, 2, 3, 4, 5, 6].s); 293 294 } 295 296 @nogc nothrow unittest { 297 Vector!int vec; 298 vec ~= [0, 1, 2, 3, 4, 5].s; 299 vec[3] = 33; 300 assert(vec[3] == 33); 301 } 302 303 @nogc nothrow unittest { 304 Vector!char vec; 305 vec ~= "abcd"; 306 assert(vec[] == cast(char[]) "abcd"); 307 } 308 309 @nogc nothrow unittest { 310 Vector!int vec; 311 vec ~= [0, 1, 2, 3, 4, 5].s; 312 vec.length = 2; 313 assert(vec[] == [0, 1].s); 314 } 315 /////////////////////////////////////////// 316 317 enum string checkVectorAllocations = ` 318 //assert(gVectorsCreated==gVectorsDestroyed); 319 gVectorsCreated=gVectorsDestroyed=0; 320 scope(exit){if(gVectorsCreated!=gVectorsDestroyed){ 321 import std.stdio : writefln; 322 writefln("created==destroyed %s==%s", gVectorsCreated, gVectorsDestroyed); 323 assert(gVectorsCreated==gVectorsDestroyed, "Vector memory leak"); 324 }} 325 `; 326 327 unittest { 328 mixin(checkVectorAllocations); 329 Vector!int vecA = Vector!int([0, 1, 2, 3, 4, 5].s); 330 assert(vecA[] == [0, 1, 2, 3, 4, 5].s); 331 Vector!int vecB; 332 vecB = vecA; 333 assert(vecB[] == [0, 1, 2, 3, 4, 5].s); 334 assert(vecB.array.ptr != vecA.array.ptr); 335 assert(vecB.used == vecA.used); 336 Vector!int vecC = vecA; 337 assert(vecC[] == [0, 1, 2, 3, 4, 5].s); 338 assert(vecC.array.ptr != vecA.array.ptr); 339 assert(vecC.used == vecA.used); 340 Vector!int vecD = vecA.init; 341 } 342 343 unittest { 344 static int numInit = 0; 345 static int numDestroy = 0; 346 scope (exit) { 347 assert(numInit == numDestroy); 348 } 349 static struct CheckDestructor { 350 int num = 1; 351 352 this(this) { 353 numInit++; 354 } 355 356 this(int n) { 357 num = n; 358 numInit++; 359 360 } 361 362 ~this() { 363 numDestroy++; 364 } 365 } 366 367 CheckDestructor[2] arr = [CheckDestructor(1), CheckDestructor(1)]; 368 Vector!CheckDestructor vec; 369 vec ~= CheckDestructor(1); 370 vec ~= arr; 371 vec.remove(1); 372 } 373 374 unittest { 375 assert(gVectorsCreated == gVectorsDestroyed); 376 gVectorsCreated = 0; 377 gVectorsDestroyed = 0; 378 scope (exit) { 379 assert(gVectorsCreated == gVectorsDestroyed); 380 } 381 string strA = "aaa bbb"; 382 string strB = "ccc"; 383 Vector!(Vector!char) vecA = Vector!(Vector!char)(Vector!char(cast(char[]) strA)); 384 assert(vecA[0] == Vector!char(cast(char[]) strA)); 385 Vector!(Vector!char) vecB; 386 vecB = vecA; 387 assert(vecB[0] == Vector!char(cast(char[]) strA)); 388 assert(vecA.array.ptr != vecB.array.ptr); 389 assert(vecB.used == vecA.used); 390 assert(vecB[0].array.ptr != vecA[0].array.ptr); 391 assert(vecB[0].used == vecA[0].used); 392 } 393 394 unittest { 395 static struct Test { 396 int num; 397 @disable this(this); 398 } 399 400 Vector!Test test; 401 }