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