1 /**
2 Memory-pool-based dynamic arrays. Optimized for memory usage, can’t fragment the memory.
3
4 Copyright:
5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur.
6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md)
7 Copyright (c) 2017 Godot-D contributors
8 Copyright (c) 2022 Godot-DLang contributors
9
10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
11
12
13 */
14 module godot.poolarrays;
15
16 import godot.abi;
17 import godot.array;
18 import godot.api.types;
19 import godot.string;
20 import godot.color;
21 import godot.vector2;
22 import godot.vector3;
23 import godot.builtins;
24
25 import std.range.primitives;
26 import std.meta, std.traits;
27
28 private alias PackedArrayTypes = AliasSeq!(
29 ubyte,
30 int,
31 long,
32 float,
33 double,
34 // String,
35 string,
36 Vector2,
37 Vector3,
38 Color
39 );
40
41 // used in GDExtensionInterface.variant_get_ptr_destructor()
42 private alias PackedArrayVariantType = AliasSeq!(
43 GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY,
44 GDEXTENSION_VARIANT_TYPE_PACKED_INT32_ARRAY,
45 GDEXTENSION_VARIANT_TYPE_PACKED_INT64_ARRAY,
46 GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT32_ARRAY,
47 GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT64_ARRAY,
48 GDEXTENSION_VARIANT_TYPE_PACKED_STRING_ARRAY,
49 GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY,
50 GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY,
51 GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY,
52 );
53
54 private enum string nameOverride(T) = AliasSeq!(
55 "byte", "int32", "int64", "float32", "float64", "string",
56 "vector2", "vector3", "color")[staticIndexOf!(T, PackedArrayTypes)];
57
58 private enum string bindNameOverride(T) = AliasSeq!(
59 "Byte", "Int32", "Int64", "Float32", "Float64", "String",
60 "Vector2", "Vector3", "Color")[staticIndexOf!(T, PackedArrayTypes)];
61
62 private enum string typeName(T) = "packed_" ~ (nameOverride!T) ~ "_array";
63 private enum string readName(T) = "packed_" ~ (nameOverride!T) ~ "_array_operator_index_const";
64 private enum string writeName(T) = "packed_" ~ (nameOverride!T) ~ "_array_operator_index";
65
66 alias PackedByteArray = PackedArray!ubyte;
67 alias PackedInt32Array = PackedArray!int;
68 alias PackedInt64Array = PackedArray!long;
69 alias PackedFloat32Array = PackedArray!float;
70 alias PackedFloat64Array = PackedArray!double;
71 // alias PackedStringArray = PackedArray!String;
72 alias PackedStringArray = PackedArray!string;
73 alias PackedVector2Array = PackedArray!Vector2;
74 //alias PackedVector2iArray = PackedArray!Vector2i;
75 alias PackedVector3Array = PackedArray!Vector3;
76 //alias PackedVector3iArray = PackedArray!Vector3i;
77 alias PackedColorArray = PackedArray!Color;
78
79 /++
80 Copy-on-write array for some Godot types, allocated with a memory pool.
81 +/
82 struct PackedArray(T) {
83 //@nogc nothrow:
84
85 static assert(staticIndexOf!(T, PackedArrayTypes) != -1,
86 "Cannot make a Godot PackedArray for a non-Godot type");
87
88 // TODO: this is now gone, replace with real array
89 //mixin("package(godot) "~(typeName!T)~" _godot_array;");
90
91 package(godot) union _PackedArray {
92 GDExtensionTypePtr _godot_array;
93 mixin("Packed" ~ bindNameOverride!T ~ "Array_Bind _bind;");
94 }
95
96 package(godot) _PackedArray _packed_array;
97 alias _packed_array this;
98
99 alias VARIANT_TYPE = PackedArrayVariantType[staticIndexOf!(T, PackedArrayTypes)];
100
101 this(this) {
102 import std.array;
103
104 //mixin("auto n = _godot_api."~(typeName!T)~"_new_copy;");
105 auto ctor = _godot_api.variant_get_ptr_constructor(VARIANT_TYPE, 1);
106 const auto args = [_godot_array].staticArray;
107 ctor(_godot_array, args.ptr);
108
109 //n(&_godot_array, &tmp);
110
111 }
112
113 package(godot) this(GDExtensionTypePtr opaque) {
114 _godot_array = opaque;
115 }
116
117 PackedArray opAssign(in PackedArray other) {
118 auto dtor = _godot_api.variant_get_ptr_destructor(VARIANT_TYPE);
119 auto ctor = _godot_api.variant_get_ptr_constructor(VARIANT_TYPE, 1);
120 dtor(&_godot_array);
121 ctor(&_godot_array, &other._godot_array);
122 return this;
123 }
124
125 /++
126 C API type to pass to/from C functions
127 +/
128 static if (is(T == Vector2))
129 private alias InternalType = godot_vector2;
130 else static if (is(T == Vector3))
131 private alias InternalType = godot_vector3;
132 else static if (is(T == Color))
133 private alias InternalType = godot_color;
134 else
135 private alias InternalType = T;
136
137 this(Array arr) {
138 auto n = _godot_api.variant_get_ptr_constructor(VARIANT_TYPE, 2);
139 n(&_godot_array, cast(void**)&arr._godot_array);
140 }
141
142 ///
143 void pushBack(in ref PackedArray arr) {
144 _bind.appendArray(arr);
145 //mixin("auto a = _godot_api."~(typeName!T)~"_append_array;");
146 //a(&_godot_array, &arr._godot_array);
147 }
148
149 deprecated("Use the concatenation operator ~= instead of append_array.") alias append_array = pushBack;
150
151 void invert() {
152 _bind.reverse();
153 //mixin("auto i = _godot_api."~(typeName!T)~"_invert;");
154 //i(&_godot_array);
155 }
156
157 void remove(size_t idx) {
158 _bind.removeAt(idx);
159 //mixin("auto r = _godot_api."~(typeName!T)~"_remove;");
160 //r(&_godot_array, cast(int)idx);
161 }
162
163 void resize(size_t size) {
164 _bind.resize(size);
165 //mixin("auto r = _godot_api."~(typeName!T)~"_resize;");
166 //r(&_godot_array, cast(int)size);
167 }
168
169 size_t size() const {
170 return _bind.size();
171 //mixin("auto s = _godot_api."~(typeName!T)~"_size;");
172 //return s(&_godot_array);
173 }
174
175 alias length = size; // D-style name for size
176 alias opDollar = size;
177
178 /// Returns: true if length is 0.
179 bool empty() const {
180 return length == 0;
181 }
182
183 ~this() {
184 //auto d = _godot_api.variant_get_ptr_destructor(GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY)
185 auto d = _godot_api.variant_get_ptr_destructor(VARIANT_TYPE);
186 d(&_godot_array);
187 }
188
189 // a few functions are different for Strings than for the others:
190 //static if(is(T == String))
191 //{
192 // void pushBack(in String data)
193 // {
194 // _godot_api.packed_string_array_push_back(&_godot_array, &data._godot_string);
195 // }
196 // void insert(size_t idx, in String data)
197 // {
198 // _godot_api.packed_string_array_insert(&_godot_array, cast(int)idx, &data._godot_string);
199 // }
200 // void set(size_t idx, in String data)
201 // {
202 // _godot_api.packed_string_array_operator_index(&_godot_array, cast(int)idx) = &data._godot_string;
203 // }
204 // void opIndexAssign(in String data, size_t idx)
205 // {
206 // _godot_api.packed_string_array_operator_index(&_godot_array, cast(int)idx) = &data._godot_string;
207 // }
208 // String opIndex(size_t idx) const
209 // {
210 // String ret = void;
211 // ret._godot_string = godot_string(cast(size_t) _godot_api.packed_string_array_operator_index_const(&_godot_array, cast(int)idx));
212 // return ret;
213 // }
214 //}
215 //else
216 //{
217 void pushBack(in T data) {
218 _bind.pushBack(data);
219 //mixin("auto p = _godot_api."~(typeName!T)~"_push_back;");
220 //static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
221 // p(&_godot_array, cast(InternalType*)&data);
222 //else p(&_godot_array, data);
223 }
224
225 void insert(size_t idx, in T data) {
226 _bind.insert(idx, data);
227 //mixin("auto i = _godot_api."~(typeName!T)~"_insert;");
228 //static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
229 // i(&_godot_array, cast(int)idx, cast(InternalType*)&data);
230 //else i(&_godot_array, cast(int)idx, data);
231 }
232
233 void set(size_t idx, in T data) {
234 _bind.set(idx, data);
235 //mixin("auto s = _godot_api."~(typeName!T)~"_set;");
236 //static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
237 // s(&_godot_array, cast(int)idx, cast(InternalType*)&data);
238 //else s(&_godot_array, cast(int)idx, data);
239 }
240
241 void opIndexAssign(in T data, size_t idx) {
242 _bind.set(idx, data);
243 //mixin("auto s = _godot_api."~(typeName!T)~"_set;");
244 //static if(is(T==Vector2) || is(T==Vector3) || is(T==Color))
245 // s(&_godot_array, cast(int)idx, cast(InternalType*)&data);
246 //else s(&_godot_array, cast(int)idx, data);
247 }
248
249 T opIndex(size_t idx) const {
250 mixin("auto g = _godot_api." ~ (typeName!T) ~ "_operator_index_const;");
251 static union V {
252 T t;
253 InternalType r;
254 }
255
256 V v;
257 v.r = *cast(InternalType*) g(&_godot_array, cast(int) idx);
258 return v.t;
259 }
260 //}
261
262 ///
263 alias append = pushBack;
264 ///
265 template opOpAssign(string op) if (op == "~" || op == "+") {
266 alias opOpAssign = pushBack;
267 }
268
269 ///
270 PackedArray opBinary(string op)(in ref PackedArray other) const
271 if (op == "~" || op == "+") {
272 PackedArray ret = this;
273 ret ~= other;
274 return ret;
275 }
276
277 static if (is(T == String))
278 char* data() inout {
279 return cast(char*) _godot_array;
280 }
281 else
282 T* data() inout {
283 return cast(T*) _godot_array;
284 }
285
286 // Superbelko: PoolVector was replaced by Vector, all PoolTypeArray's was replaced with PackedTypeArray
287 // which is simply Vector<Type> under the hood plus bells and whistles.
288 // No need to keep this anymore, but ok. use raw pointer instead of Read.
289 /// Read/Write access locks with RAII.
290 version (none) static struct Access(bool write = false) {
291 private enum string rw = write ? "operator_index" : "operator_index_const";
292 private enum string RW = write ? "Write" : "Read";
293 static if (write)
294 private alias access = writeName!T;
295 else
296 private alias access = readName!T;
297
298 private {
299 mixin(access ~ "* _access;");
300 T[] _data;
301 }
302
303 static if (write) {
304 ///
305 inout(T[]) data() inout {
306 return _data;
307 }
308 } else {
309 ///
310 const(T[]) data() const {
311 return _data;
312 }
313 }
314 // TODO: `scope` for data to ensure it doesn't outlive `this`?
315 alias data this;
316
317 this(PackedArray!T p) {
318 mixin("_access = _godot_api." ~ typeName!T ~ "_" ~ rw ~ "(&p._godot_array);");
319 mixin("void* _ptr = cast(void*)_godot_api." ~ access ~ "_ptr(_access);");
320 _data = (cast(T*) _ptr)[0 .. p.length];
321 }
322
323 this(this) {
324 mixin("_access = _godot_api." ~ access ~ "_copy(_access);");
325 }
326
327 void opAssign(const ref typeof(this) other) {
328 mixin("_godot_api." ~ access ~ "_destroy(_access);");
329 mixin("_access = _godot_api." ~ access ~ "_copy(other._access);");
330 }
331
332 ~this() {
333 mixin("_godot_api." ~ access ~ "_destroy(_access);");
334 }
335 }
336
337 version (none) {
338
339 ///
340 alias Read = Access!false;
341 /// Lock the array for read-only access to the underlying memory.
342 /// This is faster than using opIndex, which locks each time it's called.
343 Read read() const {
344 return Read(this);
345 }
346 ///
347 alias Write = Access!true;
348 /// Lock the array for write access to the underlying memory.
349 /// This is faster than using opIndexAssign, which locks each time it's called.
350 Write write() {
351 return Write(this);
352 }
353 }
354
355 /// Slice-like view of the PackedArray.
356 static struct Range {
357 private {
358 PackedArray* arr;
359 size_t start, end;
360 }
361
362 bool empty() const {
363 return start == end;
364 }
365
366 size_t length() const {
367 return end - start;
368 }
369
370 alias opDollar = length;
371 T front() {
372 return (*arr)[start];
373 }
374
375 void popFront() {
376 ++start;
377 }
378
379 T back() {
380 return (*arr)[end - 1];
381 }
382
383 void popBack() {
384 --end;
385 }
386
387 T opIndex(size_t index) {
388 return (*arr)[index + start];
389 }
390
391 Range save() {
392 return this;
393 }
394 }
395
396 static assert(isRandomAccessRange!Range);
397
398 /// Returns: a slice-like Range view over the array.
399 /// Note: Prefer `read()`/`write()`; Range locks the array on each individual access.
400 Range opSlice() {
401 return Range(&this, 0, length);
402 }
403 /// ditto
404 Range opSlice(size_t start, size_t end) {
405 return Range(&this, start, end);
406 }
407 }