1 /// Copyright: Copyright (c) 2017-2020 Andrey Penechko. 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 /// Authors: Andrey Penechko. 4 5 module vox.ir.ir_small_array; 6 7 import std.bitmanip : bitfields; 8 import std.format : formattedWrite; 9 import vox.all; 10 11 /// Stores 0-2 items inline 12 /// Allocates memory from IrFuncStorage.arrayBuffer for arrays with > 2 slots 13 struct IrSmallArray 14 { 15 union { 16 IrIndex[2] items; // .kind != IrValueKind.array 17 struct { 18 // .kind == IrValueKind.array 19 // offset into IrFuncStorage.arrayBuffer 20 IrIndex arrayIndex; 21 mixin(bitfields!( 22 // Number of items inside big array 23 uint, "arrayLength", 23, 24 // capacity is bigCapacity 25 uint, "capacityPower", 5, 26 // 4 bits need to be clear, so IrIndex.kind == IrvalueKind.none 27 // important for automatic index fixing after moving 28 // when switching from built-in items to external array 29 // item[1] must cleared 30 uint, "", 4 31 )); 32 } 33 } 34 35 private uint bigCapacity() { return 1 << capacityPower; } 36 37 uint capacity() 38 { 39 if (isBig) return bigCapacity; 40 else return 2; 41 } 42 43 uint length() 44 { 45 if (items[0].kind == IrValueKind.array) 46 return arrayLength; 47 else if (items[1].isDefined) 48 return 2; 49 else if (items[0].isDefined) 50 return 1; 51 else 52 return 0; 53 } 54 55 bool empty() { return length == 0; } 56 57 void free(IrFunction* ir) 58 { 59 if (isBig) 60 { 61 ir.freeIrArray(arrayIndex, bigCapacity); 62 } 63 items[0] = IrIndex(); 64 items[1] = IrIndex(); 65 } 66 67 bool isBig() 68 { 69 return items[0].kind == IrValueKind.array; 70 } 71 72 alias opCall = range; 73 IrSmallArrayIterator range(IrFunction* ir) return 74 { 75 return IrSmallArrayIterator(&this, ir); 76 } 77 78 void replaceAll(IrFunction* ir, IrIndex what, IrIndex byWhat) 79 { 80 foreach (ref IrIndex item; range(ir)) 81 { 82 if (item == what) item = byWhat; 83 } 84 } 85 86 bool contains(IrFunction* ir, IrIndex what) 87 { 88 foreach (IrIndex item; range(ir)) 89 { 90 if (item == what) return true; 91 } 92 return false; 93 } 94 95 /// Returns true if replacement was performed 96 bool replaceFirst(IrFunction* ir, IrIndex what, IrIndex byWhat) 97 { 98 foreach (ref IrIndex item; range(ir)) 99 { 100 if (item == what) { 101 item = byWhat; 102 return true; 103 } 104 } 105 return false; 106 } 107 108 // asserts if not found 109 uint findFirst(IrFunction* ir, IrIndex what) 110 { 111 //writefln("findFirst %s %s", what, data(ir)); 112 foreach (size_t index, ref IrIndex item; range(ir)) 113 { 114 if (item == what) return cast(uint)index; 115 } 116 assert(false, "Item not found"); 117 } 118 119 // returns uint.max if not found 120 uint tryFindFirst(IrFunction* ir, IrIndex what) 121 { 122 foreach (size_t index, ref IrIndex item; range(ir)) 123 { 124 if (item == what) return cast(uint)index; 125 } 126 return uint.max; 127 } 128 129 /// Removes all occurences of what 130 /// Preserves order of items 131 uint removeStable(IrFunction* ir, IrIndex what) 132 { 133 if (isBig) // external array 134 { 135 uint numRemoved = 0; 136 IrIndex* array = ir.getArray(arrayIndex); 137 138 foreach(i, ref IrIndex item; array[0..arrayLength]) 139 { 140 if (item == what) 141 ++numRemoved; 142 else 143 array[i - numRemoved] = item; 144 } 145 arrayLength = arrayLength - numRemoved; 146 afterRemoveBig(ir, array); // free array if needed 147 return numRemoved; 148 } 149 else return removeSmall(ir, what); // 0, 1, 2 150 } 151 152 /// Removes all occurences of what 153 /// Alters order of items 154 uint removeUnstable(IrFunction* ir, IrIndex what) 155 { 156 if (isBig) // external array 157 { 158 uint tempLength = arrayLength; 159 IrIndex* array = ir.getArray(arrayIndex); 160 161 uint i = 0; 162 while (i < tempLength) 163 { 164 if (array[i] == what) { 165 --tempLength; 166 array[i] = array[tempLength]; 167 } 168 else 169 ++i; 170 } 171 uint numRemoved = arrayLength - tempLength; 172 arrayLength = tempLength; 173 afterRemoveBig(ir, array); // free array if needed 174 return numRemoved; 175 } 176 else return removeSmall(ir, what); // 0, 1, 2 177 } 178 179 private uint removeSmall(IrFunction* ir, IrIndex what) 180 { 181 // no harm if what or items[0/1] is null here 182 if (items[0] == what) { 183 // remove first item in-place 184 if (items[1] == what) { 185 // and second too 186 items[0] = IrIndex(); 187 items[1] = IrIndex(); 188 return 2; 189 } else { 190 // only first 191 items[0] = items[1]; 192 items[1] = IrIndex(); 193 return 1; 194 } 195 } else if (items[1] == what) { 196 // remove second item in-place 197 items[1] = IrIndex(); 198 return 1; 199 } 200 return 0; 201 } 202 203 private void afterRemoveBig(IrFunction* ir, IrIndex* array) 204 { 205 switch (arrayLength) 206 { 207 case 0: 208 IrIndex arrayIndexCopy = arrayIndex; 209 ir.freeIrArray(arrayIndex, bigCapacity); 210 items[0] = IrIndex(); 211 items[1] = IrIndex(); 212 break; 213 case 1: 214 IrIndex arrayIndexCopy = arrayIndex; 215 uint _capacity = bigCapacity; 216 items[0] = array[0]; 217 items[1] = IrIndex(); 218 ir.freeIrArray(arrayIndexCopy, _capacity); 219 break; 220 case 2: 221 IrIndex arrayIndexCopy = arrayIndex; 222 uint _capacity = bigCapacity; 223 items[0] = array[0]; 224 items[1] = array[1]; 225 ir.freeIrArray(arrayIndexCopy, _capacity); 226 break; 227 default: break; 228 } 229 } 230 231 IrIndex[] data(IrFunction* ir) return 232 { 233 if (isBig) 234 { 235 IrIndex* array = ir.getArray(arrayIndex); 236 return array[0..arrayLength]; 237 } 238 else 239 { 240 if (items[1].isDefined) 241 { 242 return items[0..2]; 243 } 244 else if (items[0].isDefined) 245 { 246 return items[0..1]; 247 } 248 else 249 { 250 return null; 251 } 252 } 253 } 254 255 ref IrIndex opIndex(size_t index, IrFunction* ir) return 256 { 257 if (isBig) 258 { 259 IrIndex* array = ir.getArray(arrayIndex); 260 assert(index < arrayLength); 261 return array[index]; 262 } 263 else 264 { 265 switch(index) 266 { 267 case 0: 268 assert(items[0].isDefined, "Accessing index 0, when length is 0"); 269 return items[0]; 270 case 1: 271 assert(items[0].isDefined, "Accessing index 1, when length is < 2"); 272 return items[1]; 273 default: 274 assert(false); 275 } 276 } 277 } 278 279 void append(IrBuilder* builder, IrIndex itemData) 280 { 281 assert(itemData.kind != IrValueKind.array, "array is not storable inside IrSmallArray"); 282 assert(itemData.kind != IrValueKind.none, "IrValueKind.none is not storable inside IrSmallArray"); 283 if (isBig) 284 { 285 uint _capacity = bigCapacity; 286 IrIndex* array = builder.ir.getArray(arrayIndex); 287 if (arrayLength < _capacity) 288 { 289 //writefln("append cap %s len %s %s", _capacity, arrayLength, array[0..arrayLength]); 290 array[arrayLength] = itemData; 291 arrayLength = arrayLength + 1; 292 } 293 else 294 { 295 //writefln("append cap %s %s len %s extend %s %s", capacityPower, _capacity, arrayLength, arrayIndex, array[0..arrayLength]); 296 297 if (builder.tryExtendArray(arrayIndex, _capacity)) 298 { 299 // extend was performed in-place 300 array[arrayLength] = itemData; 301 302 // update instance 303 arrayLength = arrayLength + 1; 304 capacityPower = capacityPower + 1; 305 } 306 else 307 { 308 // allocate twice bigger array 309 uint newCapacity = _capacity * 2; 310 IrIndex newArrayIndex = builder.allocateIrArray(newCapacity); 311 //writefln(" alloc cap %s %s", newCapacity, newArrayIndex); 312 IrIndex* newArray = builder.ir.getArray(newArrayIndex); 313 // copy old data 314 newArray[0..arrayLength] = array[0..arrayLength]; 315 // add new item 316 newArray[arrayLength] = itemData; 317 // free old array 318 builder.ir.freeIrArray(arrayIndex, _capacity); 319 320 // update instance 321 arrayLength = arrayLength + 1; 322 capacityPower = capacityPower + 1; 323 arrayIndex = newArrayIndex; 324 } 325 326 //writefln(" len %s cap %s %s index %s", arrayLength, capacityPower, 1 << capacityPower, arrayIndex); 327 } 328 } 329 else 330 { 331 if (!items[0].isDefined) 332 { 333 items[0] = itemData; 334 } 335 else if (!items[1].isDefined) 336 { 337 items[1] = itemData; 338 } 339 else 340 { 341 IrIndex newArrayIndex = builder.allocateIrArray(4); 342 IrIndex* newArray = builder.ir.getArray(newArrayIndex); 343 newArray[0] = items[0]; 344 newArray[1] = items[1]; 345 newArray[2] = itemData; 346 347 // update instance 348 items[1] = IrIndex(); // clear bitfields 349 arrayIndex = newArrayIndex; 350 capacityPower = 2; // 1 << 2 == 4 351 arrayLength = 3; 352 //writefln(" len %s cap %s %s index %s", arrayLength, capacityPower, 1 << capacityPower, arrayIndex); 353 } 354 } 355 } 356 357 void toString(scope void delegate(const(char)[]) sink) 358 { 359 if (isBig) 360 sink.formattedWrite("[%s items]", arrayLength); 361 else if (items[1].isDefined) 362 sink.formattedWrite("[%s, %s]", items[0], items[1]); 363 else if (items[0].isDefined) 364 sink.formattedWrite("[%s]", items[0]); 365 else sink("[]"); 366 } 367 } 368 369 unittest 370 { 371 import std.stdio; 372 ubyte[1024] irBuf, tempBuf; 373 IrFunction ir; 374 CompilationContext context; 375 IrBuilder builder; 376 377 context.irStorage.arrayBuffer.setBuffer(irBuf[], 1024); 378 context.tempBuffer.setBuffer(tempBuf[], 1024); 379 380 builder.context = &context; 381 builder.ir = &ir; 382 ir.arrayPtr = context.irStorage.arrayBuffer.nextPtr; 383 384 IrSmallArray vec; 385 386 assert(vec.length == 0); 387 assert(!vec.isBig); 388 389 vec.append(&builder, IrIndex(1, IrValueKind.instruction)); 390 assert(vec.length == 1); 391 assert(!vec.isBig); 392 393 vec.append(&builder, IrIndex(2, IrValueKind.instruction)); 394 assert(vec.length == 2); 395 assert(!vec.isBig); 396 397 vec.append(&builder, IrIndex(3, IrValueKind.instruction)); 398 assert(vec.length == 3); 399 assert(vec.isBig); 400 401 vec.append(&builder, IrIndex(4, IrValueKind.instruction)); 402 assert(vec.length == 4); 403 assert(vec.isBig); 404 } 405 406 struct IrSmallArrayIterator 407 { 408 IrSmallArray* array; 409 IrFunction* ir; 410 411 int opApply(scope int delegate(size_t, ref IrIndex) dg) 412 { 413 return opApplyImpl!2(dg); 414 } 415 416 int opApply(scope int delegate(ref IrIndex) dg) 417 { 418 return opApplyImpl!1(dg); 419 } 420 421 int opApplyImpl(uint size, Del)(scope Del dg) 422 { 423 if (array.isBig) // external array 424 { 425 IrIndex* data = ir.getArray(array.arrayIndex); 426 uint length = array.arrayLength; 427 foreach(size_t index, ref IrIndex item; data[0..length]) 428 { 429 static if (size == 2) { 430 if (int res = dg(index, item)) return res; 431 } else { 432 if (int res = dg(item)) return res; 433 } 434 } 435 } 436 else // 0, 1, 2 437 { 438 if (!array.items[0].isDefined) return 0; // length 0 439 440 static if (size == 2) { 441 if (int res = dg(0, array.items[0])) return res; // length 1 442 if (array.items[1].isDefined) 443 { 444 if (int res = dg(1, array.items[1])) return res; // length 2 445 } 446 } else { 447 if (int res = dg(array.items[0])) return res; // length 1 448 if (array.items[1].isDefined) 449 { 450 if (int res = dg(array.items[1])) return res; // length 2 451 } 452 } 453 } 454 return 0; 455 } 456 }