1 module fixedsizearray;
2 
3 import std.array : back;
4 import std.experimental.logger;
5 
6 struct FixedSizeArraySlice(FSA,T, size_t Size) {
7 	FSA* fsa;
8 	short low;
9 	short high;
10 
11 	pragma(inline, true)
12 	this(FSA* fsa, short low, short high) {
13 		this.fsa = fsa;
14 		this.low = low;
15 		this.high = high;
16 	}
17 
18 	pragma(inline, true)
19 	@property bool empty() const pure @safe nothrow @nogc {
20 		return this.low >= this.high;
21 	}
22 
23 	pragma(inline, true)
24 	@property size_t length() pure @safe nothrow @nogc {
25 		return cast(size_t)(this.high - this.low);
26 	}
27 
28 	pragma(inline, true)
29 	@property ref T front() {
30 		return (*this.fsa)[cast(size_t)this.low];
31 	}
32 
33 	pragma(inline, true)
34 	@property ref const(T) front() const {
35 		return (*this.fsa)[cast(size_t)this.low];
36 	}
37 
38 	pragma(inline, true)
39 	@property ref T back() {
40 		return (*this.fsa)[cast(size_t)(this.high - 1)];
41 	}
42 
43 	pragma(inline, true)
44 	@property ref const(T) back() const {
45 		return (*this.fsa)[cast(size_t)(this.high - 1)];
46 	}
47 
48 	pragma(inline, true)
49 	void insertBack(S)(auto ref S s) {
50 		(*this.fsa).insertBack(s);
51 	}
52 
53 	/// Ditto
54 	alias put = insertBack;
55 
56 	pragma(inline, true)
57 	ref T opIndex(const size_t idx) {
58 		return (*this.fsa)[this.low + idx];
59 	}
60 
61 	pragma(inline, true)
62 	void popFront() pure @safe nothrow @nogc {
63 		++this.low;
64 	}
65 
66 	pragma(inline, true)
67 	void popBack() pure @safe nothrow @nogc {
68 		--this.high;
69 	}
70 
71 	pragma(inline, true)
72 	@property typeof(this) save() pure @safe nothrow @nogc {
73 		return this;
74 	}
75 
76 	pragma(inline, true)
77 	@property const(typeof(this)) save() const pure @safe nothrow @nogc {
78 		return this;
79 	}
80 
81 	pragma(inline, true)
82 	typeof(this) opIndex() pure @safe nothrow @nogc {
83 		return this;
84 	}
85 
86 	pragma(inline, true)
87 	typeof(this) opIndex(size_t l, size_t h) pure @safe nothrow @nogc {
88 		return this.opSlice(l, h);
89 	}
90 
91 	pragma(inline, true)
92 	typeof(this) opSlice(size_t l, size_t h) pure @safe nothrow @nogc {
93 		assert(l <= h);
94 		return typeof(this)(this.fsa, 
95 				cast(short)(this.low + l),
96 				cast(short)(this.low + h)
97 			);
98 	}
99 }
100 
101 struct FixedSizeArray(T,size_t Size = 32) {
102 	import std.traits;
103 	enum ByteCap = T.sizeof * Size;
104 	align(8) void[ByteCap] store;
105 	long base;
106 	long length_;
107 
108 	/** If `true` no destructor of any element stored in the FixedSizeArray
109 	  will be called.
110 	*/
111 	bool disableDtor;
112 
113 	pragma(inline, true)
114 	this(Args...)(Args args) {
115 		foreach(it; args) {
116 			static if(isAssignable!(T,typeof(it))) {
117 				this.insertBack(it);
118 			}
119 		}
120 	}
121 
122 	pragma(inline, true)
123 	~this() {
124 		static if(hasElaborateDestructor!T) {
125 			if(!this.disableDtor) {
126 				this.removeAll();
127 			}
128 		}
129 	}
130 
131 	pragma(inline, true)
132 	size_t capacity() const @nogc @safe pure nothrow {
133 		return Size;
134 	}
135 
136 	/** This function inserts an `S` element at the back if there is space.
137 	Otherwise the behaviour is undefined.
138 	*/
139 	pragma(inline, true)
140 	void insertBack(S)(auto ref S t) @trusted if(is(Unqual!(S) == T)) {
141 		import std.conv : emplace;
142 		assert(this.length + 1 <= Size);
143 
144 		*(cast(T*)(&this.store[
145 			cast(size_t)((this.base + this.length_) % ByteCap)
146 		])) = t;
147 		this.length_ += T.sizeof;
148 	}
149 
150 	/// Ditto
151 	pragma(inline, true)
152 	void insertBack(S)(auto ref S s) @trusted if(!is(Unqual!(S) == T)) {
153 		import std.traits;
154 		import std.conv;
155 
156 		static if((isIntegral!T || isFloatingPoint!T) 
157 				|| (isSomeChar!T && isSomeChar!S && T.sizeof >= S.sizeof)) 
158 		{
159 			this.insertBack!T(cast(T)(s));
160 		} else static if (isSomeChar!T && isSomeChar!S && T.sizeof < S.sizeof) {
161             /* may throwable operation:
162              * - std.utf.encode
163              */
164             // must do some transcoding around here
165             import std.utf : encode;
166             Unqual!T[T.sizeof == 1 ? 4 : 2] encoded;
167             auto len = encode(encoded, s);
168 			foreach(T it; encoded[0 .. len]) {
169 				 this.insertBack!T(it);
170 			}
171         } else static if(isAssignable!(T,S)) {
172 			*(cast(T*)(&this.store[
173 				cast(size_t)((this.base + this.length_) % ByteCap)
174 			])) = s;
175 			this.length_ += T.sizeof;
176 		} else {
177 			static assert(false);
178 		}
179 	}
180 
181 	/// Ditto
182 	pragma(inline, true)
183 	void insertBack(S)(auto ref S defaultValue, size_t num) {
184 		for(size_t i = 0; i < num; ++i) {
185 			this.insertBack(defaultValue);
186 		}
187 	}
188 
189 	///
190 	pure @safe unittest {
191 		FixedSizeArray!(int,32) fsa;
192 		fsa.insertBack(1337);
193 		assert(fsa.length == 1);
194 		assert(fsa[0] == 1337);
195 
196 		fsa.insertBack(99, 5);
197 
198 		foreach(it; fsa[1 .. fsa.length]) {
199 			assert(it == 99);
200 		}
201 	}
202 
203 	/** This function inserts an `S` element at the front if there is space.
204 	Otherwise the behaviour is undefined.
205 	*/
206 	pragma(inline, true)
207 	void insertFront(S)(auto ref S t) @trusted if(is(Unqual!(S) == T)) {
208 		import std.conv : emplace;
209 		import std.stdio;
210 		assert(this.length + 1 <= Size);
211 
212 		this.base -= T.sizeof;
213 		if(this.base < 0) {
214 			this.base = cast(typeof(this.base))((ByteCap) - T.sizeof);
215 		}
216 
217 		*(cast(T*)(&this.store[cast(size_t)this.base])) = t;
218 		this.length_ += T.sizeof;
219 	}
220 
221 	pure @safe unittest {
222 		FixedSizeArray!(int,32) fsa;
223 		fsa.insertFront(1337);
224 		assert(fsa.length == 1);
225 		assert(fsa[0] == 1337);
226 		assert(fsa.front == 1337);
227 		assert(fsa.back == 1337);
228 
229 		fsa.removeBack();
230 		assert(fsa.length == 0);
231 		assert(fsa.empty);
232 		fsa.insertFront(1336);
233 
234 		assert(fsa.length == 1);
235 		assert(fsa[0] == 1336);
236 		assert(fsa.front == 1336);
237 		assert(fsa.back == 1336);
238 	}
239 
240 	pure @safe unittest {
241 		FixedSizeArray!(int,16) fsa;
242 		for(int i = 0; i < 32; ++i) {
243 			fsa.insertFront(i);
244 			assert(fsa.length == 1);
245 			assert(!fsa.empty);
246 			assert(fsa.front == i);
247 			assert(fsa.back == i);
248 			fsa.removeFront();
249 			assert(fsa.length == 0);
250 			assert(fsa.empty);
251 		}
252 	}
253 
254 	pure @safe unittest {
255 		FixedSizeArray!(int,16) fsa;
256 		for(int i = 0; i < 32; ++i) {
257 			fsa.insertFront(i);
258 			assert(fsa.length == 1);
259 			assert(!fsa.empty);
260 			assert(fsa.front == i);
261 			assert(fsa.back == i);
262 			fsa.removeBack();
263 			assert(fsa.length == 0);
264 			assert(fsa.empty);
265 		}
266 	}
267 
268 	pure @safe nothrow unittest {
269 		FixedSizeArray!(int,16) fsa;
270 		for(int i = 0; i < 32; ++i) {
271 			fsa.insertBack(i);
272 			assert(fsa.length == 1);
273 			assert(!fsa.empty);
274 			assert(fsa.front == i);
275 			assert(fsa.back == i);
276 			fsa.removeFront();
277 			assert(fsa.length == 0);
278 			assert(fsa.empty);
279 		}
280 	}
281 
282 	/** This function emplaces an `S` element at the back if there is space.
283 	Otherwise the behaviour is undefined.
284 	*/
285 	pragma(inline, true)
286 	void emplaceBack(Args...)(auto ref Args args) {
287 		import std.conv : emplace;
288 		assert(this.length + 1 <= Size);
289 
290 		emplace(cast(T*)(&this.store[
291 			cast(size_t)((this.base + this.length_) % ByteCap)]
292 		), args);
293 		this.length_ += T.sizeof;
294 	}
295 
296 	/** This function removes an element form the back of the array.
297 	*/
298 	pragma(inline, true)
299 	void removeBack() {
300 		assert(!this.empty);
301 
302 		static if(hasElaborateDestructor!T) {
303 			if(!this.disableDtor) {
304 				static if(hasMember!(T, "__dtor")) {
305 					this.back().__dtor();
306 				} else static if(hasMember!(T, "__xdtor")) {
307 					this.back().__xdtor();
308 				} else {
309 					static assert(false);
310 				}
311 			}
312 		}
313 
314 		this.length_ -= T.sizeof;
315 	}
316 
317 	/** This function removes an element form the front of the array.
318 	*/
319 	pragma(inline, true)
320 	void removeFront() {
321 		assert(!this.empty);
322 
323 		static if(hasElaborateDestructor!T) {
324 			if(!this.disableDtor) {
325 				static if(hasMember!(T, "__dtor")) {
326 					this.back().__dtor();
327 				} else static if(hasMember!(T, "__xdtor")) {
328 					this.back().__xdtor();
329 				} else {
330 					static assert(false);
331 				}
332 			}
333 		}
334 
335 		//this.begin = (this.begin + T.sizeof) % (Size * T.sizeof);
336 		this.base += T.sizeof;
337 		if(this.base >= ByteCap) {
338 			this.base = 0;
339 		}
340 		this.length_ -= T.sizeof;
341 	}
342 
343 	pure @safe unittest {
344 		FixedSizeArray!(int,32) fsa;
345 		fsa.insertBack(1337);
346 		assert(fsa.length == 1);
347 		assert(fsa[0] == 1337);
348 		
349 		fsa.removeBack();
350 		assert(fsa.length == 0);
351 		assert(fsa.empty);
352 	}
353 
354 	/** This function removes all elements from the array.
355 	*/
356 	pragma(inline, true)
357 	void removeAll() {
358 		while(!this.empty) {
359 			this.removeBack();
360 		}
361 	}
362 
363 	pure @safe unittest {
364 		FixedSizeArray!(int,32) fsa;
365 		fsa.insertBack(1337);
366 		fsa.insertBack(1338);
367 		assert(fsa.length == 2);
368 		assert(fsa[0] == 1337);
369 		assert(fsa[1] == 1338);
370 		
371 		fsa.removeAll();
372 		assert(fsa.length == 0);
373 		assert(fsa.empty);
374 	}
375 
376 	pragma(inline, true)
377 	void remove(ulong idx) {
378 		import std.stdio;
379 		if(idx == 0) {
380 			this.removeFront();
381 		} else if(idx == this.length - 1) {
382 			this.removeBack();
383 		} else {
384 			for(long i = idx + 1; i < this.length; ++i) {
385 				this[cast(size_t)(i - 1)] = this[cast(size_t)i];
386 			}
387 			this.removeBack();
388 		}
389 	}
390 
391 	unittest {
392 		FixedSizeArray!(int,16) fsa;
393 		foreach(i; 0..10) {
394 			fsa.insertBack(i);
395 		}
396 		fsa.remove(1);
397 		foreach(idx, i; [0,2,3,4,5,6,7,8,9]) {
398 			assert(fsa[idx] == i);
399 		}
400 		fsa.remove(0);
401 		foreach(idx, i; [2,3,4,5,6,7,8,9]) {
402 			assert(fsa[idx] == i);
403 		}
404 		fsa.remove(7);
405 		foreach(idx, i; [2,3,4,5,6,7,8]) {
406 			assert(fsa[idx] == i);
407 		}
408 		fsa.remove(5);
409 		foreach(idx, i; [2,3,4,5,6,8]) {
410 			assert(fsa[idx] == i);
411 		}
412 		fsa.remove(1);
413 		foreach(idx, i; [2,4,5,6,8]) {
414 			assert(fsa[idx] == i);
415 		}
416 		fsa.remove(0);
417 		foreach(idx, i; [4,5,6,8]) {
418 			assert(fsa[idx] == i);
419 		}
420 		fsa.remove(0);
421 		foreach(idx, i; [5,6,8]) {
422 			assert(fsa[idx] == i);
423 		}
424 	}
425 
426 	/** Access the last or the first element of the array.
427 	*/
428 	pragma(inline, true)
429 	@property ref T back() @trusted {
430 		assert(!this.empty);
431 		return *(cast(T*)(&this.store[
432 			cast(size_t)(this.base + this.length_ - T.sizeof) % ByteCap
433 		]));
434 	}
435 
436 	pragma(inline, true)
437 	@property ref const(T) back() const @trusted {
438 		assert(!this.empty);
439 		return *(cast(T*)(&this.store[
440 			cast(size_t)(this.base + this.length_ - T.sizeof) % ByteCap
441 		]));
442 	}
443 
444 	/// Ditto
445 	pragma(inline, true)
446 	@property ref T front() @trusted {
447 		assert(!this.empty);
448 		return *(cast(T*)(&this.store[cast(size_t)this.base]));
449 	}
450 
451 	pragma(inline, true)
452 	@property ref const(T) front() const @trusted {
453 		assert(!this.empty);
454 		return *(cast(T*)(&this.store[cast(size_t)this.base]));
455 	}
456 
457 	///
458 	pure @safe unittest {
459 		FixedSizeArray!(int,32) fsa;
460 		fsa.insertBack(1337);
461 		fsa.insertBack(1338);
462 		assert(fsa.length == 2);
463 
464 		assert(fsa.front == 1337);
465 		assert(fsa.back == 1338);
466 	}
467 
468 	/** Use an index to access the array.
469 	*/
470 	pragma(inline, true)
471 	ref T opIndex(const size_t idx) @trusted {
472 		import std.format : format;
473 		assert(idx <= this.length, format("%s %s", idx, this.length));
474 		return *(cast(T*)(&this.store[
475 				cast(size_t)((this.base + idx * T.sizeof) % ByteCap)
476 		]));
477 	}
478 
479 	/// Ditto
480 	pragma(inline, true)
481 	ref const(T) opIndex(const size_t idx) @trusted const {
482 		import std.format : format;
483 		assert(idx <= this.length, format("%s %s", idx, this.length));
484 		return *(cast(const(T)*)(&this.store[
485 				cast(size_t)((this.base + idx * T.sizeof) % ByteCap)
486 		]));
487 	}
488 
489 	///
490 	pure @safe unittest {
491 		FixedSizeArray!(int,32) fsa;
492 		fsa.insertBack(1337);
493 		fsa.insertBack(1338);
494 		assert(fsa.length == 2);
495 
496 		assert(fsa[0] == 1337);
497 		assert(fsa[1] == 1338);
498 	}
499 
500 	/// Gives the length of the array.
501 	pragma(inline, true)
502 	@property size_t length() const pure @nogc nothrow {
503 		return cast(size_t)(this.length_ / T.sizeof);
504 	}
505 
506 	/// Ditto
507 	pragma(inline, true)
508 	@property bool empty() const pure @nogc nothrow {
509 		return this.length == 0;
510 	}
511 
512 	///
513 	pure @safe nothrow unittest {
514 		FixedSizeArray!(int,32) fsa;
515 		assert(fsa.empty);
516 		assert(fsa.length == 0);
517 
518 		fsa.insertBack(1337);
519 		fsa.insertBack(1338);
520 
521 		assert(fsa.length == 2);
522 		assert(!fsa.empty);
523 	}
524 
525 	pragma(inline, true)
526 	FixedSizeArraySlice!(typeof(this),T,Size) opSlice() pure @nogc @safe nothrow {
527 		return FixedSizeArraySlice!(typeof(this),T,Size)(&this, cast(short)0, 
528 				cast(short)this.length
529 		);
530 	}
531 	
532 	pragma(inline, true)
533 	FixedSizeArraySlice!(typeof(this),T,Size) opSlice(const size_t low, 
534 			const size_t high) 
535 			pure @nogc @safe nothrow 
536 	{
537 		return FixedSizeArraySlice!(typeof(this),T,Size)(&this, cast(short)low, 
538 				cast(short)high
539 		);
540 	}
541 
542 	pragma(inline, true)
543 	auto opSlice() pure @nogc @safe nothrow const {
544 		return FixedSizeArraySlice!(typeof(this),const(T),Size)
545 			(&this, cast(short)0, cast(short)this.length);
546 	}
547 	
548 	pragma(inline, true)
549 	auto opSlice(const size_t low, const size_t high) pure @nogc @safe nothrow const 
550 	{
551 		return FixedSizeArraySlice!(typeof(this),const(T),Size)
552 			(&this, cast(short)low, cast(short)high);
553 	}
554 }
555 
556 unittest {
557 	FixedSizeArray!(int, 10) fsa;
558 	assert(fsa.empty);
559 
560 	fsa.insertBack(1);
561 	assert(fsa.front == 1);
562 	assert(fsa.back == 1);
563 	assert(fsa[0] == 1);
564 
565 	fsa.insertFront(0);
566 	assert(fsa.front == 0);
567 	assert(fsa.back == 1);
568 	assert(fsa[0] == 0);
569 	assert(fsa[1] == 1);
570 
571 	int idx = 0;
572 	foreach(it; fsa[0 .. fsa.length]) {
573 		assert(it == idx);
574 		++idx;
575 	}
576 
577 	fsa.removeFront();
578 	assert(fsa.front == 1);
579 	assert(fsa.back == 1);
580 	assert(fsa[0] == 1);
581 
582 	fsa.removeBack();
583 	assert(fsa.empty);
584 }
585 unittest {
586 	import exceptionhandling;
587 	import std.stdio;
588 
589 	FixedSizeArray!(int, 16) fsa;
590 	assert(fsa.empty);
591 	cast(void)assertEqual(fsa.length, 0);
592 
593 	fsa.insertBack(1);
594 	assert(!fsa.empty);
595 	cast(void)assertEqual(fsa.length, 1);
596 	cast(void)assertEqual(fsa.front, 1);
597 	cast(void)assertEqual(fsa.back, 1);
598 
599 	fsa.insertBack(2);
600 	assert(!fsa.empty);
601 	cast(void)assertEqual(fsa.length, 2);
602 	cast(void)assertEqual(fsa.front, 1);
603 	cast(void)assertEqual(fsa.back, 2);
604 
605 	fsa.removeFront();
606 	assert(!fsa.empty);
607 	cast(void)assertEqual(fsa.length, 1);
608 	cast(void)assertEqual(fsa.front, 2);
609 	cast(void)assertEqual(fsa.back, 2);
610 
611 	fsa.removeBack();
612 	//writefln("%s %s", fsa.begin, fsa.end);
613 	assert(fsa.empty);
614 	cast(void)assertEqual(fsa.length, 0);
615 }
616 
617 unittest {
618 	import std.format;
619 
620 	FixedSizeArray!(char,64) fsa;
621 	formattedWrite(fsa[], "%s %s %s", "Hello", "World", 42);
622 	//assert(cast(string)fsa == "Hello World 42", cast(string)fsa);
623 }
624 
625 unittest {
626 	import exceptionhandling;
627 
628 	FixedSizeArray!(int,16) fsa;
629 	auto a = [0,1,2,4,32,64,1024,2048,65000];
630 	foreach(idx, it; a) {
631 		fsa.insertBack(it);
632 		assertEqual(fsa.length, idx + 1);
633 		assertEqual(fsa.back, it);
634 		for(int i = 0; i < idx; ++i) {
635 			assertEqual(fsa[i], a[i]);
636 		}
637 	}
638 }
639 
640 unittest {
641 	import exceptionhandling;
642 	import std.traits;
643 	import std.meta;
644 	import std.range;
645 	import std.stdio;
646 	foreach(Type; AliasSeq!(byte,int,long)) {
647 		FixedSizeArray!(Type,16) fsa2;
648 		static assert(isInputRange!(typeof(fsa2[])));
649 		static assert(isForwardRange!(typeof(fsa2[])));
650 		static assert(isBidirectionalRange!(typeof(fsa2[])));
651 		foreach(idx, it; [[0], [0,1,2,3,4], [2,3,6,5,6,21,9,36,61,62]]) {
652 			FixedSizeArray!(Type,16) fsa;
653 			foreach(jdx, jt; it) {
654 				fsa.insertBack(jt);
655 				//writefln("%s idx %d jdx %d length %d", Type.stringof, idx, jdx, fsa.length);
656 				cast(void)assertEqual(fsa.length, jdx + 1);
657 				foreach(kdx, kt; it[0 .. jdx]) {
658 					assertEqual(fsa[kdx], kt);
659 				}
660 
661 				{
662 					auto forward = fsa[];
663 					auto forward2 = forward;
664 					cast(void)assertEqual(forward.length, jdx + 1);
665 					for(size_t i = 0; i < forward.length; ++i) {
666 						cast(void)assertEqual(forward[i], it[i]);
667 						cast(void)assertEqual(forward2.front, it[i]);
668 						forward2.popFront();
669 					}
670 					assert(forward2.empty);
671 
672 					auto backward = fsa[];
673 					auto backward2 = backward.save;
674 					cast(void)assertEqual(backward.length, jdx + 1);
675 					for(size_t i = 0; i < backward.length; ++i) {
676 						cast(void)assertEqual(backward[backward.length - i - 1],
677 								it[jdx - i]
678 						);
679 
680 						cast(void)assertEqual(backward2.back, 
681 								it[0 .. jdx + 1 - i].back
682 						);
683 						backward2.popBack();
684 					}
685 					assert(backward2.empty);
686 					auto forward3 = fsa[].save;
687 					auto forward4 = fsa[0 .. jdx + 1];
688 
689 					while(!forward3.empty && !forward4.empty) {
690 						cast(void)assertEqual(forward3.front, forward4.front);
691 						cast(void)assertEqual(forward3.back, forward4.back);
692 						forward3.popFront();
693 						forward4.popFront();
694 					}
695 					assert(forward3.empty);
696 					assert(forward4.empty);
697 				}
698 
699 				{
700 					const(FixedSizeArray!(Type,16))* constFsa;
701 					constFsa = &fsa;
702 					auto forward = (*constFsa)[];
703 					auto forward2 = forward.save;
704 					cast(void)assertEqual(forward.length, jdx + 1);
705 					for(size_t i = 0; i < forward.length; ++i) {
706 						cast(void)assertEqual(cast(int)forward[i], it[i]);
707 						cast(void)assertEqual(cast(int)forward2.front, it[i]);
708 						forward2.popFront();
709 					}
710 					assert(forward2.empty);
711 
712 					auto backward = (*constFsa)[];
713 					auto backward2 = backward.save;
714 					cast(void)assertEqual(backward.length, jdx + 1);
715 					for(size_t i = 0; i < backward.length; ++i) {
716 						cast(void)assertEqual(backward[backward.length - i - 1],
717 								it[jdx - i]
718 						);
719 
720 						cast(void)assertEqual(backward2.back, 
721 								it[0 .. jdx + 1 - i].back
722 						);
723 						backward2.popBack();
724 					}
725 					assert(backward2.empty);
726 					auto forward3 = (*constFsa)[];
727 					auto forward4 = (*constFsa)[0 .. jdx + 1];
728 
729 					while(!forward3.empty && !forward4.empty) {
730 						cast(void)assertEqual(forward3.front, forward4.front);
731 						cast(void)assertEqual(forward3.back, forward4.back);
732 						forward3.popFront();
733 						forward4.popFront();
734 					}
735 					assert(forward3.empty);
736 					assert(forward4.empty);
737 				}
738 			}
739 		}
740 	}
741 }
742 
743 unittest {
744 	import exceptionhandling;
745 
746 	int cnt;
747 	int cnt2;
748 
749 	struct Foo {
750 		int* cnt;
751 		this(int* cnt) { this.cnt = cnt; }
752 		~this() { if(cnt) { ++(*cnt); } }
753 	}
754 
755 	int i = 0;
756 	for(; i < 1000; ++i) {
757 		{
758 			FixedSizeArray!(Foo) fsa;
759 			fsa.insertBack(Foo(&cnt));
760 			fsa.insertBack(Foo(&cnt));
761 			fsa.insertBack(Foo(&cnt));
762 			fsa.insertBack(Foo(&cnt));
763 		}
764 
765 		cast(void)assertEqual(cnt, 8 * i + 8);
766 
767 		{
768 			FixedSizeArray!(Foo) fsa;
769 			fsa.emplaceBack(&cnt2);
770 			fsa.emplaceBack(&cnt2);
771 			fsa.emplaceBack(&cnt2);
772 			fsa.emplaceBack(&cnt2);
773 		}
774 		cast(void)assertEqual(cnt2, 4 * i + 4);
775 	}
776 }
777 
778 // Test case Issue #2
779 unittest {
780 	import exceptionhandling;
781 
782 	FixedSizeArray!(int,2) fsa;
783 	fsa.insertBack(0);
784 	fsa.insertBack(1);
785 
786 	assertEqual(fsa[0], 0);	
787 	assertEqual(fsa[1], 1);	
788 	assertEqual(fsa.front, 0);
789 	assertEqual(fsa.back, 1);
790 }
791 
792 unittest {
793 	import exceptionhandling;
794 	import std.stdio;
795 	string s = "Hellö Wärlß";
796 	{
797 		FixedSizeArray!(char,32) fsa;
798 		foreach(dchar c; s) {
799 			fsa.insertBack(c);
800 		}
801 		for(int i = 0; i < s.length; ++i) {
802 			assert(fsa[i] == s[i]);
803 		}
804 	}
805 	{
806 		import std.format;
807 		FixedSizeArray!(char,32) fsa;
808 		formattedWrite(fsa[], s);
809 		for(int i = 0; i < s.length; ++i) {
810 			assert(fsa[i] == s[i]);
811 		}
812 	}
813 }
814 
815 unittest {
816 	import std.stdio;
817 	import core.memory;
818 	enum size = 128;
819 	auto arrays = new FixedSizeArray!(int, size)[size];
820 	GC.removeRoot(arrays.ptr);
821 	//FixedSizeArray!(int, size)[size] arrays;
822 	foreach (i; 0..size) {
823 	    foreach (j; 0..size) {
824 			assert(arrays[i].length == j);
825 	        arrays[i].insertBack(i * 1000 + j);
826 	    }
827 	}
828 	/*foreach(ref it; arrays) {
829 		writef("%d ", it.length);
830 	}
831 	writeln();*/
832 	bool[int] o;
833 	foreach (i; 0..size) {
834 	    foreach (j; 0..size) {
835 			assert(arrays[i][j] !in o);
836 	        o[arrays[i][j]] = true;
837 	    }
838 	}
839 	assert(size * size == o.length);
840 }
841 
842 // issue #1 won't fix not sure why
843 unittest {
844 	import std.stdio;
845 	import core.memory;
846 	enum size = 256;
847 	auto arrays = new FixedSizeArray!(Object,size)();
848 	//FixedSizeArray!(Object, size) arrays;
849 	foreach (i; 0..size) {
850 		auto o = new Object();
851 		assert(arrays.length == i);
852 		foreach(it; (*arrays)[]) {
853 			assert(it !is null);
854 			assert(it.toHash());
855 		}
856 	    arrays.insertBack(o);
857 		assert(arrays.back is o);
858 		assert(!arrays.empty);
859 		assert(arrays.length == i + 1);
860 	}
861 
862 	assert(arrays.length == size);
863 	for(int i = 0; i < size; ++i) {
864 		assert((*arrays)[i] !is null);
865 		assert((*arrays)[i].toHash());
866 	}
867 	bool[Object] o;
868 	foreach (i; 0..size) {
869 		assert((*arrays)[i] !is null);
870 		assert((*arrays)[i] !in o);
871 	    o[(*arrays)[i]] = true;
872 	    
873 	}
874 	assert(size == o.length);
875 }
876 
877 unittest {
878 	import exceptionhandling;
879 	FixedSizeArray!(int,16) fsa;
880 	fsa.insertFront(1337);
881 	assert(!fsa.empty);
882 	assertEqual(fsa.length, 1);
883 	assertEqual(fsa.back, 1337);
884 	assertEqual(fsa.front, 1337);
885 	assertEqual(fsa.base, 15 * int.sizeof);
886 }
887 
888 // Test case Issue #2
889 unittest {
890 	enum size = 256;
891 	auto arrays = new FixedSizeArray!(Object, size * Object.sizeof)[size];
892 	foreach (i; 0..size) {
893 	    foreach (j; 0..size) {
894 	        arrays[i].insertBack(new Object);
895 	    }
896 	}
897 	bool[Object] o;
898 	foreach (i; 0..size) {
899 	    foreach (j; 0..size) {
900 	        o[arrays[i][j]] = true;
901 	    }
902 	}
903 	assert(o.length == size * size);
904 }
905 
906 unittest {
907 	import exceptionhandling;
908 	struct Foo {
909 		int* a;
910 		this(int* a) {
911 			this.a = a;
912 		}
913 		~this() {
914 			if(this.a !is null) {
915 				++(*a);
916 			}
917 		}
918 	}
919 
920 	{
921 		int a = 0;
922 		{
923 			FixedSizeArray!(Foo,16) fsa;
924 			for(int i = 0; i < 10; ++i) {
925 				fsa.emplaceBack(&a);
926 			}
927 		}
928 		assertEqual(a, 10);
929 	}
930 	{
931 		int a = 0;
932 		{
933 			FixedSizeArray!(Foo,16) fsa;
934 			fsa.disableDtor = true;
935 			for(int i = 0; i < 10; ++i) {
936 				fsa.emplaceBack(&a);
937 			}
938 		}
939 		assertEqual(a, 0);
940 	}
941 }
942 
943 unittest {
944 	import std.range.primitives : hasAssignableElements, hasSlicing, isRandomAccessRange;
945 	FixedSizeArray!(int,32) fsa;
946 	auto s = fsa[];
947 	static assert(hasSlicing!(typeof(s)));
948 	static assert(isRandomAccessRange!(typeof(s)));
949 	static assert(hasAssignableElements!(typeof(s)));
950 }
951 
952 unittest {
953 	import exceptionhandling;
954 
955 	FixedSizeArray!(int,32) fsa;
956 	for(int i = 0; i < 32; ++i) {
957 		fsa.insertBack(i);	
958 	}
959 
960 	auto s = fsa[];
961 	for(int i = 0; i < 32; ++i) {
962 		assert(s[i] == i);
963 	}
964 	s = s[0 .. 33];
965 	for(int i = 0; i < 32; ++i) {
966 		assert(s[i] == i);
967 	}
968 
969 	auto t = s.save;
970 	s.popFront();
971 	for(int i = 0; i < 32; ++i) {
972 		assert(t[i] == i);
973 	}
974 
975 	auto r = t[10, 20];
976 	for(int i = 10; i < 20; ++i) {
977 		assertEqual(r[i-10], fsa[i]);
978 	}
979 
980 	foreach(ref it; r) {
981 		it = 0;
982 	}
983 
984 	for(int i = 10; i < 20; ++i) {
985 		assertEqual(r[i-10], 0);
986 	}
987 }
988 
989 unittest {
990 	FixedSizeArray!(int,32) fsaM;
991 	for(int i = 0; i < 32; ++i) {
992 		fsaM.insertBack(i);	
993 	}
994 
995 	const(FixedSizeArray!(int,32)) fsa = fsaM;
996 
997 	auto s = fsa[];
998 	for(int i = 0; i < 32; ++i) {
999 		assert(s[i] == i);
1000 	}
1001 	s = s[0 .. 33];
1002 	for(int i = 0; i < 32; ++i) {
1003 		assert(s[i] == i);
1004 	}
1005 
1006 	auto t = s.save;
1007 	s.popFront();
1008 	for(int i = 0; i < 32; ++i) {
1009 		assert(t[i] == i);
1010 	}
1011 }
1012 
1013 unittest {
1014 	import std.random : Random, uniform;
1015 	import std.format : format;
1016 	struct Data {
1017 		ulong a, b, c, d, e;
1018 	}
1019 
1020 	auto rnd = Random(1337);
1021 	FixedSizeArray!(Data,128) a;
1022 	for(size_t i = 0; i < 4096; ++i) {
1023 		Data d;
1024 		d.a = i;
1025 		d.b = i;
1026 		d.c = i;
1027 		d.d = i;
1028 		d.e = i;
1029 
1030 		a.insertBack(d);
1031 
1032 		int c = uniform(4,10, rnd);
1033 		while(a.length > c) {
1034 			a.removeFront();
1035 		}
1036 		assert(!a.empty);
1037 		assert(a.length <= c, format("%d < %d", a.length, c));
1038 
1039 		auto r = a[];
1040 		assert(r.back.a == i);
1041 		assert(r.front.a <= i);
1042 
1043 		foreach(it; r[]) {
1044 			assert(it.a <= i);
1045 		}
1046 
1047 		auto cr = (*(cast(const(typeof(a))*)(&a)));
1048 		assert(cr.back.a == i);
1049 		assert(cr.front.a <= i);
1050 
1051 		auto crr = cr[];
1052 		assert(crr.front.a <= i);
1053 		assert(crr.back.a <= i);
1054 		foreach(it; crr.save) {
1055 			assert(it.a <= i);
1056 		}
1057 	}
1058 }