1 module nxt.cyclic; 2 3 import core.internal.traits : hasElaborateDestructor; 4 import std.algorithm : max; 5 import std.container.array : Array; 6 import std.range; 7 import std.traits : isMutable; 8 import nxt.container_traits : isAddress; 9 10 private mixin template CyclicRangePrimitives(T, string makeCopy = "typeof(cast() this) copy;") 11 { 12 size_t capacity() const @property @nogc @safe 13 { 14 return array.length; 15 } 16 17 size_t length() const @property @nogc @safe 18 { 19 return size; 20 } 21 22 void put()(auto ref T val) 23 { 24 if (size >= array.length) 25 throw staticError!CyclicRangeError("array capacity overflow", __FILE__, __LINE__); 26 array[(start + size) % array.length] = val; 27 size++; 28 } 29 30 void put()(const auto ref T val) 31 { 32 if (size >= array.length) 33 throw staticError!CyclicRangeError("array capacity overflow", __FILE__, __LINE__); 34 array[(start + size) % array.length] = val; 35 size++; 36 } 37 38 void insertBack(T rhs) 39 { 40 put(rhs); 41 } 42 43 void insertBack(size_t n)(T[n] rhs) 44 { 45 foreach (c; rhs) 46 { 47 put(c); 48 } 49 } 50 51 void insertBack(Range)(Range rhs) 52 if (__traits(compiles, ElementType!Range) && 53 is(ElementType!Range : T)) 54 { 55 foreach (c; rhs) 56 { 57 put(c); 58 } 59 } 60 61 alias stableInsertBack = insertBack; 62 alias insert = insertBack; 63 alias stableInsert = insertBack; 64 alias linearInsert = insertBack; 65 alias stableLinearInsert = insertBack; 66 67 void insertFront()(auto ref T val) 68 { 69 if (size >= array.length) 70 throw staticError!CyclicRangeError("array capacity overflow", __FILE__, __LINE__); 71 start = (start + array.length - 1) % array.length; 72 array[start] = val; 73 size++; 74 } 75 76 void popFront() 77 { 78 if (size == 0) 79 throw staticError!CyclicRangeError("trying to pop an empty array", __FILE__, __LINE__); 80 static if (hasElaborateDestructor!T) 81 { 82 destroy(array[start]); 83 } 84 else static if (isAddress!T) 85 { 86 array[start] = null; // please the GC 87 } 88 start = (start + 1) % array.length; 89 size--; 90 } 91 92 auto save() 93 { 94 return this; 95 } 96 97 bool empty() @nogc @safe @property const 98 { 99 return size == 0; 100 } 101 102 ref auto front() @nogc @safe @property inout 103 { 104 if (size == 0) 105 throw staticError!CyclicRangeError("trying to call front on empty array", 106 __FILE__, __LINE__); 107 return array[start]; 108 } 109 110 void popBack() 111 { 112 if (size == 0) 113 throw staticError!CyclicRangeError("trying to pop an empty array", __FILE__, __LINE__); 114 size--; 115 static if (hasElaborateDestructor!T) 116 { 117 destroy(array[(start + size) % array.length]); 118 } 119 else static if (isAddress!T) 120 { 121 array[(start + size) % array.length] = null; // please the GC 122 } 123 } 124 125 ref auto back() @property 126 { 127 if (size == 0) 128 throw staticError!CyclicRangeError("trying to call back on empty array", 129 __FILE__, __LINE__); 130 return array[(start + size - 1) % array.length]; 131 } 132 133 auto back() @property const 134 { 135 if (size == 0) 136 throw staticError!CyclicRangeError("trying to call back on empty array", 137 __FILE__, __LINE__); 138 return array[(start + size - 1) % array.length]; 139 } 140 141 size_t opDollar() @nogc @safe const 142 { 143 return length; 144 } 145 146 ref inout(T) opIndex(size_t v) inout 147 { 148 if (v >= size) 149 throw staticError!CyclicRangeError("out of range", __FILE__, __LINE__); 150 else 151 return array[(v + start) % array.length]; 152 } 153 154 auto opIndex() const 155 { 156 return this.dup(); 157 } 158 159 private void validateRange(size_t[2] range) 160 { 161 immutable size_t from = range[0]; 162 immutable size_t to = range[1]; 163 if (to > size) 164 throw staticError!CyclicRangeError("trying to slice over array size", 165 __FILE__, __LINE__); 166 if (from > to) 167 throw staticError!CyclicRangeError("trying to negative slice", __FILE__, __LINE__); 168 if (from != to && from >= size || to - from > size) 169 throw staticError!CyclicRangeError("trying to slice over array bounds", 170 __FILE__, __LINE__); 171 } 172 173 auto opIndex(size_t[2] range) 174 { 175 immutable size_t from = range[0]; 176 immutable size_t to = range[1]; 177 validateRange(range); 178 179 mixin(makeCopy); 180 copy.start = 0; 181 copy.size = to - from; 182 183 foreach (const i; 0 .. copy.size) 184 { 185 copy.array[i] = array[(i + start + from) % array.length]; 186 } 187 return copy; 188 } 189 190 static if (isMutable!T) 191 { 192 void opIndexUnary(string op)() 193 { 194 foreach (const i; 0 .. size) 195 { 196 mixin(op ~ "array[(i + start) % array.length];"); 197 } 198 } 199 200 auto opIndexUnary(string op)(size_t i) 201 { 202 return mixin(op ~ "array[(i + start) % array.length]"); 203 } 204 205 void opIndexUnary(string op)(size_t[2] range) 206 { 207 immutable size_t from = range[0]; 208 immutable size_t to = range[1]; 209 validateRange(range); 210 211 foreach (const i; 0 .. to - from) 212 { 213 mixin(op ~ "array[(i + start + from) % array.length];"); 214 } 215 } 216 217 void opIndexAssign(U)(U val) 218 { 219 foreach (const i; 0 .. size) 220 { 221 array[(i + start) % array.length] = val; 222 } 223 } 224 225 void opIndexAssign(U)(U val, size_t i) 226 { 227 opIndex(i) = val; 228 } 229 230 void opIndexAssign(U)(U val, size_t[2] range) 231 { 232 immutable size_t from = range[0]; 233 immutable size_t to = range[1]; 234 validateRange(range); 235 236 foreach (const i; 0 .. to - from) 237 { 238 array[(i + start + from) % array.length] = val; 239 } 240 } 241 242 void opIndexOpAssign(string op, U)(U val) 243 { 244 foreach (const i; 0 .. size) 245 { 246 mixin("array[(i + start) % array.length] " ~ op ~ "= val;"); 247 } 248 } 249 250 void opIndexOpAssign(string op, U)(U val, size_t i) 251 { 252 mixin("array[(i + start) % array.length] " ~ op ~ "= val;"); 253 } 254 255 void opIndexOpAssign(string op, U)(U val, size_t[2] range) 256 { 257 immutable size_t from = range[0]; 258 immutable size_t to = range[1]; 259 validateRange(range); 260 261 foreach (const i; 0 .. to - from) 262 { 263 mixin("array[(i + start + from) % array.length] " ~ op ~ "= val;"); 264 } 265 } 266 } 267 268 static if (isMutable!T) 269 { 270 import std.algorithm.mutation : move; 271 272 T moveFront() 273 { 274 if (size == 0) 275 throw staticError!CyclicRangeError("trying to move in empty array", 276 __FILE__, __LINE__); 277 return move(array[0]); 278 } 279 280 T moveBack() 281 { 282 if (size == 0) 283 throw staticError!CyclicRangeError("trying to move in empty array", 284 __FILE__, __LINE__); 285 return move(array[(start + size - 1) % array.length]); 286 } 287 288 T moveAt(size_t i) 289 { 290 if (i >= size) 291 throw staticError!CyclicRangeError("trying to move outside range", 292 __FILE__, __LINE__); 293 return move(array[(start + i) % array.length]); 294 } 295 } 296 297 size_t[2] opSlice(size_t i : 0)() const 298 { 299 return [0, size]; 300 } 301 302 size_t[2] opSlice(size_t i : 0)(size_t from, size_t to) const 303 { 304 return [from, to]; 305 } 306 307 /** 308 * Removes the last element from the array and returns it. 309 * Both stable and non-stable versions behave the same and guarantee 310 * that ranges iterating over the array are never invalidated. 311 * 312 * Precondition: `empty == false` 313 * 314 * Returns: The element removed. 315 * 316 * Complexity: $(BIGOH 1). 317 * 318 * Throws: `Exception` if the array is empty. 319 */ 320 T removeAny() 321 { 322 auto result = back; 323 removeBack(); 324 return result; 325 } 326 327 alias stableRemoveAny = removeAny; 328 329 /** 330 * Removes the value from the back of the array. Both stable and non-stable 331 * versions behave the same and guarantee that ranges iterating over the 332 * array are never invalidated. 333 * 334 * Precondition: `empty == false` 335 * 336 * Complexity: $(BIGOH 1). 337 * 338 * Throws: `Exception` if the array is empty. 339 */ 340 void removeBack() 341 { 342 popBack(); 343 } 344 345 /// ditto 346 alias stableRemoveBack = removeBack; 347 348 void removeBack(int howMany) 349 { 350 foreach (const i; 0 .. howMany) 351 { 352 popBack(); 353 } 354 } 355 } 356 357 struct CyclicRange(T) 358 { 359 T[] array; 360 size_t start, size; 361 362 mixin CyclicRangePrimitives!T; 363 364 CyclicRange!T dup() const 365 { 366 return cast(CyclicRange!T) this; 367 } 368 } 369 370 /// @nogc array without memory management using a cyclic array internally 371 /// Should be treated like a static array when no len is set (copies on assignment) 372 /// The maximum capacity is static and by default so many elements that it fills at most 4KiB, but at least 8 elements (even if that is more than 4KiB). 373 /// Set length to 0 to make it a std.container.Array based array 374 /// Bugs: foreach with ref value requires @nogc body to make this @nogc compatible 375 struct CyclicArray(T, size_t len = max(8, 4096 / T.sizeof)) 376 { 377 static if (len == 0) 378 { 379 private void reserve(size_t length) @nogc @system nothrow 380 { 381 assert(length > 0); 382 array.length = length; 383 } 384 } 385 386 private: 387 static if (len == 0) 388 Array!T array; 389 else 390 T[len] array; 391 size_t start; 392 size_t size; 393 394 public: 395 static if (len == 0) 396 { 397 invariant 398 { 399 assert(array.length > 0); 400 } 401 402 // @disable this(); // TODO this triggers bug in dmd: https://issues.dlang.org/show_bug.cgi?id=20934 403 404 this(Array!T array) @nogc 405 { 406 assert(array.length > 0); 407 this.array = array; 408 } 409 410 this(size_t length) @nogc 411 { 412 assert(length > 0); 413 array = Array!T(); 414 reserve(length); 415 } 416 417 this(size_t n)(T[n] val) 418 { 419 array = Array!T(); 420 reserve(max(8, 4096 / T.sizeof)); 421 static if (len != 0) 422 static assert(n <= len); 423 foreach (ref v; val) 424 { 425 put(v); 426 } 427 } 428 429 this(Range)(Range val) 430 if (__traits(compiles, ElementType!Range) && is(ElementType!Range : T)) 431 { 432 array = Array!T(); 433 reserve(max(8, 4096 / T.sizeof)); 434 foreach (ref v; val) 435 { 436 put(v); 437 } 438 } 439 440 this(Args...)(Args val) 441 { 442 array = Array!T(); 443 reserve(max(8, 4096 / T.sizeof)); 444 foreach (ref v; val) 445 { 446 put(v); 447 } 448 } 449 } 450 else 451 { 452 this(size_t n)(T[n] val) 453 { 454 static if (len != 0) 455 { 456 static assert(n <= len); 457 } 458 foreach (ref v; val) 459 { 460 put(v); 461 } 462 } 463 464 this(Range)(Range val) 465 if (__traits(compiles, ElementType!Range) && is(ElementType!Range : T)) 466 { 467 foreach (ref v; val) 468 { 469 put(v); 470 } 471 } 472 473 this(Args...)(Args val) 474 { 475 foreach (ref v; val) 476 { 477 put(v); 478 } 479 } 480 } 481 482 mixin CyclicRangePrimitives!(T, q{ 483 static if (len == 0) 484 auto copy = typeof(cast() this)(array.length); 485 else 486 typeof(cast() this) copy; 487 }); 488 489 static if (len != 0) 490 CyclicRange!T byRef() @property @nogc @safe 491 { 492 return CyclicRange!T(array[], start, size); 493 } 494 495 size_t length() const @property @nogc @safe 496 { 497 return size; 498 } 499 500 size_t length(size_t val) @property 501 { 502 if (size > array.length) 503 assert(false); 504 if (val == 0) 505 { 506 clear(); 507 return size; 508 } 509 if (val > size) 510 { 511 foreach (const i; size .. val) 512 { 513 array[(start + i) % array.length] = T.init; 514 } 515 } 516 else if (val < size) 517 { 518 static if (hasElaborateDestructor!T) 519 { 520 foreach (const i; val .. size) 521 { 522 destroy(array[(start + i) % array.length]); 523 } 524 } 525 else static if (isAddress!T) 526 { 527 foreach (const i; val .. size) 528 { 529 array[(start + i) % array.length] = null; // please the GC 530 } 531 } 532 } 533 return size = val; 534 } 535 536 void clear() 537 { 538 static if (hasElaborateDestructor!T) 539 { 540 foreach (const i; 0 .. size) 541 { 542 destroy(array[(start + i) % array.length]); 543 } 544 } 545 start = 0; // optimize clears 546 size = 0; 547 } 548 549 auto opBinary(string op : "~")(T rhs) const 550 { 551 auto copy = this.dup; 552 copy.put(rhs); 553 return copy; 554 } 555 556 auto opBinary(string op : "~", size_t n)(T[n] rhs) const 557 { 558 auto copy = this.dup; 559 copy ~= rhs; 560 return copy; 561 } 562 563 auto opBinary(string op : "~", Range)(Range rhs) const 564 if (isInputRange!Range && is(ElementType!Range : T)) 565 { 566 auto copy = this.dup; 567 copy ~= rhs; 568 return copy; 569 } 570 571 void opOpAssign(string op : "~")(T rhs) 572 { 573 put(rhs); 574 } 575 576 void opOpAssign(string op : "~", size_t n)(T[n] rhs) 577 { 578 foreach (c; rhs) 579 { 580 put(c); 581 } 582 } 583 584 void opOpAssign(string op : "~", size_t n)(CyclicArray!(T, n) rhs) 585 { 586 for (int i = 0; i < rhs.size; i++) 587 put(rhs.array[(rhs.start + i) % rhs.array.length]); 588 } 589 590 void opOpAssign(string op : "~", Range)(Range rhs) 591 if (__traits(compiles, ElementType!Range) && is(ElementType!Range : T)) 592 { 593 foreach (c; rhs) 594 { 595 put(c); 596 } 597 } 598 599 static if (len == 0) 600 { 601 CyclicArray!(T, len) dup() const 602 { 603 auto ret = CyclicArray!(T, len)(array); 604 ret.start = start; 605 ret.size = size; 606 return ret; 607 } 608 } 609 else 610 { 611 CyclicArray!(T, len) dup() const 612 { 613 CyclicArray!(T, len) ret; 614 ret.array = cast(typeof(ret.array)) array; 615 ret.start = start; 616 ret.size = size; 617 return ret; 618 } 619 } 620 621 static if (len != 0) 622 { 623 int opApply(int delegate(ref T) @nogc dg) @nogc 624 { 625 int result = 0; 626 627 for (size_t i = 0; i < size; i++) 628 { 629 result = dg(array[(i + start) % array.length]); 630 if (result) 631 break; 632 } 633 634 return result; 635 } 636 637 int opApply(int delegate(size_t, ref T) @nogc dg) @nogc 638 { 639 int result = 0; 640 641 for (size_t i = 0; i < size; i++) 642 { 643 result = dg(i, array[(i + start) % array.length]); 644 if (result) 645 break; 646 } 647 648 return result; 649 } 650 } 651 652 bool opEquals(in CyclicArray!(T, len) b) 653 { 654 if (size != b.size) 655 return false; 656 for (int i = 0; i < size; i++) 657 if (array[(i + start) % array.length] != b.array[(i + b.start) % b.array.length]) 658 return false; 659 return true; 660 } 661 662 bool opEquals(size_t n)(T[n] b) 663 { 664 if (size != n) 665 return false; 666 for (int i = 0; i < size; i++) 667 if (array[(i + start) % array.length] != b[i]) 668 return false; 669 return true; 670 } 671 672 bool opEquals(Range)(Range b) if (hasLength!Range && is(ElementType!Range : T)) 673 { 674 if (size != b.length) 675 return false; 676 auto r = b.save; 677 for (int i = 0; i < size; i++) 678 { 679 if (array[(i + start) % array.length] != r.front) 680 return false; 681 r.popFront; 682 } 683 return true; 684 } 685 } 686 687 /// 688 @nogc @safe unittest 689 { 690 CyclicArray!int array; 691 assert(array.length == 0); 692 array ~= 5; 693 assert(!array.empty); 694 assert(array.front == 5); 695 assert(array[0] == 5); 696 array ~= [4, 3]; 697 698 assert(array == [5, 4, 3]); 699 700 // same efficiency as insertBack/put/concat 701 array.insertFront(5); 702 } 703 704 @nogc @system unittest 705 { 706 // heap array using std.container.Array with 16 elements 707 auto heapArray = CyclicArray!(int, 0)(16); 708 709 // custom memory using Array 710 auto myArray = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 711 auto customArray = CyclicArray!(int, 0)(myArray[0 .. 6]); 712 } 713 714 class CyclicRangeError : Error 715 { 716 @nogc @safe pure nothrow this(string msg, string file = __FILE__, 717 size_t line = __LINE__, Throwable next = null) 718 { 719 super(msg, file, line, next); 720 } 721 } 722 723 // TLS storage shared for all errors, chaining might create circular reference 724 private void[128] _store; 725 726 // only Errors for now as those are rarely chained 727 private T staticError(T, Args...)(auto ref Args args) @trusted if (is(T : Error)) 728 { 729 // pure hack, what we actually need is @noreturn and allow to call that in pure functions 730 static T get() 731 { 732 static assert(__traits(classInstanceSize, T) <= _store.length, 733 T.stringof ~ " is too large for staticError()"); 734 735 _store[0 .. __traits(classInstanceSize, T)] = typeid(T).initializer[]; 736 return cast(T) _store.ptr; 737 } 738 739 auto res = (cast(T function() @trusted pure nothrow @nogc)&get)(); 740 res.__ctor(args); 741 return res; 742 } 743 744 // lots of unittests taken from std.container.array 745 746 // Give the Range object some testing. 747 @system unittest 748 { 749 import std.algorithm.comparison : equal; 750 import std.range : retro; 751 752 auto a = CyclicArray!int(0, 1, 2, 3, 4, 5, 6)[]; 753 assert(equal(a, [0, 1, 2, 3, 4, 5, 6])); 754 assert(equal(a[], [0, 1, 2, 3, 4, 5, 6])); 755 assert(equal(a[0 .. $], [0, 1, 2, 3, 4, 5, 6])); 756 auto b = CyclicArray!int(6, 5, 4, 3, 2, 1, 0)[]; 757 alias A = typeof(a); 758 alias ARef = typeof(a.byRef); 759 760 static assert(isRandomAccessRange!A); 761 static assert(hasSlicing!A); 762 static assert(hasAssignableElements!A); 763 static assert(hasMobileElements!A); 764 765 static assert(isRandomAccessRange!ARef); 766 static assert(hasSlicing!ARef); 767 static assert(hasAssignableElements!ARef); 768 static assert(hasMobileElements!ARef); 769 770 assert(equal(retro(b), a)); 771 assert(a.length == 7); 772 assert(equal(a[1 .. 4], [1, 2, 3])); 773 } 774 775 @system unittest 776 { 777 alias S = structBug5920; 778 uint dMask; 779 780 auto arr = CyclicArray!S(cast(S[])[]); 781 foreach (const i; 0 .. 8) 782 { 783 arr.insertBack(S(i, &dMask)); 784 } 785 // don't check dMask now as S may be copied multiple times (it's ok?) 786 { 787 assert(arr.length == 8); 788 dMask = 0; 789 arr.length = 6; 790 assert(arr.length == 6); // make sure shrinking calls the d'tor 791 assert(dMask == 0b1100_0000); 792 arr.removeBack(); 793 assert(arr.length == 5); // make sure removeBack() calls the d'tor 794 assert(dMask == 0b1110_0000); 795 arr.removeBack(3); 796 assert(arr.length == 2); // ditto 797 assert(dMask == 0b1111_1100); 798 arr.clear(); 799 assert(arr.length == 0); // make sure clear() calls the d'tor 800 assert(dMask == 0b1111_1111); 801 } 802 assert(dMask == 0b1111_1111); // make sure the d'tor is called once only. 803 } 804 805 @system unittest 806 { 807 auto a = CyclicArray!(int[])([[1, 2], [3, 4]]); 808 assert(a.length == 2); 809 assert(a[0] == [1, 2]); 810 assert(a[1] == [3, 4]); 811 } 812 813 @system unittest 814 { 815 import std.algorithm.comparison : equal; 816 817 //Test "array-wide" operations 818 auto a = CyclicArray!int([0, 1, 2]); //CyclicArray 819 a[] += 5; 820 assert(a[].equal([5, 6, 7])); 821 ++a[]; 822 assert(a[].equal([6, 7, 8])); 823 import std.stdio; 824 825 a[1 .. 3] *= 5; 826 assert(a[].equal([6, 35, 40])); 827 a[0 .. 2] = 0; 828 assert(a[].equal([0, 0, 40])); 829 830 //Test empty array 831 auto a2 = CyclicArray!int.init; 832 ++a2[]; 833 ++a2[0 .. 0]; 834 a2[] = 0; 835 a2[0 .. 0] = 0; 836 a2[] += 0; 837 a2[0 .. 0] += 0; 838 839 //Test "range-wide" operations 840 auto r = CyclicArray!int([0, 1, 2])[]; //CyclicArray.Range 841 r[] += 5; 842 assert(r.equal([5, 6, 7])); 843 ++r[]; 844 assert(r.equal([6, 7, 8])); 845 r[1 .. 3] *= 5; 846 assert(r.equal([6, 35, 40])); 847 r[0 .. 2] = 0; 848 assert(r.equal([0, 0, 40])); 849 850 //Test empty Range 851 auto r2 = CyclicArray!int.init[]; 852 ++r2[]; 853 ++r2[0 .. 0]; 854 r2[] = 0; 855 r2[0 .. 0] = 0; 856 r2[] += 0; 857 r2[0 .. 0] += 0; 858 } 859 860 // Test issue 861 @system unittest 862 { 863 static struct S 864 { 865 int i = 1337; 866 void* p; 867 this(this) 868 { 869 assert(i == 1337); 870 } 871 872 ~this() @nogc 873 { 874 assert(i == 1337); 875 } 876 } 877 878 CyclicArray!S arr; 879 S s; 880 arr ~= s; 881 arr ~= s; 882 } 883 884 @system unittest 885 { 886 import std.algorithm.iteration : filter; 887 888 auto a = CyclicArray!int([1, 2, 2].filter!"true"()); 889 } 890 891 @safe unittest 892 { 893 auto arr = new CyclicArray!int; 894 } 895 896 @system unittest //6998 897 { 898 static int i = 0; 899 class C 900 { 901 int dummy = 1; 902 this() 903 { 904 ++i; 905 } 906 907 ~this() @nogc 908 { 909 --i; 910 } 911 } 912 913 assert(i == 0); 914 auto c = new C(); 915 assert(i == 1); 916 917 //scope 918 { 919 auto arr = CyclicArray!C(c); 920 assert(i == 1); 921 } 922 //CyclicArray should not have destroyed the class instance 923 assert(i == 1); 924 925 //Just to make sure the GC doesn't collect before the above test. 926 assert(c.dummy == 1); 927 } 928 929 @system unittest //6998-2 930 { 931 static class C 932 { 933 int i; 934 } 935 936 auto c = new C; 937 c.i = 42; 938 CyclicArray!C a; 939 a ~= c; 940 a.clear; 941 assert(c.i == 42); //fails 942 } 943 944 @nogc: 945 unittest 946 { 947 alias IntArray = CyclicArray!int; 948 alias IntRange = CyclicRange!int; 949 950 static assert(isInputRange!IntArray); 951 static assert(isOutputRange!(IntArray, int)); 952 static assert(isForwardRange!IntArray); 953 static assert(isBidirectionalRange!IntArray); 954 static assert(isRandomAccessRange!IntArray); 955 static assert(hasMobileElements!IntArray); 956 static assert(is(ElementType!IntArray == int)); 957 static assert(hasSwappableElements!IntArray); 958 static assert(hasAssignableElements!IntArray); 959 static assert(hasLvalueElements!IntArray); 960 static assert(hasLength!IntArray); 961 static assert(hasSlicing!IntArray); 962 963 static assert(isInputRange!IntRange); 964 static assert(isOutputRange!(IntRange, int)); 965 static assert(isForwardRange!IntRange); 966 static assert(isBidirectionalRange!IntRange); 967 static assert(isRandomAccessRange!IntRange); 968 static assert(hasMobileElements!IntRange); 969 static assert(is(ElementType!IntRange == int)); 970 static assert(hasSwappableElements!IntRange); 971 static assert(hasAssignableElements!IntRange); 972 static assert(hasLvalueElements!IntRange); 973 static assert(hasLength!IntRange); 974 static assert(hasSlicing!IntRange); 975 976 IntArray array; 977 assert(array.length == 0); 978 assert(array.empty); 979 array ~= 5; 980 assert(!array.empty); 981 assert(array.length == 1); 982 assert(array.front == 5); 983 assert(array[0] == 5); 984 assert(array[0 .. 1].front == 5); 985 assert(array[0 .. 0].empty); 986 array ~= cast(int[2])[4, 3]; 987 assert(array.length == 3); 988 assert(array[1] == 4); 989 assert(array[2] == 3); 990 991 for (int i = 0; i < 50000; i++) 992 { 993 array ~= i; 994 array.popFront(); 995 } 996 997 assert(array.length == 3); 998 999 array ~= array; 1000 assert(array.length == 6); 1001 assert((array ~ array).length == 12); 1002 assert(array.length == 6); 1003 1004 array[5] = 1; 1005 assert(array[5] == 1); 1006 auto copy = array; 1007 copy[5] = 2; 1008 assert(array[5] == 1); 1009 array[][5] = 1; 1010 assert(array[5] == 1); 1011 1012 foreach (ref v; array.byRef) 1013 { 1014 v = 42; 1015 } 1016 1017 assert(array[5] == 42); 1018 } 1019 1020 unittest 1021 { 1022 alias IntArray = CyclicArray!(int, 0); 1023 1024 static assert(isInputRange!IntArray); 1025 static assert(isOutputRange!(IntArray, int)); 1026 static assert(isForwardRange!IntArray); 1027 static assert(isBidirectionalRange!IntArray); 1028 static assert(isRandomAccessRange!IntArray); 1029 static assert(hasMobileElements!IntArray); 1030 static assert(is(ElementType!IntArray == int)); 1031 static assert(hasSwappableElements!IntArray); 1032 static assert(hasAssignableElements!IntArray); 1033 static assert(hasLvalueElements!IntArray); 1034 static assert(hasLength!IntArray); 1035 static assert(hasSlicing!IntArray); 1036 1037 auto array = IntArray(1024); 1038 assert(array.length == 0); 1039 assert(array.empty); 1040 array ~= 5; 1041 assert(!array.empty); 1042 assert(array.length == 1); 1043 assert(array.front == 5); 1044 assert(array[0] == 5); 1045 assert(array[0 .. 1].front == 5); 1046 assert(array[0 .. 0].empty); 1047 array ~= cast(int[2])[4, 3]; 1048 assert(array.length == 3); 1049 assert(array[1] == 4); 1050 assert(array[2] == 3); 1051 1052 for (int i = 0; i < 50000; i++) 1053 { 1054 array ~= i; 1055 array.popFront(); 1056 } 1057 1058 assert(array.length == 3); 1059 1060 array ~= array; 1061 assert(array.length == 6); 1062 assert((array ~ array).length == 12); 1063 assert(array.length == 6); 1064 1065 array[5] = 1; 1066 assert(array[5] == 1); 1067 auto copy = array[]; 1068 copy[5] = 2; 1069 assert(array[5] == 1); 1070 array[][5] = 1; 1071 assert(array[5] == 1); 1072 } 1073 1074 @system unittest 1075 { 1076 CyclicArray!int a; 1077 assert(a.empty); 1078 } 1079 1080 @system unittest 1081 { 1082 CyclicArray!int a; 1083 a.length = 10; 1084 assert(a.length == 10); 1085 assert(a.capacity >= a.length); 1086 } 1087 1088 @system unittest 1089 { 1090 struct Dumb 1091 { 1092 int x = 5; 1093 } 1094 1095 CyclicArray!Dumb a; 1096 a.length = 10; 1097 assert(a.length == 10); 1098 assert(a.capacity >= a.length); 1099 immutable cap = a.capacity; 1100 foreach (ref e; a) 1101 { 1102 e.x = 10; 1103 } 1104 a.length = 5; 1105 assert(a.length == 5); 1106 foreach (ref e; a) 1107 { 1108 assert(e.x == 10); 1109 } 1110 1111 a.length = 8; 1112 assert(a.length == 8); 1113 assert(Dumb.init.x == 5); 1114 foreach (const i; 0 .. 5) 1115 { 1116 assert(a[i].x == 10); 1117 } 1118 foreach (const i; 5 .. a.length) 1119 { 1120 assert(a[i].x == Dumb.init.x); 1121 } 1122 1123 a[] = Dumb(1); 1124 a.length = 20; 1125 foreach (const i; 0 .. 8) 1126 { 1127 assert(a[i].x == 1); 1128 } 1129 foreach (const i; 8 .. a.length) 1130 { 1131 assert(a[i].x == Dumb.init.x); 1132 } 1133 1134 // check if overlapping elements properly initialized 1135 a.length = 1; 1136 a.length = 20; 1137 assert(a[0].x == 1); 1138 foreach (e; a[1 .. $]) 1139 { 1140 assert(e.x == Dumb.init.x); 1141 } 1142 } 1143 1144 @system unittest 1145 { 1146 CyclicArray!int a = CyclicArray!int(1, 2, 3); 1147 //a._data._refCountedDebug = true; 1148 auto b = a.dup; 1149 assert(b == CyclicArray!int(1, 2, 3)); 1150 b.front = 42; 1151 assert(b == CyclicArray!int(42, 2, 3)); 1152 assert(a == CyclicArray!int(1, 2, 3)); 1153 } 1154 1155 @system unittest 1156 { 1157 auto a = CyclicArray!int(1, 2, 3); 1158 assert(a.length == 3); 1159 } 1160 1161 @system unittest 1162 { 1163 const CyclicArray!int a = [1, 2]; 1164 1165 assert(a[0] == 1); 1166 assert(a.front == 1); 1167 assert(a.back == 2); 1168 1169 static assert(!__traits(compiles, { a[0] = 1; })); 1170 static assert(!__traits(compiles, { a.front = 1; })); 1171 static assert(!__traits(compiles, { a.back = 1; })); 1172 1173 auto r = a[]; 1174 size_t i; 1175 foreach (e; r) 1176 { 1177 assert(e == i + 1); 1178 i++; 1179 } 1180 } 1181 1182 @system unittest 1183 { 1184 auto a = CyclicArray!int(1, 2, 3); 1185 a[1] *= 42; 1186 assert(a[1] == 84); 1187 } 1188 1189 @system unittest 1190 { 1191 auto a = CyclicArray!int(1, 2, 3); 1192 auto b = CyclicArray!int(11, 12, 13); 1193 auto c = a ~ b; 1194 assert(c == CyclicArray!int(1, 2, 3, 11, 12, 13)); 1195 assert(a ~ b[] == CyclicArray!int(1, 2, 3, 11, 12, 13)); 1196 assert(a ~ [4, 5] == CyclicArray!int(1, 2, 3, 4, 5)); 1197 } 1198 1199 @system unittest 1200 { 1201 auto a = CyclicArray!int(1, 2, 3); 1202 auto b = CyclicArray!int(11, 12, 13); 1203 a ~= b; 1204 assert(a == CyclicArray!int(1, 2, 3, 11, 12, 13)); 1205 } 1206 1207 @system unittest 1208 { 1209 auto a = CyclicArray!int(1, 2, 3, 4); 1210 assert(a.removeAny() == 4); 1211 assert(a == CyclicArray!int(1, 2, 3)); 1212 } 1213 1214 private struct structBug5920 1215 { 1216 int order; 1217 uint* pDestructionMask; 1218 ~this() @nogc 1219 { 1220 if (pDestructionMask) 1221 *pDestructionMask += 1 << order; 1222 } 1223 } 1224 1225 @system unittest 1226 { 1227 auto a = CyclicArray!int([1, 1]); 1228 a[1] = 0; //Check CyclicArray.opIndexAssign 1229 assert(a[1] == 0); 1230 a[1] += 1; //Check CyclicArray.opIndexOpAssign 1231 assert(a[1] == 1); 1232 1233 //Check CyclicArray.opIndexUnary 1234 ++a[0]; 1235 //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed 1236 assert(a[0] == 2); 1237 assert(+a[0] == +2); 1238 assert(-a[0] == -2); 1239 assert(~a[0] == ~2); 1240 1241 auto r = a[]; 1242 r[1] = 0; //Check CyclicArray.Range.opIndexAssign 1243 assert(r[1] == 0); 1244 r[1] += 1; //Check CyclicArray.Range.opIndexOpAssign 1245 assert(r[1] == 1); 1246 1247 //Check CyclicArray.Range.opIndexUnary 1248 ++r[0]; 1249 //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed 1250 assert(r[0] == 3); 1251 assert(+r[0] == +3); 1252 assert(-r[0] == -3); 1253 assert(~r[0] == ~3); 1254 } 1255 1256 @safe unittest 1257 { 1258 static struct S 1259 { 1260 bool b; 1261 alias b this; 1262 } 1263 1264 alias A = CyclicArray!S; 1265 alias B = CyclicArray!(shared bool); 1266 } 1267 1268 @system unittest 1269 { 1270 CyclicArray!int ai; 1271 ai ~= 1; 1272 assert(ai.front == 1); 1273 1274 static const arr = [1, 2, 3]; 1275 ai.insertBack(arr); 1276 }