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 template nameOverride(T)
37 {
38 	import std.uni : toLower;
39 	static if(is(T==ubyte)) enum string nameOverride = "byte";
40 	else static if(is(T==real_t)) enum string nameOverride = "real";
41 	else enum string nameOverride = T.stringof.toLower;
42 }
43 
44 private enum string typeName(T) = "godot_pool_"~(nameOverride!T)~"_array";
45 private enum string readName(T) = "godot_pool_"~(nameOverride!T)~"_array_read_access";
46 private enum string writeName(T) = "godot_pool_"~(nameOverride!T)~"_array_write_access";
47 
48 alias PoolByteArray = PoolArray!ubyte;
49 alias PoolIntArray = PoolArray!int;
50 alias PoolRealArray = PoolArray!real_t;
51 alias PoolStringArray = PoolArray!String;
52 alias PoolVector2Array = PoolArray!Vector2;
53 alias PoolVector3Array = PoolArray!Vector3;
54 alias PoolColorArray = PoolArray!Color;
55 
56 /++
57 Copy-on-write array for some Godot types, allocated with a memory pool.
58 +/
59 struct PoolArray(T)
60 {
61 	@nogc nothrow:
62 	
63 	static assert(staticIndexOf!(T, PoolArrayTypes) != -1,
64 		"Cannot make a Godot PoolArray for a non-Godot type");
65 	
66 	mixin("package(godot) "~(typeName!T)~" _godot_array;");
67 	
68 	this(this)
69 	{
70 		mixin("auto n = _godot_api."~(typeName!T)~"_new_copy;");
71 		const auto tmp = _godot_array;
72 		n(&_godot_array, &tmp);
73 	}
74 	
75 	PoolArray opAssign(in PoolArray other)
76 	{
77 		mixin("auto d = _godot_api."~(typeName!T)~"_destroy;");
78 		mixin("auto n = _godot_api."~(typeName!T)~"_new_copy;");
79 		d(&_godot_array);
80 		n(&_godot_array, &other._godot_array);
81 		return this;
82 	}
83 	
84 	/++
85 	C API type to pass to/from C functions
86 	+/
87 	static if(is(T==Vector2)) private alias InternalType = godot_vector2;
88 	else static if(is(T==Vector3)) private alias InternalType = godot_vector3;
89 	else static if(is(T==Color)) private alias InternalType = godot_color;
90 	else private alias InternalType = T;
91 	
92 	this(Array arr)
93 	{
94 		mixin("auto n = _godot_api."~(typeName!T)~"_new_with_array;");
95 		n(&_godot_array, &arr._godot_array);
96 	}
97 	
98 	void append_array(in ref PoolArray arr)
99 	{
100 		mixin("auto a = _godot_api."~(typeName!T)~"_append_array;");
101 		a(&_godot_array, &arr._godot_array);
102 	}
103 	
104 	void invert()
105 	{
106 		mixin("auto i = _godot_api."~(typeName!T)~"_invert;");
107 		i(&_godot_array);
108 	}
109 	
110 	void remove(size_t idx)
111 	{
112 		mixin("auto r = _godot_api."~(typeName!T)~"_remove;");
113 		r(&_godot_array, cast(int)idx);
114 	}
115 	
116 	void resize(size_t size)
117 	{
118 		mixin("auto r = _godot_api."~(typeName!T)~"_resize;");
119 		r(&_godot_array, cast(int)size);
120 	}
121 	
122 	size_t size() const
123 	{
124 		mixin("auto s = _godot_api."~(typeName!T)~"_size;");
125 		return s(&_godot_array);
126 	}
127 	alias length = size; // D-style name for size
128 	alias opDollar = size;
129 	
130 	/// Returns: true if length is 0.
131 	bool empty() const
132 	{
133 		return length == 0;
134 	}
135 	
136 	~this()
137 	{
138 		mixin("auto d = _godot_api."~(typeName!T)~"_destroy;");
139 		d(&_godot_array);
140 	}
141 	
142 	
143 	// a few functions are different for Strings than for the others:
144 	static if(is(T == String))
145 	{
146 		void pushBack(in String data)
147 		{
148 			_godot_api.godot_pool_string_array_push_back(&_godot_array, &data._godot_string);
149 		}
150 		void insert(size_t idx, in String data)
151 		{
152 			_godot_api.godot_pool_string_array_insert(&_godot_array, cast(int)idx, &data._godot_string);
153 		}
154 		void set(size_t idx, in String data)
155 		{
156 			_godot_api.godot_pool_string_array_set(&_godot_array, cast(int)idx, &data._godot_string);
157 		}
158 		void opIndexAssign(in String data, size_t idx)
159 		{
160 			_godot_api.godot_pool_string_array_set(&_godot_array, cast(int)idx, &data._godot_string);
161 		}
162 		String opIndex(size_t idx) const
163 		{
164 			String ret = void;
165 			ret._godot_string = _godot_api.godot_pool_string_array_get(&_godot_array, cast(int)idx);
166 			return ret;
167 		}
168 	}
169 	else
170 	{
171 		void pushBack(in T data)
172 		{
173 			mixin("auto p = _godot_api."~(typeName!T)~"_push_back;");
174 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
175 				p(&_godot_array, cast(InternalType*)&data);
176 			else p(&_godot_array, data);
177 		}
178 		void insert(size_t idx, in T data)
179 		{
180 			mixin("auto i = _godot_api."~(typeName!T)~"_insert;");
181 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
182 				i(&_godot_array, cast(int)idx, cast(InternalType*)&data);
183 			else i(&_godot_array, cast(int)idx, data);
184 		}
185 		void set(size_t idx, in T data)
186 		{
187 			mixin("auto s = _godot_api."~(typeName!T)~"_set;");
188 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
189 				s(&_godot_array, cast(int)idx, cast(InternalType*)&data);
190 			else s(&_godot_array, cast(int)idx, data);
191 		}
192 		void opIndexAssign(in T data, size_t idx)
193 		{
194 			mixin("auto s = _godot_api."~(typeName!T)~"_set;");
195 			static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
196 				s(&_godot_array, cast(int)idx, cast(InternalType*)&data);
197 			else s(&_godot_array, cast(int)idx, data);
198 		}
199 		T opIndex(size_t idx) const
200 		{
201 			mixin("auto g = _godot_api."~(typeName!T)~"_get;");
202 			static union V
203 			{
204 				T t;
205 				InternalType r;
206 			}
207 			V v;
208 			v.r = g(&_godot_array, cast(int)idx);
209 			return v.t;
210 		}
211 	}
212 	
213 	alias append = pushBack;
214 	alias opOpAssign(string op : "~") = pushBack;
215 	
216 	/// Slice-like view of the PoolArray
217 	/// TODO: implement this with Read/Write?
218 	static struct Range
219 	{
220 		private
221 		{
222 			PoolArray* arr;
223 			size_t start, end;
224 		}
225 		
226 		bool empty() const { return start == end; }
227 		size_t length() const { return end - start; }
228 		alias opDollar = length;
229 		T front() { return (*arr)[start]; }
230 		void popFront() { ++start; }
231 		T back() { return (*arr)[end-1]; }
232 		void popBack() { --end; }
233 		T opIndex(size_t index) { return (*arr)[index+start]; }
234 		
235 		Range save() { return this; }
236 	}
237 	static assert(isRandomAccessRange!Range);
238 	
239 	Range opSlice() { return Range(&this, 0, length); }
240 	Range opSlice(size_t start, size_t end) { return Range(&this, start, end); }
241 }
242