1 /**
2 Dynamic Variant array.
3 
4 Copyright:
5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.  
6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)  
7 Copyright (c) 2017-2018 Godot-D contributors  
8 
9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
10 
11 
12 */
13 module godot.core.array;
14 
15 import godot.c;
16 import godot.core.variant;
17 import godot.core.poolarrays;
18 
19 import std.meta;
20 import std.traits, std.range;
21 
22 /**
23 Generic array, contains several elements of any type, accessible by numerical index starting at 0. Negative indices can be used to count from the right, like in Python. Arrays are always passed by reference.
24 */
25 struct Array
26 {
27 	int opApply(int delegate(size_t, ref Variant) dg)
28 	{
29 		foreach(i; 0..length)
30 		{
31 			Variant* v = cast(Variant*)&(this[i]);
32 			int res = dg(cast(size_t)i, *v);
33 			if(res) return res;
34 		}
35 		return 0;
36 	}
37 	
38 	int opApply(int delegate(size_t, const(Variant)) dg) const
39 	{
40 		foreach(i; 0..length)
41 		{
42 			int res = dg(cast(size_t)i, this[i]);
43 			if(res) return res;
44 		}
45 		return 0;
46 	}
47 	
48 	int opApply(int delegate(ref Variant) dg)
49 	{
50 		foreach(i; 0..length)
51 		{
52 			Variant* v = cast(Variant*)&(this[i]);
53 			int res = dg(*v);
54 			if(res) return res;
55 		}
56 		return 0;
57 	}
58 	
59 	int opApply(int delegate(const(Variant)) dg) const
60 	{
61 		foreach(i; 0..length)
62 		{
63 			int res = dg(this[i]);
64 			if(res) return res;
65 		}
66 		return 0;
67 	}
68 
69 	/// Convert to a static array.
70 	/// Excess elements are discarded if the Array is longer than `T`.
71 	T as(T)() const if(isStaticArray!T && Variant.compatibleFromGodot!(ElementType!T))
72 	{
73 		import std.algorithm : min;
74 		T ret;
75 		foreach(i; 0..min(T.length, length)) ret[i] = (this[i]).as!(ElementType!T);
76 		return ret;
77 	}
78 
79 	/// Create an Array from any D range or static array with compatible elements.
80 	static Array from(T)(T t) if((isForwardRange!T || isStaticArray!T) && Variant.compatibleToGodot!(ElementType!T))
81 	{
82 		import std.algorithm.iteration;
83 		Array ret = Array.make();
84 		static if(hasLength!T)
85 		{
86 			ret.resize(cast(int)t.length);
87 			int ei = 0;
88 			foreach(e; t) ret[ei++] = e;
89 		}
90 		else t.each!(e => ret ~= e);
91 		return ret;
92 	}
93 	
94 	@nogc nothrow:
95 	
96 	package(godot) godot_array _godot_array;
97 	
98 	@disable this();
99 	
100 	this(this)
101 	{
102 		const godot_array tmp = _godot_array;
103 		_godot_api.godot_array_new_copy(&_godot_array, &tmp);
104 	}
105 	
106 	Array opAssign(in Array other)
107 	{
108 		_godot_api.godot_array_destroy(&_godot_array);
109 		_godot_api.godot_array_new_copy(&_godot_array, &other._godot_array);
110 		return this;
111 	}
112 	
113 	/++
114 	Assigning null empties the Array variable, but unlike `clear`, does not
115 	destroy the original memory unless it was the only remaining reference.
116 	+/
117 	Array opAssign(in typeof(null) n)
118 	{
119 		return opAssign(Array.make());
120 	}
121 
122 	/++
123 	Create an array and add all $(PARAM args) to it.
124 	+/
125 	static Array make(Args...)(Args args)
126 		if(allSatisfy!(Variant.compatibleToGodot, Args))
127 	{
128 		Array ret = void;
129 		_godot_api.godot_array_new(&ret._godot_array);
130 		static if(args.length) ret.resize(args.length);
131 		static foreach(i, Arg; Args)
132 		{
133 			ret[i] = args[i];
134 		}
135 		return ret;
136 	}
137 	
138 	deprecated("Use Array.make() with 0 args instead.")
139 	static Array empty_array()
140 	{
141 		Array ret = void;
142 		_godot_api.godot_array_new(&ret._godot_array);
143 		return ret;
144 	}
145 	
146 	this(in typeof(null) n)
147 	{
148 		_godot_api.godot_array_new(&_godot_array);
149 	}
150 	
151 	this(in PoolByteArray a)
152 	{
153 		_godot_api.godot_array_new_pool_byte_array(&_godot_array, &a._godot_array);
154 	}
155 	
156 	this(in PoolIntArray a)
157 	{
158 		_godot_api.godot_array_new_pool_int_array(&_godot_array, &a._godot_array);
159 	}
160 	
161 	this(in PoolRealArray a)
162 	{
163 		_godot_api.godot_array_new_pool_real_array(&_godot_array, &a._godot_array);
164 	}
165 	
166 	this(in PoolStringArray a)
167 	{
168 		_godot_api.godot_array_new_pool_string_array(&_godot_array, &a._godot_array);
169 	}
170 	
171 	this(in PoolVector2Array a)
172 	{
173 		_godot_api.godot_array_new_pool_vector2_array(&_godot_array, &a._godot_array);
174 	}
175 	
176 	this(in PoolVector3Array a)
177 	{
178 		_godot_api.godot_array_new_pool_vector3_array(&_godot_array, &a._godot_array);
179 	}
180 	
181 	this(in PoolColorArray a)
182 	{
183 		_godot_api.godot_array_new_pool_color_array(&_godot_array, &a._godot_array);
184 	}
185 	
186 	auto ref inout(Variant) opIndex(size_t idx) inout
187 	{
188 		godot_variant* v = _godot_api.godot_array_operator_index(cast(godot_array*)&_godot_array, cast(int)idx);
189 		return *cast(inout(Variant)*)v;
190 	}
191 	
192 	void opIndexAssign(T)(auto ref T value, in size_t idx) if(is(T : Variant) || Variant.compatibleToGodot!T)
193 	{
194 		Variant v = Variant(value);
195 		_godot_api.godot_array_set(&_godot_array, cast(int)idx, &v._godot_variant);
196 	}
197 
198 	/// Append a single element.
199 	///
200 	/// Note: an Array or range will be appended as one single element of type
201 	/// Array, *not* concatenated to this Array. Use `appendRange` or
202 	/// `appendArray` to concatenate/chain ranges or Arrays into one.
203 	void append(T)(auto ref T t) if(is(T : Variant) || Variant.compatibleToGodot!T)
204 	{
205 		Variant v = Variant(t);
206 		_godot_api.godot_array_append(&_godot_array, &v._godot_variant);
207 	}
208 	/// ditto
209 	template opOpAssign(string op) if(op == "~" || op == "+")
210 	{
211 		alias opOpAssign = append;
212 	}
213 
214 	/// Concatenate a range or another Array to the end of this one.
215 	void appendRange(R)(in auto ref R other) if(
216 		!is(Unqual!R : Array) &&
217 		isInputRange!R &&
218 		(is(ElementType!R : Variant) || Variant.compatible!(ElementType!R)))
219 	{
220 		static if(hasLength!R)
221 		{
222 			size_t l = length;
223 			resize(l + other.length);
224 			Variant[] slice = this[];
225 			size_t i = l;
226 			foreach(const v; other) slice[i++] = v;
227 		}
228 		else foreach(const v; other) append(v);
229 	}
230 	/// ditto
231 	void appendArray(in ref Array other)
232 	{
233 		appendRange(other[]);
234 	}
235 
236 	private static Array fromConcat(R, S)(in auto ref R r, in auto ref S s)
237 	{
238 		Array ret = Array.make();
239 		ret.resize(r.length + s.length);
240 		Variant[] slice = ret[];
241 		size_t i = 0;
242 		foreach(const v; r) slice[i++] = v;
243 		foreach(const v; s) slice[i++] = v;
244 		return ret;
245 	}
246 	/// Concatenate two arrays into a new one. The originals are left unaffected
247 	/// if there are still other references to them remaining.
248 	Array opBinary(string op, R)(in auto ref R other) if(
249 		(op == "~" || op == "+") && !is(Unqual!R : Array) &&
250 		isInputRange!R && hasLength!R &&
251 		(is(ElementType!R : Variant) || Variant.compatible!(ElementType!R)))
252 	{
253 		return fromConcat(this[], other);
254 	}
255 	/// ditto
256 	Array opBinary(string op)(in auto ref Array other) if(op == "~" || op == "+")
257 	{
258 		return fromConcat(this[], other[]);
259 	}
260 	/// ditto
261 	Array opBinaryRight(string op, R)(in auto ref R other) if(
262 		(op == "~" || op == "+") && !is(Unqual!R : Array) &&
263 		isInputRange!R && hasLength!R &&
264 		(is(ElementType!R : Variant) || Variant.compatible!(ElementType!R)))
265 	{
266 		return fromConcat(other, this[]);
267 	}
268 
269 	void clear()
270 	{
271 		_godot_api.godot_array_clear(&_godot_array);
272 	}
273 	
274 	size_t count(in Variant v)
275 	{
276 		return _godot_api.godot_array_count(&_godot_array, &v._godot_variant);
277 	}
278 	
279 	bool empty() const
280 	{
281 		return cast(bool)_godot_api.godot_array_empty(&_godot_array);
282 	}
283 	
284 	void erase(T)(T v)  if(is(T : Variant) || Variant.compatibleToGodot!T)
285 	{
286 		Variant vv = v;
287 		_godot_api.godot_array_erase(&_godot_array, &vv._godot_variant);
288 	}
289 	
290 	Variant front() const
291 	{
292 		godot_variant v = _godot_api.godot_array_front(&_godot_array);
293 		return cast(Variant)v;
294 	}
295 	
296 	Variant back() const
297 	{
298 		godot_variant v = _godot_api.godot_array_back(&_godot_array);
299 		return cast(Variant)v;
300 	}
301 	
302 	int find(T)(in T what, size_t from) const if(is(T : Variant) || Variant.compatibleToGodot!T)
303 	{
304 		const Variant vv = what;
305 		return _godot_api.godot_array_find(&_godot_array, &vv._godot_variant, cast(int)from);
306 	}
307 	
308 	int findLast(T)(in T what) const if(is(T : Variant) || Variant.compatibleToGodot!T)
309 	{
310 		const Variant vv = what;
311 		return _godot_api.godot_array_find_last(&_godot_array, &vv._godot_variant);
312 	}
313 	
314 	bool has(T)(in T what) const if(is(T : Variant) || Variant.compatibleToGodot!T)
315 	{
316 		const Variant vv = what;
317 		return cast(bool)_godot_api.godot_array_has(&_godot_array, &vv._godot_variant);
318 	}
319 	
320 	@trusted
321 	uint hash() const
322 	{
323 		return _godot_api.godot_array_hash(&_godot_array);
324 	}
325 	@trusted
326 	hash_t toHash() const
327 	{
328 		return cast(hash_t)hash();
329 	}
330 	
331 	void insert(T)(const size_t pos, T value) if(is(T : Variant) || Variant.compatibleToGodot!T)
332 	{
333 		Variant vv = value;
334 		_godot_api.godot_array_insert(&_godot_array, cast(int)pos, &vv._godot_variant);
335 	}
336 	
337 	void invert()
338 	{
339 		_godot_api.godot_array_invert(&_godot_array);
340 	}
341 	
342 	Variant popBack()
343 	{
344 		godot_variant v = _godot_api.godot_array_pop_back(&_godot_array);
345 		return cast(Variant)v;
346 	}
347 	
348 	Variant popFront()
349 	{
350 		godot_variant v = _godot_api.godot_array_pop_front(&_godot_array);
351 		return cast(Variant)v;
352 	}
353 	
354 	void pushBack(T)(T v) if(is(T : Variant) || Variant.compatibleToGodot!T)
355 	{
356 		Variant vv = v;
357 		_godot_api.godot_array_push_back(&_godot_array, &vv._godot_variant);
358 	}
359 	
360 	void pushFront(T)(T v) if(is(T : Variant) || Variant.compatibleToGodot!T)
361 	{
362 		Variant vv = v;
363 		_godot_api.godot_array_push_front(&_godot_array, &vv._godot_variant);
364 	}
365 	
366 	void remove(size_t idx)
367 	{
368 		_godot_api.godot_array_remove(&_godot_array, cast(int)idx);
369 	}
370 	
371 	size_t size() const
372 	{
373 		return _godot_api.godot_array_size(&_godot_array);
374 	}
375 	alias length = size; // D-style `length`
376 	alias opDollar = size;
377 	
378 	void resize(size_t size)
379 	{
380 		_godot_api.godot_array_resize(&_godot_array, cast(int)size);
381 	}
382 	
383 	int rfind(T)(in T what, size_t from) const if(is(T : Variant) || Variant.compatibleToGodot!T)
384 	{
385 		const Variant vv = what;
386 		return _godot_api.godot_array_rfind(&_godot_array, &vv._godot_variant, cast(int)from);
387 	}
388 	
389 	void sort()
390 	{
391 		_godot_api.godot_array_sort(&_godot_array);
392 	}
393 	
394 	/+void sort_custom(godot.Object obj, in ref String func)
395 	{
396 		_godot_api.godot_array_sort_custom(&_godot_array, obj, &func._godot_string);
397 	}+/
398 	
399 	/// Allocate a new separate copy of the Array
400 	Array dup() const
401 	{
402 		Array ret = Array.make();
403 		size_t l = size();
404 		ret.resize(l);
405 		foreach(vi; 0..l)
406 		{
407 			ret[vi] = this[vi];
408 		}
409 		return ret;
410 	}
411 
412 	/// Returns: a new Array containing a slice of the original. It is a copy,
413 	/// *not* a reference to the original Array's memory.
414 	///
415 	/// Note: `end` is non-inclusive, as in D slice operations, not as in Godot.
416 	Array slice(size_t start, size_t end, size_t stride = 1, bool deep = false) const
417 	{
418 		Array ret = void;
419 		ret._godot_array = _godot_api.godot_array_slice(&_godot_array,
420 			cast(int)start, cast(int)(end-1), cast(int)stride, deep);
421 		return ret;
422 	}
423 
424 	/++
425 	Returns: a slice of the array memory. The slice does *not* have ownership of
426 	the reference-counted memory and is invalid after the original Array goes
427 	out of scope or is resized.
428 	+/
429 	Variant[] opSlice(size_t start, size_t end)
430 	{
431 		Variant* ret = cast(Variant*)_godot_api.godot_array_operator_index(&_godot_array, 0);
432 		return ret[start..end];
433 	}
434 	/// ditto
435 	const(Variant)[] opSlice(size_t start, size_t end) const
436 	{
437 		const(Variant)* ret = cast(const(Variant)*)_godot_api.godot_array_operator_index_const(&_godot_array, 0);
438 		return ret[start..end];
439 	}
440 	/// ditto
441 	Variant[] opSlice() { return this[0..length]; }
442 	/// ditto
443 	const(Variant)[] opSlice() const { return this[0..length]; }
444 	// TODO: `scope` for the returned slices?
445 	
446 	~this()
447 	{
448 		_godot_api.godot_array_destroy(&_godot_array);
449 	}
450 }
451 
452 
453 
454