1 /** 2 Dynamic Variant array. 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.array; 15 16 import godot.abi; 17 import godot.variant; 18 import godot.poolarrays; 19 20 // generated raw bindings 21 import godot.builtins; 22 import godot.api.bind; 23 24 import std.meta; 25 import std.traits, std.range; 26 import std.exception : assumeWontThrow; 27 28 /** 29 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. 30 */ 31 struct Array { 32 int opApply(int delegate(size_t, ref Variant) dg) { 33 foreach (i; 0 .. length) { 34 Variant* v = cast(Variant*)&(this[i]); 35 int res = dg(cast(size_t) i, *v); 36 if (res) 37 return res; 38 } 39 return 0; 40 } 41 42 int opApply(int delegate(size_t, const(Variant)) dg) const { 43 foreach (i; 0 .. length) { 44 int res = dg(cast(size_t) i, this[i]); 45 if (res) 46 return res; 47 } 48 return 0; 49 } 50 51 int opApply(int delegate(ref Variant) dg) { 52 foreach (i; 0 .. length) { 53 Variant* v = cast(Variant*)&(this[i]); 54 int res = dg(*v); 55 if (res) 56 return res; 57 } 58 return 0; 59 } 60 61 int opApply(int delegate(const(Variant)) dg) const { 62 foreach (i; 0 .. length) { 63 int res = dg(this[i]); 64 if (res) 65 return res; 66 } 67 return 0; 68 } 69 70 /// Convert to a static array. 71 /// Excess elements are discarded if the Array is longer than `T`. 72 T as(T)() const 73 if (isStaticArray!T && Variant.compatibleFromGodot!(ElementType!T)) { 74 import std.algorithm : min; 75 76 T ret; 77 foreach (i; 0 .. min(T.length, length)) 78 ret[i] = (this[i]).as!(ElementType!T); 79 return ret; 80 } 81 82 /// Create an Array from any D range or static array with compatible elements. 83 static Array from(T)(T t) 84 if ((isForwardRange!T || isStaticArray!T) && Variant.compatibleToGodot!( 85 ElementType!T)) { 86 import std.algorithm.iteration; 87 88 Array ret = Array.make(); 89 static if (hasLength!T) { 90 ret.resize(cast(int) t.length); 91 int ei = 0; 92 foreach (e; t) 93 ret[ei++] = e; 94 } else 95 t.each!(e => ret ~= e); 96 return ret; 97 } 98 99 //@nogc nothrow: 100 101 package(godot) { 102 union array { 103 godot_array _godot_array; 104 Array_Bind _bind; 105 } 106 107 array _array; 108 } 109 110 alias _array this; 111 112 package(godot) this(godot_array opaque) { 113 _godot_array = opaque; 114 } 115 116 //@disable this(this); 117 //{ 118 // const godot_array tmp = _godot_array; 119 // _godot_api.get_variant_from_type_constructor(GDEXTENSION_VARIANT_TYPE_ARRAY)(cast(GDExtensionTypePtr) &_godot_array, cast(GDExtensionTypePtr) &tmp); 120 // this = _bind.duplicate(false); 121 //} 122 123 this(const scope ref Array other) { 124 //if (&_godot_array) 125 // _bind._destructor(); 126 _godot_array = other._godot_array; 127 } 128 129 Array opAssign(in Array other) { 130 //if (&_godot_array) 131 // _bind._destructor(); 132 this = _bind.new1(other); // do we actually need a copy here? 133 return this; 134 //_godot_api.variant_destroy(&_godot_array); 135 //_godot_api.variant_new_copy(&_godot_array, &other._godot_array); 136 //return this; 137 } 138 139 /++ 140 Assigning null empties the Array variable, but unlike `clear`, does not 141 destroy the original memory unless it was the only remaining reference. 142 +/ 143 Array opAssign(in typeof(null) n) { 144 return opAssign(Array.make()); 145 } 146 147 /++ 148 Create an array and add all $(PARAM args) to it. 149 +/ 150 static Array make(Args...)(Args args) 151 if (allSatisfy!(Variant.compatibleToGodot, Args)) { 152 Array ret = void; 153 ret._godot_array = godot_array.init; 154 auto ct = _godot_api.variant_get_ptr_constructor(GDEXTENSION_VARIANT_TYPE_ARRAY, 0); 155 ct(cast(GDExtensionTypePtr)&ret._godot_array, null); 156 157 static if (args.length) 158 ret.resize(args.length); 159 static foreach (i, Arg; Args) { 160 ret[i] = args[i]; 161 } 162 return ret; 163 } 164 165 deprecated("Use Array.make() with 0 args instead.") 166 static Array empty_array() { 167 Array ret = void; 168 _godot_api.variant_new_nil(&ret._godot_array); 169 return ret; 170 } 171 172 this(in typeof(null) n) { 173 //_godot_api.variant_new_nil(&_godot_array); 174 _bind.new0(); 175 } 176 177 // TODO: verify the following array constructors, since previous API's are gone 178 // it now uses overloads of Array.from() extension method 179 this(in PackedByteArray a) { 180 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 181 } 182 183 this(in PackedInt32Array a) { 184 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 185 } 186 187 this(in PackedInt64Array a) { 188 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 189 } 190 191 this(in PackedFloat32Array a) { 192 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 193 } 194 195 this(in PackedFloat64Array a) { 196 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 197 } 198 199 this(in PackedStringArray a) { 200 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 201 } 202 203 this(in PackedVector2Array a) { 204 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 205 } 206 207 this(in PackedVector3Array a) { 208 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 209 } 210 211 this(in PackedColorArray a) { 212 _godot_api.variant_new_copy(&_godot_array, &a._godot_array); 213 } 214 215 auto ref inout(Variant) opIndex(size_t idx) inout { 216 godot_variant* v = cast(godot_variant*) _godot_api.array_operator_index( 217 cast(godot_array*)&_godot_array, cast(int) idx); 218 return *cast(inout(Variant)*) v; 219 } 220 221 void opIndexAssign(T)(auto ref T value, in size_t idx) 222 if (is(T : Variant) || Variant.compatibleToGodot!T) { 223 Variant v = Variant(value); 224 godot_variant* val = cast(godot_variant*) _godot_api.array_operator_index( 225 &_godot_array, cast(int) idx); 226 *val = v._godot_variant; 227 } 228 229 /// Append a single element. 230 /// 231 /// Note: an Array or range will be appended as one single element of type 232 /// Array, *not* concatenated to this Array. Use `appendRange` or 233 /// `appendArray` to concatenate/chain ranges or Arrays into one. 234 void append(T)(auto ref T t) if (is(T : Variant) || Variant.compatibleToGodot!T) { 235 import godot.string; 236 237 Variant v = Variant(t); 238 //_godot_api.array_append(&_godot_array, &v._godot_variant); 239 _bind.append(v); 240 } 241 242 void append(const(void)* nativeObjectPtr) { 243 import godot.string; 244 245 Array a = void; 246 a._godot_array = _godot_array; 247 // TODO: implement me 248 //Variant v = Variant.from(a); 249 //_godot_api.array_append(&_godot_array, &v._godot_variant); 250 //v.as!Array.append(nativeObjectPtr); 251 } 252 /// ditto 253 template opOpAssign(string op) if (op == "~" || op == "+") { 254 alias opOpAssign = append; 255 } 256 257 /// Concatenate a range or another Array to the end of this one. 258 void appendRange(R)(in auto ref R other) 259 if ( 260 !is(Unqual!R : Array) && 261 isInputRange!R && 262 (is(ElementType!R 263 : Variant) || Variant.compatible!(ElementType!R))) { 264 static if (hasLength!R) { 265 size_t l = length; 266 resize(l + other.length); 267 Variant[] slice = this[]; 268 size_t i = l; 269 foreach (const v; other) 270 slice[i++] = v; 271 } else 272 foreach (const v; other) 273 append(v); 274 } 275 /// ditto 276 void appendArray(in ref Array other) { 277 appendRange(other[]); 278 } 279 280 private static Array fromConcat(R, S)(in auto ref R r, in auto ref S s) { 281 Array ret = Array.make(); 282 ret.resize(r.length + s.length); 283 Variant[] slice = ret[]; 284 size_t i = 0; 285 foreach (const v; r) 286 slice[i++] = v; 287 foreach (const v; s) 288 slice[i++] = v; 289 return ret; 290 } 291 /// Concatenate two arrays into a new one. The originals are left unaffected 292 /// if there are still other references to them remaining. 293 Array opBinary(string op, R)(in auto ref R other) 294 if ( 295 (op == "~" || op == "+") && !is(Unqual!R : Array) && 296 isInputRange!R && hasLength!R && 297 (is(ElementType!R : Variant) || Variant.compatible!(ElementType!R))) { 298 return fromConcat(this[], other); 299 } 300 /// ditto 301 Array opBinary(string op)(in auto ref Array other) if (op == "~" || op == "+") { 302 return fromConcat(this[], other[]); 303 } 304 /// ditto 305 Array opBinaryRight(string op, R)(in auto ref R other) 306 if ( 307 (op == "~" || op == "+") && !is(Unqual!R : Array) && 308 isInputRange!R && hasLength!R && 309 (is(ElementType!R : Variant) || Variant.compatible!(ElementType!R))) { 310 return fromConcat(other, this[]); 311 } 312 313 void clear() { 314 _bind.clear(); 315 } 316 317 size_t count(in Variant v) { 318 //return _godot_api.array_count(&_godot_array, &v._godot_variant); 319 return _bind.count(v); 320 } 321 322 bool empty() const { 323 //return cast(bool)_godot_api.array_empty(&_godot_array); 324 return _bind.isEmpty(); 325 } 326 327 void erase(T)(T v) if (is(T : Variant) || Variant.compatibleToGodot!T) { 328 Variant vv = v; 329 //_godot_api.array_erase(&_godot_array, &vv._godot_variant); 330 _bind.erase(v); 331 } 332 333 Variant front() const { 334 godot_variant v = void; 335 v = _bind.front()._godot_variant; 336 //godot_variant v = _godot_api.array_front(&_godot_array); 337 return cast(Variant) v; 338 } 339 340 Variant back() const { 341 //godot_variant v = _godot_api.array_back(&_godot_array); 342 //return cast(Variant)v; 343 return _bind.back(); 344 } 345 346 int find(T)(in T what, size_t from) const 347 if (is(T : Variant) || Variant.compatibleToGodot!T) { 348 const Variant vv = what; 349 //return _godot_api.array_find(&_godot_array, &vv._godot_variant, cast(int)from); 350 return _bind.find(vv, from); 351 } 352 353 int findLast(T)(in T what) const 354 if (is(T : Variant) || Variant.compatibleToGodot!T) { 355 const Variant vv = what; 356 //return _godot_api.array_find_last(&_godot_array, &vv._godot_variant); 357 return _bind.findLast(vv); 358 } 359 360 bool has(T)(in T what) const if (is(T : Variant) || Variant.compatibleToGodot!T) { 361 const Variant vv = what; 362 //return cast(bool)_godot_api.array_has(&_godot_array, &vv._godot_variant); 363 return _bind.has(vv); 364 } 365 366 @trusted 367 uint hash() const nothrow { 368 //return _godot_api.array_hash(&_godot_array); 369 return cast(uint) assumeWontThrow(_bind.hash()); 370 } 371 372 @trusted 373 hash_t toHash() const nothrow { 374 return cast(hash_t) hash(); 375 } 376 377 void insert(T)(const size_t pos, T value) 378 if (is(T : Variant) || Variant.compatibleToGodot!T) { 379 Variant vv = value; 380 //_godot_api.array_insert(&_godot_array, cast(int)pos, &vv._godot_variant); 381 _bind.insert(pos, vv); 382 } 383 384 void invert() { 385 //_godot_api.array_invert(&_godot_array); 386 _bind.reverse(); 387 } 388 389 Variant popBack() { 390 //godot_variant v = _godot_api.array_pop_back(&_godot_array); 391 //return cast(Variant)v; 392 return _bind.popBack(); 393 } 394 395 Variant popFront() { 396 //godot_variant v = _godot_api.array_pop_front(&_godot_array); 397 //return cast(Variant)v; 398 return _bind.popFront(); 399 } 400 401 void pushBack(T)(T v) if (is(T : Variant) || Variant.compatibleToGodot!T) { 402 Variant vv = v; 403 //_godot_api.array_push_back(&_godot_array, &vv._godot_variant); 404 _bind.pushBack(vv); 405 } 406 407 void pushFront(T)(T v) if (is(T : Variant) || Variant.compatibleToGodot!T) { 408 Variant vv = v; 409 //_godot_api.array_push_front(&_godot_array, &vv._godot_variant); 410 _bind.pushFront(vv); 411 } 412 413 void remove(size_t idx) { 414 //_godot_api.array_remove(&_godot_array, cast(int)idx); 415 _bind.removeAt(idx); 416 } 417 418 size_t size() const { 419 //return _godot_api.array_size(&_godot_array); 420 return _bind.size(); 421 } 422 423 alias length = size; // D-style `length` 424 alias opDollar = size; 425 426 void resize(size_t size) { 427 //_godot_api.array_resize(&_godot_array, cast(int)size); 428 _bind.resize(size); 429 } 430 431 int rfind(T)(in T what, size_t from) const 432 if (is(T : Variant) || Variant.compatibleToGodot!T) { 433 const Variant vv = what; 434 //return _godot_api.array_rfind(&_godot_array, &vv._godot_variant, cast(int)from); 435 return _bind.rfind(vv, from); 436 } 437 438 void sort() { 439 //_godot_api.array_sort(&_godot_array); 440 _bind.sort(); 441 } 442 443 /+void sort_custom(godot.Object obj, in ref String func) 444 { 445 _godot_api.array_sort_custom(&_godot_array, obj, &func._godot_string); 446 }+/ 447 448 /// Allocate a new separate copy of the Array 449 Array dup() const { 450 Array ret = Array.make(); 451 size_t l = size(); 452 ret.resize(l); 453 foreach (vi; 0 .. l) { 454 ret[vi] = this[vi]; 455 } 456 return ret; 457 } 458 459 /// Returns: a new Array containing a slice of the original. It is a copy, 460 /// *not* a reference to the original Array's memory. 461 /// 462 /// Note: `end` is non-inclusive, as in D slice operations, not as in Godot. 463 Array slice(size_t start, size_t end, size_t stride = 1, bool deep = false) const { 464 //Array ret = void; 465 //ret._godot_array = _godot_api.array_slice(&_godot_array, 466 // cast(int)start, cast(int)(end-1), cast(int)stride, deep); 467 return _bind.slice(start, end, stride, deep); 468 } 469 470 /++ 471 Returns: a slice of the array memory. The slice does *not* have ownership of 472 the reference-counted memory and is invalid after the original Array goes 473 out of scope or is resized. 474 +/ 475 Variant[] opSlice(size_t start, size_t end) { 476 Variant* ret = cast(Variant*) _godot_api.array_operator_index(&_godot_array, 0); 477 return ret[start .. end]; 478 } 479 /// ditto 480 const(Variant)[] opSlice(size_t start, size_t end) const { 481 const(Variant)* ret = cast(const(Variant)*) _godot_api.array_operator_index_const( 482 &_godot_array, 0); 483 return ret[start .. end]; 484 } 485 /// ditto 486 Variant[] opSlice() { 487 return this[0 .. length]; 488 } 489 /// ditto 490 const(Variant)[] opSlice() const { 491 return this[0 .. length]; 492 } 493 // TODO: `scope` for the returned slices? 494 495 ~this() { 496 //if (&_godot_array) 497 // _bind._destructor(); 498 _array = _array.init; 499 //_godot_api.variant_destroy(&_godot_array); 500 } 501 } 502 503 struct TypedArray(T) { 504 Array _array; 505 alias _array this; 506 507 this(this) { 508 } 509 510 this(Array other) { 511 _array = other; 512 } 513 514 this(T[] other) { 515 _array = Array.from(other); 516 } 517 }