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