1 module memutils.vector; 2 3 import std.algorithm : swap, initializeAll; 4 import std.range : empty; 5 import std.traits; 6 import core.stdc.string; 7 import std.range : isInputRange, isForwardRange, isRandomAccessRange, ElementType, refRange, RefRange, hasLength; 8 import core.exception : RangeError; 9 import std.exception : enforce; 10 import memutils.allocators; 11 import memutils.helpers; 12 import memutils.utils; 13 import memutils.refcounted; 14 import std.conv : emplace, to; 15 16 template isImplicitlyConvertibleLegacy(From, To) 17 { 18 enum bool isImplicitlyConvertibleLegacy = is(typeof({ 19 void fun(ref From v) 20 { 21 void gun(To) {} 22 gun(v); 23 } 24 })); 25 } 26 27 28 public alias SecureArray(T) = Array!(T, SecureMem); 29 30 template Array(T, ALLOC = ThreadMem) 31 if (!is (T == RefCounted!(Vector!(T, ALLOC), ALLOC))) 32 { 33 alias Array = RefCounted!(Vector!(T, ALLOC), ALLOC); 34 } 35 36 public alias SecureVector(T) = Vector!(T, SecureMem); 37 // TODO: Remove implicit string casting for Vector!ubyte! Encourage use of Vector!char [].idup instead. 38 39 /// An array that uses a custom allocator. 40 struct Vector(T, ALLOC = ThreadMem) 41 { 42 @disable this(this); 43 static if (!is(ALLOC == AppMem)) enum NOGC = true; 44 void opAssign()(auto ref Vector!(T, ALLOC) other) { 45 if (other.ptr !is this.ptr) 46 this.swap(other); 47 } 48 49 void opAssign()(ref T[] other) { 50 this[] = other[]; 51 } 52 53 // Payload cannot be copied 54 private struct Payload 55 { 56 size_t _capacity; 57 T[] _payload; 58 59 // Convenience constructor 60 this()(auto ref T[] p) 61 { 62 if (p.length == 0) return; 63 64 length = p.length; 65 66 static if (isImplicitlyConvertibleLegacy!(T, T)) { 67 if (_payload.ptr is p.ptr && _payload.length == p.length) { 68 p = null; 69 } 70 else { 71 _payload[] = p[0 .. $]; // todo: use emplace? 72 } 73 } 74 else 75 { 76 memmove(cast(void*)_payload.ptr, p.ptr, T.sizeof*p.length); 77 } 78 } 79 80 // Destructor releases array memory 81 ~this() const nothrow 82 { 83 try { 84 if (_capacity == 0 || _payload.ptr is null) 85 return; 86 T[] data = cast(T[]) _payload.ptr[0 .. _capacity]; 87 freeArray!(T, ALLOC)(data, _payload.length); // calls destructors and frees memory 88 (cast()this)._payload = null; 89 (cast()this)._capacity = 0; 90 } 91 catch (Throwable e) { 92 try { 93 logError(e.msg); 94 } catch (Throwable e2) {} 95 } 96 } 97 98 void opAssign(Payload rhs) 99 { 100 assert(false); 101 } 102 103 // Duplicate data 104 // @property Payload clone() 105 // { 106 // Payload result; 107 // result._payload = _payload.dup; 108 // // Conservatively assume initial capacity == length 109 // result._capacity = result._payload.length; 110 // return result; 111 // } 112 113 // length 114 @property size_t length() const 115 { 116 return _payload.length; 117 } 118 119 // length 120 @property void length(size_t newLength) 121 { 122 if (length > 0 && length >= newLength) 123 { 124 // shorten 125 static if (hasElaborateDestructor!T) { 126 foreach (ref e; _payload.ptr[newLength .. _payload.length]) { 127 static if (is(T == struct) && !isPointer!T) // call destructors but not for indirections... 128 .destroy(e); 129 } 130 131 // Zero out unused capacity to prevent gc from seeing 132 // false pointers 133 static if (hasIndirections!T) 134 memset(cast(void*)(_payload.ptr + newLength), 0, (_payload.length - newLength) * T.sizeof); 135 } 136 _payload = _payload.ptr[0 .. newLength]; 137 return; 138 } 139 140 if (newLength > 0) { 141 // enlarge 142 auto startEmplace = length; 143 reserve(newLength); 144 _payload = _payload.ptr[0 .. newLength]; 145 static if (!isImplicitlyConvertibleLegacy!(T, T)) { 146 T t = T(); 147 foreach (size_t i; startEmplace .. length) 148 memmove(cast(void*)(_payload.ptr + i), cast(void*)&t, T.sizeof); 149 150 } else 151 initializeAll(_payload.ptr[startEmplace .. length]); 152 } 153 } 154 155 // capacity 156 @property size_t capacity() const 157 { 158 return _capacity; 159 } 160 161 // reserve 162 void reserve(size_t elements) 163 { 164 if (elements <= capacity) return; 165 // TODO: allow vector to become smaller? 166 167 TRACE("Reserve ", length, " => ", elements, " elements."); 168 169 if (_capacity > 0) { 170 size_t len = _payload.length; 171 _payload = _payload.ptr[0 .. _capacity]; 172 _payload = reallocArray!(T, ALLOC)(_payload, elements)[0 .. len]; 173 } 174 else if (elements > 0) { 175 _payload = allocArray!(T, ALLOC)(elements)[0 .. _payload.length]; 176 } 177 _capacity = elements; 178 } 179 180 static if (is(T == char)) 181 pragma(inline, true) size_t pushBack(Stuff)(Stuff stuff) 182 if (is(Stuff == char[]) || is(Stuff == string)) 183 { 184 TRACE("Vector.append @disabled this(this)"); 185 if (_capacity <= length + stuff.length) 186 { 187 reserve(1 + (length + stuff.length) * 3 / 2); 188 } 189 assert(capacity >= length + stuff.length && _payload.ptr); 190 191 memmove(cast(void*)(_payload.ptr + _payload.length), stuff.ptr, stuff.length); 192 _payload = _payload.ptr[0 .. _payload.length + stuff.length]; 193 194 return 1; 195 } 196 197 pragma(inline, true) size_t pushBack(Stuff)(auto ref Stuff stuff) 198 if (!isImplicitlyConvertibleLegacy!(T, T) && is(T == Stuff)) 199 { 200 TRACE("Vector.append @disabled this(this)"); 201 if (_capacity == length) 202 { 203 reserve(1 + capacity * 3 / 2); 204 } 205 assert(capacity > length && _payload.ptr); 206 207 T* t = &stuff; 208 209 memmove(cast(void*)(_payload.ptr + _payload.length), cast(void*)t, T.sizeof); 210 memset(cast(void*)t, 0, T.sizeof); 211 _payload = _payload.ptr[0 .. _payload.length + 1]; 212 213 return 1; 214 } 215 216 // Insert one item 217 pragma(inline, true) size_t pushBack(Stuff)(auto ref Stuff stuff) 218 if (isImplicitlyConvertibleLegacy!(T, T) && isImplicitlyConvertibleLegacy!(Stuff, T)) 219 { 220 TRACE("Vector.append"); 221 //logTrace("Capacity: ", _capacity, " length: ", length); 222 if (_capacity == length) 223 { 224 reserve(1 + capacity * 3 / 2); 225 } 226 assert(capacity > length && _payload.ptr, "Payload pointer " ~ _payload.ptr.to!string ~ "'s capacity: " ~ capacity.to!string ~ " must be more than " ~ length.to!string); 227 emplace(_payload.ptr + _payload.length, stuff); 228 _payload = _payload.ptr[0 .. _payload.length + 1]; 229 return 1; 230 } 231 232 /// Insert a range of items 233 pragma(inline, true) size_t pushBack(Stuff)(auto ref Stuff stuff) 234 if (isInputRange!Stuff && (isImplicitlyConvertibleLegacy!(ElementType!Stuff, T) || is(T == ElementType!Stuff))) 235 { 236 TRACE("Vector.append 2"); 237 static if (hasLength!Stuff) 238 { 239 immutable oldLength = length; 240 reserve(oldLength + stuff.length); 241 } 242 size_t result; 243 foreach (ref item; stuff) 244 { 245 pushBack(item); 246 ++result; 247 } 248 static if (hasLength!Stuff) 249 { 250 assert(length == oldLength + stuff.length); 251 } 252 return result; 253 } 254 } 255 256 private alias Data = Payload; 257 private Data _data; 258 259 this(size_t elms) { 260 resize(elms); 261 } 262 263 /** 264 Constructor taking a number of items 265 */ 266 this(U)(U[] values...) 267 if (isImplicitlyConvertibleLegacy!(U, T)) 268 { 269 // TODO: This doesn't work with refcounted 270 _data = Data(cast(T[])values); 271 } 272 273 /** 274 Constructor taking an input range 275 */ 276 this(Stuff)(Stuff stuff) 277 if (isInputRange!Stuff && isImplicitlyConvertibleLegacy!(UnConst!(ElementType!Stuff), T) && !is(Stuff == T[])) 278 { 279 insertBack(stuff); 280 } 281 282 /** 283 * Move Constructor 284 */ 285 this()(auto ref typeof(this) other) { 286 if (this.ptr !is other.ptr) 287 this.swap(other); 288 else { 289 typeof(this) ini; 290 other.swap(ini); 291 } 292 } 293 @disable @property Vector!(T, ALLOC) dup() const; 294 /** 295 Duplicates the container. The elements themselves are not transitively 296 duplicated. 297 298 Complexity: $(BIGOH n). 299 */ 300 @property Vector!(T, ALLOC) clone() const 301 { 302 static if (__traits(compiles, { T a; T b; a = b; } ())) { 303 auto ret = Vector!(T, ALLOC)(cast(T[])(cast()this)._data._payload); 304 return ret.move; 305 } 306 else static if (__traits(hasMember, T, "move")) // Element is @disable this(this) but has move() 307 { 308 Vector!(T, ALLOC) vec = Vector!(T, ALLOC)(length); 309 // swap each element with a duplicate 310 foreach (size_t i, ref el; _data._payload) { 311 T t = el.move; 312 memmove(cast(void*)(vec._data._payload.ptr + i), cast(void*)&t, T.sizeof); 313 memset(cast(void*)&t, 0, T.sizeof); 314 } 315 return vec.move(); 316 } else static if (__traits(hasMember, T, "clone")) // Element is @disable this(this), doesn't have move() but has clone 317 { 318 Vector!(T, ALLOC) vec = Vector!(T, ALLOC)(length); 319 // swap each element with a duplicate 320 foreach (size_t i, ref el; _data._payload) { 321 T t = el.clone; 322 memmove(cast(void*)(vec._data._payload.ptr + i), cast(void*)&t, T.sizeof); 323 memset(cast(void*)&t, 0, T.sizeof); 324 } 325 return vec.move(); 326 } else static if (__traits(hasMember, T, "dup")) // Element is @disable this(this), can only be duplicated in the GC 327 { 328 Vector!(T, ALLOC) vec = Vector!(T, ALLOC)(length); 329 // swap each element with a duplicate 330 foreach (size_t i, ref el; _data._payload) { 331 T t = el.dup; 332 memmove(cast(void*)(vec._data._payload.ptr + i), cast(void*)&t, T.sizeof); 333 memset(cast(void*)&t, 0, T.sizeof); 334 } 335 return vec.move(); 336 } else static assert(false, "Cannot clone() the element: " ~ T.stringof); 337 } 338 339 @disable @property RefCounted!(Vector!(T, ALLOC), ALLOC) dupr() const; 340 341 /// ditto 342 @property RefCounted!(Vector!(T, ALLOC), ALLOC) cloneToRef() const 343 { 344 return RefCounted!(Vector!(T, ALLOC), ALLOC)(cast(T[])_data._payload); 345 } 346 347 void swap(ref Vector!(T, ALLOC) other) { 348 import std.algorithm : swap; 349 .swap(_data._payload, other._data._payload); 350 .swap(_data._capacity, other._data._capacity); 351 } 352 void swap(T[] other) { 353 this[] = other; 354 } 355 356 @property Vector!(T, ALLOC) move() { 357 return Vector!(T, ALLOC)(this); 358 } 359 360 /** 361 Property returning $(D true) if and only if the container has no 362 elements. 363 364 Complexity: $(BIGOH 1) 365 */ 366 @property bool empty() const 367 { 368 return !_data._payload || _data._payload.empty; 369 } 370 371 /** 372 Returns the number of elements in the container. 373 374 Complexity: $(BIGOH 1). 375 */ 376 @property size_t length() const 377 { 378 return _data._payload.length; 379 } 380 381 /// ditto 382 size_t opDollar() const 383 { 384 return length; 385 } 386 387 @property T* ptr() inout { 388 return cast(T*) _data._payload.ptr; 389 } 390 391 @property T* end() inout { 392 return this.ptr + this.length; 393 } 394 395 /** 396 Returns the maximum number of elements the container can store without 397 (a) allocating memory, (b) invalidating iterators upon insertion. 398 399 Complexity: $(BIGOH 1) 400 */ 401 @property size_t capacity() const 402 { 403 return _data._capacity; 404 } 405 406 /* 407 @property auto range() { 408 return refRange(&_data._payload); 409 } 410 */ 411 412 /** 413 Ensures sufficient capacity to accommodate $(D e) elements. 414 415 Postcondition: $(D capacity >= e) 416 417 Complexity: $(BIGOH 1) 418 */ 419 void reserve(size_t elements) 420 { 421 _data.reserve(elements); 422 } 423 424 /** 425 Returns an array that can be translated to a range using ($D refRange). 426 427 Complexity: $(BIGOH 1) 428 */ 429 auto opSlice() inout 430 { 431 //static if (is(T[] == ubyte[])) 432 // return cast(string) _data._payload; 433 //else 434 return *cast(T[]*)&_data._payload; 435 } 436 437 /** 438 Returns an array of the container from index $(D a) up to (excluding) index $(D b). 439 440 Precondition: $(D a <= b && b <= length) 441 442 Complexity: $(BIGOH 1) 443 */ 444 T[] opSlice(size_t i, size_t j) const 445 { 446 version (assert) if (i > j || j > length) throw new RangeError(); 447 return (cast(T[])_data._payload)[i .. j]; 448 } 449 450 /** 451 Forward to $(D opSlice().front) and $(D opSlice().back), respectively. 452 453 Precondition: $(D !empty) 454 455 Complexity: $(BIGOH 1) 456 */ 457 @property ref T front() 458 { 459 return _data._payload[0]; 460 } 461 462 /// ditto 463 @property ref T back() 464 { 465 return _data._payload[$ - 1]; 466 } 467 468 /** 469 Indexing operators yield or modify the value at a specified index. 470 471 Precondition: $(D i < length) 472 473 Complexity: $(BIGOH 1) 474 */ 475 ref T opIndex(size_t i) const 476 { 477 return *cast(T*)&_data._payload[i]; 478 } 479 480 void opIndexAssign(U)(auto ref U val, size_t i) 481 { 482 static if (__traits(compiles, {_data._payload[i] = cast(T) val; }())) 483 _data._payload[i] = cast(T) val; 484 else { // swap 485 memmove(cast(void*)(_data._payload.ptr + i), cast(void*)&val, U.sizeof); 486 memset(cast(void*)&val, 0, U.sizeof); 487 } 488 } 489 /** 490 Slicing operations execute an operation on an entire slice. 491 492 Precondition: $(D i < j && j < length) 493 494 Complexity: $(BIGOH slice.length) 495 */ 496 void opSliceAssign(Stuff)(auto ref Stuff value) 497 { 498 static if (isRandomAccessRange!Stuff) 499 { 500 _data.length = value.length; 501 _data._payload.ptr[0 .. value.length] = value[0 .. $]; 502 } else static if (is(UnConst!Stuff == Vector!(T, ALLOC))) { 503 _data.length = value._data.length; 504 _data._payload[] = value._data._payload[]; 505 } 506 else static if (isImplicitlyConvertibleLegacy!(T, ElementType!Stuff)) { 507 _data.length = value.length; 508 _data._payload[] = cast(T[]) value; 509 } else static assert(false, "Can't convert " ~ Stuff.stringof ~ " to " ~ T.stringof ~ "[]"); 510 } 511 512 /// ditto 513 void opSliceAssign(Stuff)(Stuff value, size_t i, size_t j) 514 { 515 auto slice = _data._payload; 516 slice[i .. j] = value; 517 } 518 519 /// ditto 520 void opSliceUnary(string op)() 521 if(op == "++" || op == "--") 522 { 523 mixin(op~"_data._payload[];"); 524 } 525 526 /// ditto 527 void opSliceUnary(string op)(size_t i, size_t j) 528 if(op == "++" || op == "--") 529 { 530 mixin(op~"slice[i .. j];"); 531 } 532 533 /// ditto 534 void opSliceOpAssign(string op)(T value) 535 { 536 mixin("_data._payload[] "~op~"= value;"); 537 } 538 539 /// ditto 540 void opSliceOpAssign(string op)(T value, size_t i, size_t j) 541 { 542 mixin("slice[i .. j] "~op~"= value;"); 543 } 544 545 /** 546 Returns a new container that's the concatenation of $(D this) and its 547 argument. $(D opBinaryRight) is only defined if $(D Stuff) does not 548 define $(D opBinary). 549 550 Complexity: $(BIGOH n + m), where m is the number of elements in $(D 551 stuff) 552 */ 553 auto opBinary(string op, Stuff)(Stuff stuff) 554 if (op == "~") 555 { 556 TRACE("Appending stuff"); 557 RefCounted!(Vector!(T, ALLOC), ALLOC) result; 558 // @@@BUG@@ result ~= this[] doesn't work 559 auto r = this[]; 560 result ~= r; 561 assert(result.length == length); 562 result ~= stuff[]; 563 return result; 564 } 565 566 void opOpAssign(string op, U)(auto ref U input) 567 if (op == "^") 568 { 569 if (this.length < input.length) 570 this.resize(input.length); 571 572 pure static void xorBuf(T)(T* output, const(T)* input, size_t length) 573 { 574 while (length >= 8) 575 { 576 output[0 .. 8] ^= input[0 .. 8]; 577 578 output += 8; input += 8; length -= 8; 579 } 580 581 output[0 .. length] ^= input[0 .. length]; 582 } 583 584 xorBuf(this.ptr, input.ptr, input.length); 585 } 586 587 /** 588 Forwards to $(D pushBack(stuff)). 589 */ 590 pragma(inline, true) void opOpAssign(string op, Stuff)(auto ref Stuff stuff) 591 if (op == "~") 592 { 593 static if (is (Stuff == RefCounted!(typeof(this)))) { 594 insertBack(cast(T[]) stuff[]); 595 } 596 else static if (is (Stuff == typeof(this))) { 597 insertBack(cast(T[]) stuff[]); 598 } 599 else 600 { 601 insertBack(stuff); 602 } 603 } 604 605 /** 606 Removes all contents from the container. The container decides how $(D 607 capacity) is affected. 608 609 Postcondition: $(D empty) 610 611 Complexity: $(BIGOH n) 612 */ 613 void clear() 614 { 615 TRACE("Vector.clear()"); 616 _data.length = 0; 617 } 618 619 620 /** 621 Sets the number of elements in the container to $(D newSize). If $(D 622 newSize) is greater than $(D length), the added elements are added to 623 unspecified positions in the container and initialized with $(D 624 T.init). 625 626 Complexity: $(BIGOH abs(n - newLength)) 627 628 Postcondition: $(D length == newLength) 629 */ 630 pragma(inline, true) @property void length(size_t newLength) 631 { 632 _data.length = newLength; 633 } 634 635 pragma(inline, true) void resize(size_t newLength) 636 { 637 this.length = newLength; 638 } 639 640 import std.traits : isNumeric; 641 642 int opCmp(Alloc)(auto const ref Vector!(T, Alloc) other) const 643 { 644 if (this[] == other[]) 645 return 0; 646 else if (this[] < other[]) 647 return -1; 648 else 649 return 0; 650 } 651 652 alias put = pushBack; 653 654 pragma(inline, true) size_t pushBack(Stuff...)(Stuff stuff) 655 if (!isNumeric!Stuff || !is ( T == ubyte )) 656 { 657 return insertBack(stuff); 658 } 659 660 pragma(inline, true) size_t pushBack(Stuff...)(Stuff stuff) 661 if (isNumeric!Stuff && is(T == ubyte)) 662 { 663 return insertBack(cast(T) stuff); 664 } 665 666 pragma(inline, true) size_t insert(Stuff...)(Stuff stuff) { 667 return insertBack(stuff); 668 } 669 670 /** 671 Inserts $(D value) to the front or back of the container. $(D stuff) 672 can be a value convertible to $(D T) or a range of objects convertible 673 to $(D T). The stable version behaves the same, but guarantees that 674 ranges iterating over the container are never invalidated. 675 676 Returns: The number of elements inserted 677 678 Complexity: $(BIGOH m * log(n)), where $(D m) is the number of 679 elements in $(D stuff) 680 */ 681 pragma(inline, true) size_t insertBack(Stuff)(auto ref Stuff stuff) 682 { 683 static if (isImplicitlyConvertibleLegacy!(Stuff, T[])) 684 return _data.pushBack(cast(T[])stuff); 685 else static if (isSomeString!(Stuff) && !isImplicitlyConvertibleLegacy!(Stuff, T)) { 686 return _data.pushBack(cast(T[])stuff); 687 } 688 else static if (isSomeString!(Stuff) && isImplicitlyConvertibleLegacy!(Stuff, T)) { 689 return _data.pushBack(cast(T)stuff); 690 } 691 else static if (isInputRange!(Stuff) && isImplicitlyConvertibleLegacy!(ForeachType!Stuff, T)) { 692 return _data.pushBack(stuff); 693 } 694 else 695 return _data.pushBack(cast(T) stuff); 696 } 697 698 /** 699 Removes the value at the back of the container. The stable version 700 behaves the same, but guarantees that ranges iterating over the 701 container are never invalidated. 702 703 Precondition: $(D !empty) 704 705 Complexity: $(BIGOH log(n)). 706 */ 707 void removeBack() 708 { 709 enforce(!empty); 710 static if (hasElaborateDestructor!T) 711 .destroy(_data._payload[$ - 1]); 712 713 _data._payload = _data._payload[0 .. $ - 1]; 714 } 715 716 @trusted 717 void removeFront() { 718 719 enforce(!empty); 720 static if (hasElaborateDestructor!T) 721 .destroy(_data._payload[0]); 722 if (_data._payload.length > 1) { 723 memmove(cast(void*)_data._payload.ptr, cast(void*)(_data._payload.ptr + 1), T.sizeof * (_data._payload.length - 1)); 724 memset(cast(void*)(_data._payload.ptr + _data._payload.length - 1), 0, T.sizeof); 725 } 726 _data._payload.length -= 1; 727 } 728 729 /** 730 Removes $(D howMany) values at the front or back of the 731 container. Unlike the unparameterized versions above, these functions 732 do not throw if they could not remove $(D howMany) elements. Instead, 733 if $(D howMany > n), all elements are removed. The returned value is 734 the effective number of elements removed. The stable version behaves 735 the same, but guarantees that ranges iterating over the container are 736 never invalidated. 737 738 Returns: The number of elements removed 739 740 Complexity: $(BIGOH howMany). 741 */ 742 size_t removeBack(size_t howMany) 743 { 744 if (howMany > length) howMany = length; 745 static if (hasElaborateDestructor!T) 746 foreach (ref e; _data._payload[$ - howMany .. $]) 747 .destroy(e); 748 749 _data._payload = _data._payload[0 .. $ - howMany]; 750 return howMany; 751 } 752 753 /** 754 Inserts $(D stuff) before position i. 755 756 Returns: The number of values inserted. 757 758 Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) 759 */ 760 void insertBefore(Stuff)(size_t i, Stuff stuff) 761 if (isImplicitlyConvertibleLegacy!(Stuff, T)) 762 { 763 enforce(i <= length); 764 reserve(length + 1); 765 766 // Move elements over by one slot 767 memmove(cast(void*)(_data._payload.ptr + i + 1), 768 cast(void*)(_data._payload.ptr + i), 769 T.sizeof * (length - i)); 770 emplace(_data._payload.ptr + i, stuff); 771 _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; 772 } 773 774 /// ditto 775 size_t insertBefore(Stuff)(size_t i, Stuff stuff) 776 if (isInputRange!Stuff && isImplicitlyConvertibleLegacy!(ElementType!Stuff, T)) 777 { 778 enforce(i <= length); 779 static if (isForwardRange!Stuff) 780 { 781 // Can find the length in advance 782 auto extra = walkLength(stuff); 783 if (!extra) return 0; 784 reserve(length + extra); 785 // Move elements over by extra slots 786 memmove(cast(void*)(_data._payload.ptr + i + extra), 787 cast(void*)(_data._payload.ptr + i), 788 T.sizeof * (length - i)); 789 foreach (p; _data._payload.ptr + i .. 790 _data._payload.ptr + i + extra) 791 { 792 emplace(p, stuff.front); 793 stuff.popFront(); 794 } 795 _data._payload = _data._payload.ptr[0 .. _data._payload.length + extra]; 796 return extra; 797 } 798 else 799 { 800 enforce(_data); 801 immutable offset = i; 802 enforce(offset <= length); 803 auto result = pushBack(stuff); 804 bringToFront(this[offset .. length - result], 805 this[length - result .. length]); 806 return result; 807 } 808 } 809 810 /// ditto 811 size_t insertAfter(Stuff)(size_t i, Stuff stuff) 812 { 813 enforce(r._outer._data is _data); 814 // TODO: optimize 815 immutable offset = i; 816 enforce(offset <= length); 817 auto result = pushBack(stuff); 818 bringToFront(this[offset .. length - result], 819 this[length - result .. length]); 820 return result; 821 } 822 823 bool opEquals()(auto const ref RefCounted!(Vector!(T, ALLOC), ALLOC) other_) const { 824 import memutils.constants : logTrace; 825 if (other_.empty && empty()) 826 return true; 827 else if (other_.empty) 828 return false; 829 if (other_.length != length) 830 return false; 831 // TODO: use the true types 832 return _data._payload[0 .. length] == other_._data._payload[0 .. length]; 833 } 834 835 bool opEquals()(auto const ref Vector!(T, ALLOC) other_) const { 836 if (other_.empty && empty()) 837 return true; 838 else if (other_.empty) 839 return false; 840 if (other_.length != length) 841 return false; 842 843 return _data._payload[0 .. length] == other_._data._payload[0 .. length]; 844 } 845 846 bool opEquals()(auto const ref T[] other) { 847 logTrace("other: ", other, " this: ", _data._payload); 848 return other == _data._payload; 849 } 850 851 } 852 853 auto array(T)(T[] val) 854 { 855 return Array!(Unqual!T)(val); 856 } 857 858 auto vector(T)(T[] val) 859 { 860 return Vector!(Unqual!T)(val); 861 } 862 863 void TRACE(T...)(T t) { 864 //import std.stdio : writeln; 865 //writeln(t); 866 }