1 /** 2 Memory-pool-based dynamic arrays. Optimized for memory usage, can’t fragment the memory. 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.poolarrays; 14 15 import godot.c; 16 import godot.core.array; 17 import godot.core.defs; 18 import godot.core..string; 19 import godot.core.color; 20 import godot.core.vector2; 21 import godot.core.vector3; 22 23 import std.range.primitives; 24 import std.meta, std.traits; 25 26 private alias PoolArrayTypes = AliasSeq!( 27 ubyte, 28 int, 29 real_t, 30 String, 31 Vector2, 32 Vector3, 33 Color 34 ); 35 36 private enum string nameOverride(T) = AliasSeq!( 37 "byte", "int", "real", "string", "vector2", "vector3", "color")[staticIndexOf!(T, PoolArrayTypes)]; 38 39 private enum string typeName(T) = "godot_pool_"~(nameOverride!T)~"_array"; 40 private enum string readName(T) = "godot_pool_"~(nameOverride!T)~"_array_read_access"; 41 private enum string writeName(T) = "godot_pool_"~(nameOverride!T)~"_array_write_access"; 42 43 alias PoolByteArray = PoolArray!ubyte; 44 alias PoolIntArray = PoolArray!int; 45 alias PoolRealArray = PoolArray!real_t; 46 alias PoolStringArray = PoolArray!String; 47 alias PoolVector2Array = PoolArray!Vector2; 48 alias PoolVector3Array = PoolArray!Vector3; 49 alias PoolColorArray = PoolArray!Color; 50 51 /++ 52 Copy-on-write array for some Godot types, allocated with a memory pool. 53 +/ 54 struct PoolArray(T) 55 { 56 @nogc nothrow: 57 58 static assert(staticIndexOf!(T, PoolArrayTypes) != -1, 59 "Cannot make a Godot PoolArray for a non-Godot type"); 60 61 mixin("package(godot) "~(typeName!T)~" _godot_array;"); 62 63 this(this) 64 { 65 mixin("auto n = _godot_api."~(typeName!T)~"_new_copy;"); 66 const auto tmp = _godot_array; 67 n(&_godot_array, &tmp); 68 } 69 70 PoolArray opAssign(in PoolArray other) 71 { 72 mixin("auto d = _godot_api."~(typeName!T)~"_destroy;"); 73 mixin("auto n = _godot_api."~(typeName!T)~"_new_copy;"); 74 d(&_godot_array); 75 n(&_godot_array, &other._godot_array); 76 return this; 77 } 78 79 /++ 80 C API type to pass to/from C functions 81 +/ 82 static if(is(T==Vector2)) private alias InternalType = godot_vector2; 83 else static if(is(T==Vector3)) private alias InternalType = godot_vector3; 84 else static if(is(T==Color)) private alias InternalType = godot_color; 85 else private alias InternalType = T; 86 87 this(Array arr) 88 { 89 mixin("auto n = _godot_api."~(typeName!T)~"_new_with_array;"); 90 n(&_godot_array, &arr._godot_array); 91 } 92 93 /// 94 void pushBack(in ref PoolArray arr) 95 { 96 mixin("auto a = _godot_api."~(typeName!T)~"_append_array;"); 97 a(&_godot_array, &arr._godot_array); 98 } 99 deprecated("Use the concatenation operator ~= instead of append_array.") alias append_array = pushBack; 100 101 void invert() 102 { 103 mixin("auto i = _godot_api."~(typeName!T)~"_invert;"); 104 i(&_godot_array); 105 } 106 107 void remove(size_t idx) 108 { 109 mixin("auto r = _godot_api."~(typeName!T)~"_remove;"); 110 r(&_godot_array, cast(int)idx); 111 } 112 113 void resize(size_t size) 114 { 115 mixin("auto r = _godot_api."~(typeName!T)~"_resize;"); 116 r(&_godot_array, cast(int)size); 117 } 118 119 size_t size() const 120 { 121 mixin("auto s = _godot_api."~(typeName!T)~"_size;"); 122 return s(&_godot_array); 123 } 124 alias length = size; // D-style name for size 125 alias opDollar = size; 126 127 /// Returns: true if length is 0. 128 bool empty() const 129 { 130 return length == 0; 131 } 132 133 ~this() 134 { 135 mixin("auto d = _godot_api."~(typeName!T)~"_destroy;"); 136 d(&_godot_array); 137 } 138 139 140 // a few functions are different for Strings than for the others: 141 static if(is(T == String)) 142 { 143 void pushBack(in String data) 144 { 145 _godot_api.godot_pool_string_array_push_back(&_godot_array, &data._godot_string); 146 } 147 void insert(size_t idx, in String data) 148 { 149 _godot_api.godot_pool_string_array_insert(&_godot_array, cast(int)idx, &data._godot_string); 150 } 151 void set(size_t idx, in String data) 152 { 153 _godot_api.godot_pool_string_array_set(&_godot_array, cast(int)idx, &data._godot_string); 154 } 155 void opIndexAssign(in String data, size_t idx) 156 { 157 _godot_api.godot_pool_string_array_set(&_godot_array, cast(int)idx, &data._godot_string); 158 } 159 String opIndex(size_t idx) const 160 { 161 String ret = void; 162 ret._godot_string = _godot_api.godot_pool_string_array_get(&_godot_array, cast(int)idx); 163 return ret; 164 } 165 } 166 else 167 { 168 void pushBack(in T data) 169 { 170 mixin("auto p = _godot_api."~(typeName!T)~"_push_back;"); 171 static if(is(T==Vector2) || is(T==Vector3) || is(T==Color)) 172 p(&_godot_array, cast(InternalType*)&data); 173 else p(&_godot_array, data); 174 } 175 void insert(size_t idx, in T data) 176 { 177 mixin("auto i = _godot_api."~(typeName!T)~"_insert;"); 178 static if(is(T==Vector2) || is(T==Vector3) || is(T==Color)) 179 i(&_godot_array, cast(int)idx, cast(InternalType*)&data); 180 else i(&_godot_array, cast(int)idx, data); 181 } 182 void set(size_t idx, in T data) 183 { 184 mixin("auto s = _godot_api."~(typeName!T)~"_set;"); 185 static if(is(T==Vector2) || is(T==Vector3) || is(T==Color)) 186 s(&_godot_array, cast(int)idx, cast(InternalType*)&data); 187 else s(&_godot_array, cast(int)idx, data); 188 } 189 void opIndexAssign(in T data, size_t idx) 190 { 191 mixin("auto s = _godot_api."~(typeName!T)~"_set;"); 192 static if(is(T==Vector2) || is(T==Vector3) || is(T==Color)) 193 s(&_godot_array, cast(int)idx, cast(InternalType*)&data); 194 else s(&_godot_array, cast(int)idx, data); 195 } 196 T opIndex(size_t idx) const 197 { 198 mixin("auto g = _godot_api."~(typeName!T)~"_get;"); 199 static union V 200 { 201 T t; 202 InternalType r; 203 } 204 V v; 205 v.r = g(&_godot_array, cast(int)idx); 206 return v.t; 207 } 208 } 209 210 /// 211 alias append = pushBack; 212 /// 213 template opOpAssign(string op) if(op == "~" || op == "+") 214 { 215 alias opOpAssign = pushBack; 216 } 217 218 /// 219 PoolArray opBinary(string op)(in ref PoolArray other) const if(op == "~" || op == "+") 220 { 221 PoolArray ret = this; 222 ret ~= other; 223 return ret; 224 } 225 226 /// Read/Write access locks with RAII. 227 static struct Access(bool write = false) 228 { 229 private enum string rw = write ? "write" : "read"; 230 private enum string RW = write ? "Write" : "Read"; 231 static if(write) private alias access = writeName!T; 232 else private alias access = readName!T; 233 234 private 235 { 236 mixin(access ~ "* _access;"); 237 T[] _data; 238 } 239 240 static if(write) 241 { 242 /// 243 inout(T[]) data() inout { return _data; } 244 } 245 else 246 { 247 /// 248 const(T[]) data() const { return _data; } 249 } 250 // TODO: `scope` for data to ensure it doesn't outlive `this`? 251 alias data this; 252 253 this(PoolArray!T p) 254 { 255 mixin("_access = _godot_api." ~ typeName!T ~ "_" ~ rw ~ "(&p._godot_array);"); 256 mixin("void* _ptr = cast(void*)_godot_api." ~ access ~ "_ptr(_access);"); 257 _data = (cast(T*)_ptr)[0..p.length]; 258 } 259 this(this) 260 { 261 mixin("_access = _godot_api." ~ access ~ "_copy(_access);"); 262 } 263 void opAssign(const ref typeof(this) other) 264 { 265 mixin("_godot_api." ~ access ~ "_destroy(_access);"); 266 mixin("_access = _godot_api." ~ access ~ "_copy(other._access);"); 267 } 268 ~this() 269 { 270 mixin("_godot_api." ~ access ~ "_destroy(_access);"); 271 } 272 } 273 274 /// 275 alias Read = Access!false; 276 /// Lock the array for read-only access to the underlying memory. 277 /// This is faster than using opIndex, which locks each time it's called. 278 Read read() const { return Read(this); } 279 /// 280 alias Write = Access!true; 281 /// Lock the array for write access to the underlying memory. 282 /// This is faster than using opIndexAssign, which locks each time it's called. 283 Write write() { return Write(this); } 284 285 /// Slice-like view of the PoolArray. 286 static struct Range 287 { 288 private 289 { 290 PoolArray* arr; 291 size_t start, end; 292 } 293 294 bool empty() const { return start == end; } 295 size_t length() const { return end - start; } 296 alias opDollar = length; 297 T front() { return (*arr)[start]; } 298 void popFront() { ++start; } 299 T back() { return (*arr)[end-1]; } 300 void popBack() { --end; } 301 T opIndex(size_t index) { return (*arr)[index+start]; } 302 303 Range save() { return this; } 304 } 305 static assert(isRandomAccessRange!Range); 306 307 /// Returns: a slice-like Range view over the array. 308 /// Note: Prefer `read()`/`write()`; Range locks the array on each individual access. 309 Range opSlice() { return Range(&this, 0, length); } 310 /// ditto 311 Range opSlice(size_t start, size_t end) { return Range(&this, start, end); } 312 } 313