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