1 /**
2 Memory-pool-based dynamic arrays. Optimized for memory usage, can’t fragment the memory.
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.poolarrays;
14 
15 import godot.c;
16 import godot.core.array;
17 import godot.core.defs;
18 import godot.core..string;
19 import godot.core.color;
20 import godot.core.vector2;
21 import godot.core.vector3;
22 
23 import std.range.primitives;
24 import std.meta, std.traits;
25 
26 private alias PoolArrayTypes = AliasSeq!(
27 	ubyte,
28 	int,
29 	real_t,
30 	String,
31 	Vector2,
32 	Vector3,
33 	Color
34 );
35 
36 private enum string nameOverride(T) = AliasSeq!(
37 	"byte", "int", "real", "string", "vector2", "vector3", "color")[staticIndexOf!(T, PoolArrayTypes)];
38 
39 private enum string typeName(T) = "godot_pool_"~(nameOverride!T)~"_array";
40 private enum string readName(T) = "godot_pool_"~(nameOverride!T)~"_array_read_access";
41 private enum string writeName(T) = "godot_pool_"~(nameOverride!T)~"_array_write_access";
42 
43 alias PoolByteArray = PoolArray!ubyte;
44 alias PoolIntArray = PoolArray!int;
45 alias PoolRealArray = PoolArray!real_t;
46 alias PoolStringArray = PoolArray!String;
47 alias PoolVector2Array = PoolArray!Vector2;
48 alias PoolVector3Array = PoolArray!Vector3;
49 alias PoolColorArray = PoolArray!Color;
50 
51 /++
52 Copy-on-write array for some Godot types, allocated with a memory pool.
53 +/
54 struct PoolArray(T)
55 {
56 	@nogc nothrow:
57 	
58 	static assert(staticIndexOf!(T, PoolArrayTypes) != -1,
59 		"Cannot make a Godot PoolArray for a non-Godot type");
60 	
61 	mixin("package(godot) "~(typeName!T)~" _godot_array;");
62 	
63 	this(this)
64 	{
65 		mixin("auto n = _godot_api."~(typeName!T)~"_new_copy;");
66 		const auto tmp = _godot_array;
67 		n(&_godot_array, &tmp);
68 	}
69 	
70 	PoolArray opAssign(in PoolArray other)
71 	{
72 		mixin("auto d = _godot_api."~(typeName!T)~"_destroy;");
73 		mixin("auto n = _godot_api."~(typeName!T)~"_new_copy;");
74 		d(&_godot_array);
75 		n(&_godot_array, &other._godot_array);
76 		return this;
77 	}
78 	
79 	/++
80 	C API type to pass to/from C functions
81 	+/
82 	static if(is(T==Vector2)) private alias InternalType = godot_vector2;
83 	else static if(is(T==Vector3)) private alias InternalType = godot_vector3;
84 	else static if(is(T==Color)) private alias InternalType = godot_color;
85 	else private alias InternalType = T;
86 	
87 	this(Array arr)
88 	{
89 		mixin("auto n = _godot_api."~(typeName!T)~"_new_with_array;");
90 		n(&_godot_array, &arr._godot_array);
91 	}
92 
93 	///
94 	void pushBack(in ref PoolArray arr)
95 	{
96 		mixin("auto a = _godot_api."~(typeName!T)~"_append_array;");
97 		a(&_godot_array, &arr._godot_array);
98 	}
99 	deprecated("Use the concatenation operator ~= instead of append_array.") alias append_array = pushBack;
100 	
101 	void invert()
102 	{
103 		mixin("auto i = _godot_api."~(typeName!T)~"_invert;");
104 		i(&_godot_array);
105 	}
106 	
107 	void remove(size_t idx)
108 	{
109 		mixin("auto r = _godot_api."~(typeName!T)~"_remove;");
110 		r(&_godot_array, cast(int)idx);
111 	}
112 	
113 	void resize(size_t size)
114 	{
115 		mixin("auto r = _godot_api."~(typeName!T)~"_resize;");
116 		r(&_godot_array, cast(int)size);
117 	}
118 	
119 	size_t size() const
120 	{
121 		mixin("auto s = _godot_api."~(typeName!T)~"_size;");
122 		return s(&_godot_array);
123 	}
124 	alias length = size; // D-style name for size
125 	alias opDollar = size;
126 	
127 	/// Returns: true if length is 0.
128 	bool empty() const
129 	{
130 		return length == 0;
131 	}
132 	
133 	~this()
134 	{
135 		mixin("auto d = _godot_api."~(typeName!T)~"_destroy;");
136 		d(&_godot_array);
137 	}
138 	
139 	
140 	// a few functions are different for Strings than for the others:
141 	static if(is(T == String))
142 	{
143 		void pushBack(in String data)
144 		{
145 			_godot_api.godot_pool_string_array_push_back(&_godot_array, &data._godot_string);
146 		}
147 		void insert(size_t idx, in String data)
148 		{
149 			_godot_api.godot_pool_string_array_insert(&_godot_array, cast(int)idx, &data._godot_string);
150 		}
151 		void set(size_t idx, in String data)
152 		{
153 			_godot_api.godot_pool_string_array_set(&_godot_array, cast(int)idx, &data._godot_string);
154 		}
155 		void opIndexAssign(in String data, size_t idx)
156 		{
157 			_godot_api.godot_pool_string_array_set(&_godot_array, cast(int)idx, &data._godot_string);
158 		}
159 		String opIndex(size_t idx) const
160 		{
161 			String ret = void;
162 			ret._godot_string = _godot_api.godot_pool_string_array_get(&_godot_array, cast(int)idx);
163 			return ret;
164 		}
165 	}
166 	else
167 	{
168 		void pushBack(in T data)
169 		{
170 			mixin("auto p = _godot_api."~(typeName!T)~"_push_back;");
171 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
172 				p(&_godot_array, cast(InternalType*)&data);
173 			else p(&_godot_array, data);
174 		}
175 		void insert(size_t idx, in T data)
176 		{
177 			mixin("auto i = _godot_api."~(typeName!T)~"_insert;");
178 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
179 				i(&_godot_array, cast(int)idx, cast(InternalType*)&data);
180 			else i(&_godot_array, cast(int)idx, data);
181 		}
182 		void set(size_t idx, in T data)
183 		{
184 			mixin("auto s = _godot_api."~(typeName!T)~"_set;");
185 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
186 				s(&_godot_array, cast(int)idx, cast(InternalType*)&data);
187 			else s(&_godot_array, cast(int)idx, data);
188 		}
189 		void opIndexAssign(in T data, size_t idx)
190 		{
191 			mixin("auto s = _godot_api."~(typeName!T)~"_set;");
192 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
193 				s(&_godot_array, cast(int)idx, cast(InternalType*)&data);
194 			else s(&_godot_array, cast(int)idx, data);
195 		}
196 		T opIndex(size_t idx) const
197 		{
198 			mixin("auto g = _godot_api."~(typeName!T)~"_get;");
199 			static union V
200 			{
201 				T t;
202 				InternalType r;
203 			}
204 			V v;
205 			v.r = g(&_godot_array, cast(int)idx);
206 			return v.t;
207 		}
208 	}
209 
210 	///
211 	alias append = pushBack;
212 	///
213 	template opOpAssign(string op) if(op == "~" || op == "+")
214 	{
215 		alias opOpAssign = pushBack;
216 	}
217 
218 	///
219 	PoolArray opBinary(string op)(in ref PoolArray other) const if(op == "~" || op == "+")
220 	{
221 		PoolArray ret = this;
222 		ret ~= other;
223 		return ret;
224 	}
225 
226 	/// Read/Write access locks with RAII.
227 	static struct Access(bool write = false)
228 	{
229 		private enum string rw = write ? "write" : "read";
230 		private enum string RW = write ? "Write" : "Read";
231 		static if(write) private alias access = writeName!T;
232 		else private alias access = readName!T;
233 
234 		private
235 		{
236 			mixin(access ~ "* _access;");
237 			T[] _data;
238 		}
239 
240 		static if(write)
241 		{
242 			/// 
243 			inout(T[]) data() inout { return _data; }
244 		}
245 		else
246 		{
247 			/// 
248 			const(T[]) data() const { return _data; }
249 		}
250 		// TODO: `scope` for data to ensure it doesn't outlive `this`?
251 		alias data this;
252 
253 		this(PoolArray!T p)
254 		{
255 			mixin("_access = _godot_api." ~ typeName!T ~ "_" ~ rw ~ "(&p._godot_array);");
256 			mixin("void* _ptr = cast(void*)_godot_api." ~ access ~ "_ptr(_access);");
257 			_data = (cast(T*)_ptr)[0..p.length];
258 		}
259 		this(this)
260 		{
261 			mixin("_access = _godot_api." ~ access ~ "_copy(_access);");
262 		}
263 		void opAssign(const ref typeof(this) other)
264 		{
265 			mixin("_godot_api." ~ access ~ "_destroy(_access);");
266 			mixin("_access = _godot_api." ~ access ~ "_copy(other._access);");
267 		}
268 		~this()
269 		{
270 			mixin("_godot_api." ~ access ~ "_destroy(_access);");
271 		}
272 	}
273 
274 	/// 
275 	alias Read = Access!false;
276 	/// Lock the array for read-only access to the underlying memory.
277 	/// This is faster than using opIndex, which locks each time it's called.
278 	Read read() const { return Read(this); }
279 	/// 
280 	alias Write = Access!true;
281 	/// Lock the array for write access to the underlying memory.
282 	/// This is faster than using opIndexAssign, which locks each time it's called.
283 	Write write() { return Write(this); }
284 	
285 	/// Slice-like view of the PoolArray.
286 	static struct Range
287 	{
288 		private
289 		{
290 			PoolArray* arr;
291 			size_t start, end;
292 		}
293 		
294 		bool empty() const { return start == end; }
295 		size_t length() const { return end - start; }
296 		alias opDollar = length;
297 		T front() { return (*arr)[start]; }
298 		void popFront() { ++start; }
299 		T back() { return (*arr)[end-1]; }
300 		void popBack() { --end; }
301 		T opIndex(size_t index) { return (*arr)[index+start]; }
302 		
303 		Range save() { return this; }
304 	}
305 	static assert(isRandomAccessRange!Range);
306 	
307 	/// Returns: a slice-like Range view over the array.
308 	/// Note: Prefer `read()`/`write()`; Range locks the array on each individual access.
309 	Range opSlice() { return Range(&this, 0, length); }
310 	/// ditto
311 	Range opSlice(size_t start, size_t end) { return Range(&this, start, end); }
312 }
313