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 }