1 /************************************************************************************************************************
2 Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
3 License: BSD 3-clause, see LICENSE file in project root folder.
4 */
5 module bubel.ecs.vector;
6 
7 import core.bitop;
8 
9 //import core.stdc.stdlib : free, malloc;
10 import bubel.ecs.std;
11 
12 //import core.stdc.string : memcpy, memset;
13 //import std.algorithm : swap;
14 import std.conv : emplace;
15 import std.traits : hasMember, isCopyable, TemplateOf, Unqual;
16 
17 export @nogc @safe nothrow pure size_t nextPow2(size_t num)
18 {
19 	return 1 << bsr(num) + 1;
20 }
21 
22 export __gshared size_t gVectorsCreated = 0;
23 export __gshared size_t gVectorsDestroyed = 0;
24 
25 struct Vector(T)
26 {
27 	T[] array;
28 	size_t used;
29 public:
30 
31 	export this()(T t)
32 	{
33 		add(t);
34 	}
35 
36 	export this(X)(X[] t) if (is(Unqual!X == Unqual!T))
37 	{
38 		add(t);
39 
40 	}
41 
42 	/*static if (isCopyable!T) {
43 		export this(this) {
44 			T[] tmp = array[0 .. used];
45 			array = null;
46 			used = 0;
47 			add(tmp);
48 		}
49 	} else {
50 		@disable this(this);
51 	}*/
52 
53 	@disable this(this);
54 
55 	export ~this()
56 	{
57 		clear();
58 	}
59 
60 	export void clear()
61 	{
62 		removeAll();
63 	}
64 
65 	export void removeAll()
66 	{
67 		if (array !is null)
68 		{
69 			/*foreach (ref el; array[0 .. used]) {
70 				destroy(el);
71 			}*/
72 			//freeData(cast(void[]) array);
73 			freeData((cast(void*) array.ptr)[0 .. array.length * T.sizeof]);
74 			gVectorsDestroyed++;
75 		}
76 		array = null;
77 		used = 0;
78 	}
79 
80 	export bool empty() const
81 	{
82 		return (used == 0);
83 	}
84 
85 	export size_t length() const
86 	{
87 		return used;
88 	}
89 
90 	export void length(size_t newLength)
91 	{
92 		if (newLength > used)
93 		{
94 			reserve(newLength);
95 			foreach (ref el; array[used .. newLength])
96 			{
97 				emplace(&el);
98 			}
99 		}
100 		else
101 		{
102 			foreach (ref el; array[newLength .. used])
103 			{
104 				//destroy(el);
105 				static if (__traits(hasMember, T, "__xdtor"))
106 					el.__xdtor();
107 				else static if (__traits(hasMember, T, "__dtor"))
108 					el.__dtor();
109 			}
110 		}
111 		used = newLength;
112 	}
113 
114 	export void reset()
115 	{
116 		used = 0;
117 	}
118 
119 	export void reserve(size_t numElements)
120 	{
121 		if (numElements > array.length)
122 		{
123 			extend(numElements);
124 		}
125 	}
126 
127 	export size_t capacity()
128 	{
129 		return array.length - used;
130 	}
131 
132 	export void extend(size_t newNumOfElements)
133 	{
134 		auto oldArray = manualExtend(array, newNumOfElements);
135 		if (oldArray !is null)
136 		{
137 			freeData(oldArray);
138 		}
139 	}
140 
141 	export @nogc void freeData(void[] data)
142 	{
143 		// 0x0F probably invalid value for pointers and other types
144 		memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD 
145 		free(data.ptr);
146 	}
147 
148 	export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0)
149 	{
150 		if (newNumOfElements == 0)
151 			newNumOfElements = 2;
152 		if (array.length == 0)
153 			gVectorsCreated++;
154 		T[] oldArray = array;
155 		size_t oldSize = oldArray.length * T.sizeof;
156 		size_t newSize = newNumOfElements * T.sizeof;
157 		T* memory = cast(T*) malloc(newSize);
158 		memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize);
159 		array = memory[0 .. newNumOfElements];
160 		//return cast(void[]) oldArray;
161 		return (cast(void*) oldArray.ptr)[0 .. oldArray.length * T.sizeof];
162 	}
163 
164 	export Vector!T copy()()
165 	{
166 		Vector!T duplicate;
167 		duplicate.reserve(used);
168 		duplicate ~= array[0 .. used];
169 		return duplicate;
170 	}
171 
172 	/*export bool canAddWithoutRealloc(uint elemNum = 1)
173 	{
174 		return used + elemNum <= array.length;
175 	}*/
176 
177 	export void add()(T t)
178 	{
179 		if (used >= array.length)
180 		{
181 			extend(nextPow2(used + 1));
182 		}
183 		emplace(&array[used], t);
184 		used++;
185 	}
186 
187 	/// Add element at given position moving others
188 	export void add()(T t, size_t pos)
189 	{
190 		assert(pos <= used);
191 		if (used >= array.length)
192 		{
193 			extend(array.length * 2);
194 		}
195 		foreach_reverse (size_t i; pos .. used)
196 		{
197 			//swap(array[i + 1], array[i]);
198 			array[i + 1] = array[i];
199 		}
200 		emplace(&array[pos], t);
201 		used++;
202 	}
203 
204 	export void add(X)(X[] t) if (is(Unqual!X == Unqual!T))
205 	{
206 		if (used + t.length > array.length)
207 		{
208 			extend(nextPow2(used + t.length));
209 		}
210 		foreach (i; 0 .. t.length)
211 		{
212 			emplace(&array[used + i], t[i]);
213 		}
214 		used += t.length;
215 	}
216 
217 	export void remove(size_t elemNum)
218 	{
219 		//destroy(array[elemNum]);
220 		static if (__traits(hasMember, T, "__xdtor"))
221 			array[elemNum].__xdtor();
222 		else static if (__traits(hasMember, T, "__dtor"))
223 			array[elemNum].__dtor();
224 		//swap(array[elemNum], array[used - 1]);
225 		array[elemNum] = array[used - 1];
226 		used--;
227 	}
228 
229 	export void removeStable()(size_t elemNum)
230 	{
231 		used--;
232 		foreach (i; 0 .. used)
233 		{
234 			array[i] = array[i + 1];
235 		}
236 	}
237 
238 	export bool tryRemoveElement()(T elem)
239 	{
240 		foreach (i, ref el; array[0 .. used])
241 		{
242 			if (el == elem)
243 			{
244 				remove(i);
245 				return true;
246 			}
247 		}
248 		return false;
249 	}
250 
251 	export void removeElement()(T elem)
252 	{
253 		bool ok = tryRemoveElement(elem);
254 		assert(ok, "There is no such an element in vector");
255 	}
256 
257 	export ref T opIndex(size_t elemNum) const
258 	{
259 		//debug assert(elemNum < used, "Range violation [index]");
260 		return *cast(T*)&array.ptr[elemNum];
261 	}
262 
263 	export auto opSlice()
264 	{
265 		return array.ptr[0 .. used];
266 	}
267 
268 	export T[] opSlice(size_t x, size_t y)
269 	{
270 		assert(y <= used);
271 		return array.ptr[x .. y];
272 	}
273 
274 	export size_t opDollar()
275 	{
276 		return used;
277 	}
278 
279 	export void opAssign(X)(X[] slice)
280 	{
281 		reset();
282 		this ~= slice;
283 	}
284 
285 	export void opOpAssign(string op)(T obj)
286 	{
287 		//static assert(op == "~");
288 		add(obj);
289 	}
290 
291 	export void opOpAssign(string op, X)(X[] obj)
292 	{
293 		//static assert(op == "~");
294 		add(obj);
295 	}
296 
297 	export void opIndexAssign()(T obj, size_t elemNum)
298 	{
299 		assert(elemNum < used, "Range viloation");
300 		array[elemNum] = obj;
301 	}
302 
303 	export void opSliceAssign()(T[] obj, size_t a, size_t b)
304 	{
305 		assert(b <= used && a <= b, "Range viloation");
306 		array.ptr[a .. b] = obj;
307 	}
308 
309 	export bool opEquals()(auto ref const Vector!(T) r) const
310 	{
311 		return used == r.used && array.ptr[0 .. used] == r.array.ptr[0 .. r.used];
312 	}
313 }