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