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 }