1 module simplefixedsizearray;
2 
3 struct SFA(T,size_t Size = 32) {
4 	static assert(Size > 0);
5 	import std.traits : CopyTypeQualifiers, hasElaborateDestructor, hasMember;
6 
7 	size_t length;
8 	ubyte[T.sizeof * Size] store;
9 
10 	~this() {
11 		static if(hasElaborateDestructor!T) {
12 			this.removeAll();
13 		}
14 	}
15 
16 	struct SFARange(S,U) {
17 		S* ptr;
18 		size_t low;
19 		size_t high;
20 
21 		@property bool empty() const pure @safe nothrow @nogc {
22 			return this.low >= this.high;
23 		}
24 
25 		pragma(inline, true)
26 		@property size_t length() pure @safe nothrow @nogc {
27 			return cast(size_t)(this.high - this.low);
28 		}
29 
30 		@property ref U front() {
31 			return (*this.ptr)[this.low];
32 		}
33 
34 		@property ref U back() {
35 			return (*this.ptr)[this.high];
36 		}
37 
38 		ref T opIndex(const size_t idx) {
39 			return (*this.fsa)[this.low + idx];
40 		}
41 
42 		void popFront() pure @safe nothrow @nogc {
43 			++this.low;
44 		}
45 
46 		void popBack() pure @safe nothrow @nogc {
47 			--this.high;
48 		}
49 
50 		@property typeof(this) save() pure @safe nothrow @nogc {
51 			return this;
52 		}
53 	}
54 
55 	SFARange!(typeof(this), CopyTypeQualifiers!(S,T)) opSlice(this S)(size_t low, size_t high) {
56 		assert(low <= high);
57 		assert(low <= this.length);
58 		return typeof(return)(&this, low, high);
59 	}
60 
61 	@property bool empty() const @safe @nogc nothrow {
62 		return this.length == 0U;
63 	}
64 
65 	@property bool hasCapacity() const @safe @nogc nothrow {
66 		return (this.length * T.sizeof) < this.store.length;
67 	}
68 
69 	void insertBack(T t) {
70 		assert(this.length + 1 <= Size);
71 		*(cast(T*)(&this.store[cast(size_t)(this.length * T.sizeof)])) = t;
72 		++length;
73 	}
74 
75 	ref CopyTypeQualifiers!(S,T) opIndex(this S)(size_t idx) {
76 		idx *= T.sizeof;
77 		assert(idx < this.store.length);
78 		return *(cast(T*)&(this.store[idx]));
79 	}
80 
81 	@property ref CopyTypeQualifiers!(S,T) front(this S)() {
82 		assert(!this.empty);
83 		return *(cast(T*)(&this.store[0U]));
84 	}
85 
86 	@property ref CopyTypeQualifiers!(S,T) back(this S)() {
87 		import std.stdio;
88 		assert(!this.empty);
89 		return *(cast(T*)(&this.store[(this.length * T.sizeof) - T.sizeof]));
90 	}
91 
92 	void removeAll() {
93 		while(!this.empty) {
94 			this.removeBack();
95 		}
96 	}
97 
98 	void removeBack() {
99 		assert(!this.empty);
100 
101 		static if(hasElaborateDestructor!T) {
102 			static if(hasMember!(T, "__dtor")) {
103 				this.back().__dtor();
104 			} else static if(hasMember!(T, "__xdtor")) {
105 				this.back().__xdtor();
106 			} else {
107 				static assert(false);
108 			}
109 		}
110 
111 		--this.length;
112 	}
113 
114 	void remove(size_t idx) {
115 		assert(idx < this.length);
116 		static if(hasElaborateDestructor!T) {
117 			static if(hasMember!(T, "__dtor")) {
118 				this[idx].__dtor();
119 			} else static if(hasMember!(T, "__xdtor")) {
120 				this[idx].__xdtor();
121 			} else {
122 				static assert(false);
123 			}
124 		}
125 
126 		for(size_t i = idx * T.sizeof; i < ((this.length - 1U) * T.sizeof); ++i) {
127 			this.store[i] = this.store[i + T.sizeof];
128 		}
129 
130 		--this.length;
131 	}
132 }
133 
134 unittest {
135 	SFA!(int) a;
136 }
137 
138 unittest {
139 	SFA!(int) a;
140 	assert(a.hasCapacity);
141 	a.insertBack(10);
142 	assert(a[0] == 10);
143 	assert(a.front == 10);
144 	assert(a.back == 10);
145 	a.removeBack();
146 	assert(a.empty);
147 }
148 
149 unittest {
150 	struct Foo {
151 		size_t value;
152 		alias value this;
153 
154 		~this() {
155 		}
156 	}
157 
158 	void test(size_t Size, T)() {
159 		SFA!(T, Size) a;
160 		assert(a.empty);
161 		foreach(it; 0 .. Size) {
162 			assert(a.hasCapacity);
163 			a.insertBack(T(it));
164 			assert(a.length == it + 1);
165 			assert(a.front == 0);
166 			assert(a.back == it);
167 
168 			foreach(jt; 0 .. it + 1) {
169 				assert(a[jt] == jt);
170 			}
171 
172 			auto r = a[0 .. a.length - 1];
173 			assert(r.front == 0);
174 			assert(r.back == it);
175 		}
176 		assert(!a.hasCapacity);
177 	}
178 
179 	static foreach(tSize; [1,2,3,4,5,10,11,32,63,64]) {
180 		test!(tSize, size_t)();
181 		test!(tSize, Foo)();
182 	}
183 }
184 
185 unittest {
186 	import std.stdio;
187 	SFA!(int) a;
188 	foreach(it; [0,1,2,3,4,5,6,7,8,9]) {
189 		a.insertBack(it);
190 	}
191 
192 	a.remove(5);
193 
194 	foreach(idx, it; [0,1,2,3,4,6,7,8,9]) {
195 		assert(a[idx] == it);
196 	}
197 }
198 
199 unittest {
200 	SFA!int a;
201 	a.insertBack(1337);
202 	a.remove(0);
203 	assert(a.empty);
204 }