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 	static if(Size <= ubyte.max) {
8 		ubyte length_;
9 	} else static if(Size <= ushort.max) {
10 		ushort length_;
11 	} else {
12 		size_t length_;
13 	}
14 
15 	ubyte[T.sizeof * Size] store;
16 
17 	~this() {
18 		static if(hasElaborateDestructor!T) {
19 			this.removeAll();
20 		}
21 	}
22 
23 	struct SFARange(S,U) {
24 		S* ptr;
25 		size_t low;
26 		size_t high;
27 
28 		@property bool empty() const pure @safe nothrow @nogc {
29 			return this.low >= this.high;
30 		}
31 
32 		pragma(inline, true)
33 		@property size_t length() pure @safe nothrow @nogc {
34 			return cast(size_t)(this.high - this.low);
35 		}
36 
37 		@property ref U front() {
38 			return (*this.ptr)[this.low];
39 		}
40 
41 		@property ref U back() {
42 			return (*this.ptr)[this.high - 1];
43 		}
44 
45 		ref T opIndex(const size_t idx) {
46 			return (*this.ptr)[this.low + idx];
47 		}
48 
49 		void popFront() pure @safe nothrow @nogc {
50 			++this.low;
51 		}
52 
53 		void popBack() pure @safe nothrow @nogc {
54 			--this.high;
55 		}
56 
57 		@property typeof(this) save() pure @safe nothrow @nogc {
58 			return this;
59 		}
60 	}
61 
62 	SFARange!(typeof(this), CopyTypeQualifiers!(S,T)) opSlice(this S)(size_t low, size_t high) {
63 		assert(low <= high);
64 		assert(low <= this.length_);
65 		return typeof(return)(&this, low, high);
66 	}
67 
68 	SFARange!(typeof(this), CopyTypeQualifiers!(S,T)) opSlice(this S)() {
69 		return typeof(return)(&this, 0U, this.length);
70 	}
71 
72 	@property size_t length() const @safe pure nothrow @nogc {
73 		return cast(size_t)this.length_;
74 	}
75 
76 	@property bool empty() const @safe @nogc nothrow {
77 		return this.length_ == 0U;
78 	}
79 
80 	@property bool hasCapacity() const @safe @nogc nothrow {
81 		return (this.length * T.sizeof) < this.store.length;
82 	}
83 
84 	void insertBack(T t) {
85 		assert(this.length + 1 <= Size);
86 		*(cast(T*)(&this.store[cast(size_t)(this.length * T.sizeof)])) = t;
87 		++this.length_;
88 	}
89 
90 	ref CopyTypeQualifiers!(S,T) opIndex(this S)(size_t idx) {
91 		idx *= T.sizeof;
92 		assert(idx < this.store.length);
93 		return *(cast(T*)&(this.store[idx]));
94 	}
95 
96 	@property ref CopyTypeQualifiers!(S,T) front(this S)() {
97 		assert(!this.empty);
98 		return *(cast(T*)(&this.store[0U]));
99 	}
100 
101 	@property ref CopyTypeQualifiers!(S,T) back(this S)() {
102 		import std.stdio;
103 		assert(!this.empty);
104 		return *(cast(T*)(&this.store[(this.length * T.sizeof) - T.sizeof]));
105 	}
106 
107 	void removeAll() {
108 		while(!this.empty) {
109 			this.removeBack();
110 		}
111 	}
112 
113 	void removeBack() {
114 		assert(!this.empty);
115 
116 		static if(hasElaborateDestructor!T) {
117 			static if(hasMember!(T, "__dtor")) {
118 				this.back().__dtor();
119 			} else static if(hasMember!(T, "__xdtor")) {
120 				this.back().__xdtor();
121 			} else {
122 				static assert(false);
123 			}
124 		}
125 
126 		--this.length_;
127 	}
128 
129 	void remove(size_t idx) {
130 		assert(idx < this.length);
131 		static if(hasElaborateDestructor!T) {
132 			static if(hasMember!(T, "__dtor")) {
133 				this[idx].__dtor();
134 			} else static if(hasMember!(T, "__xdtor")) {
135 				this[idx].__xdtor();
136 			} else {
137 				static assert(false);
138 			}
139 		}
140 
141 		for(size_t i = idx * T.sizeof; i < ((this.length - 1U) * T.sizeof); ++i) {
142 			this.store[i] = this.store[i + T.sizeof];
143 		}
144 
145 		--this.length_;
146 	}
147 }
148 
149 unittest {
150 	SFA!(int) a;
151 }
152 
153 unittest {
154 	SFA!(int) a;
155 	assert(a.hasCapacity);
156 	a.insertBack(10);
157 	assert(a[0] == 10);
158 	assert(a.front == 10);
159 	assert(a.back == 10);
160 	a.removeBack();
161 	assert(a.empty);
162 }
163 
164 unittest {
165 	struct Foo {
166 		size_t value;
167 		alias value this;
168 
169 		~this() {
170 		}
171 	}
172 
173 	void test(size_t Size, T)() {
174 		static import std.algorithm;
175 		import std.stdio;
176 		SFA!(T, Size) a;
177 		assert(a.empty);
178 		foreach(it; 0 .. Size) {
179 			assert(a.hasCapacity);
180 			a.insertBack(T(it));
181 			assert(a.length == it + 1);
182 			assert(a.front == 0);
183 			assert(a.back == it);
184 
185 			foreach(jt; 0 .. it + 1) {
186 				assert(a[jt] == jt);
187 			}
188 
189 			auto r = a[0 .. a.length];
190 			auto r2 = r.save();
191 
192 			//writefln!"%d %d"(r.low, r.high);
193 			assert(!r.empty);
194 			assert(r.front == 0);
195 			assert(r.back == it);
196 
197 			size_t idx = 0;
198 			foreach(jt; r) {
199 				assert(jt == idx);
200 				++idx;
201 			}
202 
203 			idx = it;
204 			foreach_reverse(jt; r2) {
205 				assert(jt == idx);
206 				--idx;
207 			}
208 		}
209 		assert(!a.hasCapacity);
210 	}
211 
212 	static foreach(tSize; [1,2,3,4,5,10,11,32,63,64,256]) {
213 		test!(tSize, size_t)();
214 		test!(tSize, Foo)();
215 	}
216 }
217 
218 unittest {
219 	import std.stdio;
220 	SFA!(int) a;
221 	foreach(it; [0,1,2,3,4,5,6,7,8,9]) {
222 		a.insertBack(it);
223 	}
224 
225 	a.remove(5);
226 
227 	foreach(idx, it; [0,1,2,3,4,6,7,8,9]) {
228 		assert(a[idx] == it);
229 	}
230 }
231 
232 unittest {
233 	SFA!int a;
234 	a.insertBack(1337);
235 	a.remove(0);
236 	assert(a.empty);
237 }
238 
239 unittest {
240 	import std.stdio;
241 	SFA!(int) a;
242 	foreach(it; 0 .. 10) {
243 		a.insertBack(it);
244 		assert(a.length == it + 1);
245 	}
246 
247 	auto r = a[];
248 	foreach(it; 0 .. 10) {		// Slice.opIndex
249 		assert(r[it] == it);
250 	}
251 
252 	for(size_t i = 0; !r.empty; r.popFront(), ++i) {
253 		assert(r.length == 10 - i);
254 		auto f = r.front;
255 		foreach(it; i .. 10) {
256 			//writefln!"%d %d"(r[it - i], it);
257 			assert(r[it - i] == it);
258 		}
259 	}
260 }