1 module mutils.container.buckets_chain; 2 3 import core.memory; 4 import std.algorithm : sort; 5 import std.conv : emplace; 6 import std.experimental.allocator; 7 import std.experimental.allocator.mallocator; 8 import std.traits; 9 10 import mutils.container.vector; 11 12 enum doNotInline = "pragma(inline,false);version(LDC)pragma(LDC_never_inline);"; 13 14 struct BitsArray(uint bitsNum) { 15 ubyte[bitsNum / 8 + (bitsNum % 8 > 0) + 0] bytes; 16 17 static uint byteNumber(uint bitNum) { 18 return bitNum / 8; 19 } 20 21 void set(uint bitNum) { 22 assert(bitNum < bitsNum); 23 uint byteNum = byteNumber(bitNum); 24 uint bitInByte = bitNum % 8; 25 bytes[byteNum] |= 1 << bitInByte; 26 27 } 28 29 void clear(uint bitNum) { 30 assert(bitNum < bitsNum); 31 uint byteNum = byteNumber(bitNum); 32 uint bitInByte = bitNum % 8; 33 bytes[byteNum] &= ~(1 << bitInByte); 34 } 35 36 bool get(uint bitNum) { 37 assert(bitNum < bitsNum); 38 uint byteNum = byteNumber(bitNum); 39 uint bitInByte = bitNum % 8; 40 return (bytes[byteNum] >> bitInByte) & 1; 41 } 42 43 uint numOfSetBits() { 44 uint num; 45 foreach (uint i; 0 .. bitsNum) { 46 num += get(i); 47 } 48 return num; 49 } 50 } 51 52 unittest { 53 alias BA = BitsArray!(13); 54 BA ba; 55 56 assert(ba.bytes.length == 2); 57 ba.set(0); 58 ba.set(1); 59 ba.set(8); 60 ba.set(9); 61 assert(ba.bytes[0] == 3); 62 assert(ba.bytes[1] == 3); 63 ba.clear(1); 64 ba.clear(9); 65 assert(ba.bytes[0] == 1); 66 assert(ba.bytes[1] == 1); 67 } 68 /** 69 * Value typed fixed size container. 70 * It is not random access container. 71 * Empty elements are determined by bitfields. 72 * Designed to be performant when iterating on all elements. If container is full simple foreach is used. 73 */ 74 struct BucketWithBits(T, uint elementsNum = 128) { 75 static assert(elementsNum % 8 == 0, "Number of elements must be multiple of 8."); 76 T[elementsNum] elements; 77 BitsArray!(elementsNum) emptyElements; 78 79 void initialize() { 80 foreach (uint i; 0 .. elementsNum) { 81 emptyElements.set(i); 82 } 83 clear(); 84 } 85 86 void clear() { 87 foreach (ref T el; this) { 88 remove(&el); 89 } 90 assert(length == 0); 91 foreach (uint i; 0 .. elementsNum) { 92 emptyElements.set(i); 93 } 94 } 95 96 void reset() { 97 clear(); 98 } 99 100 ~this() { 101 clear(); 102 } 103 104 size_t length() { 105 return elementsNum - emptyElements.numOfSetBits; 106 } 107 108 private int getEmptyElementNum() { 109 int num = -1; 110 upper: foreach (uint i, b; emptyElements.bytes) { 111 if (b > 0) { 112 foreach (uint j; 0 .. 8) { 113 if (emptyElements.get(i * 8 + j)) { 114 num = i * 8 + j; 115 emptyElements.clear(num); 116 break upper; 117 } 118 } 119 } 120 } 121 return num; 122 } 123 124 T* add() { 125 int num = getEmptyElementNum(); 126 assert(num != -1); 127 return emplace(&elements[num]); 128 } 129 130 T* add()(ref T obj) { 131 int num = getEmptyElementNum(); 132 assert(num != -1); 133 134 return emplace(&elements[num], obj); 135 } 136 137 T* add()(T obj) { 138 return add(obj); 139 } 140 141 void opOpAssign(string op)(T obj) { 142 static assert(op == "~"); 143 add(obj); 144 } 145 146 void opOpAssign(string op)(T[] obj) { 147 static assert(op == "~"); 148 foreach (o; obj) { 149 add(obj); 150 } 151 } 152 153 bool isFull() { 154 foreach (b; emptyElements.bytes) { 155 if (b != 0) { 156 return false; 157 } 158 } 159 return true; 160 } 161 162 void remove(T* obj) { 163 sizediff_t dt = cast(void*) obj - cast(void*)&elements[0]; 164 uint num = cast(uint)(dt / T.sizeof); 165 assert(num < elementsNum); 166 emptyElements.set(num); 167 168 destroy(*obj); 169 } 170 171 bool isIn(T* obj) { 172 size_t ptr = cast(size_t) obj; 173 size_t ptrBeg = cast(size_t)&elements; 174 size_t ptrEnd = cast(size_t)&elements[elementsNum - 1]; 175 if (ptr >= ptrBeg && ptr <= ptrEnd + T.sizeof) { 176 return true; 177 } 178 return false; 179 } 180 181 int opApply(Dg)(scope Dg dg) { 182 static assert(ParameterTypeTuple!Dg.length == 1 || ParameterTypeTuple!Dg.length == 2); 183 enum hasI = ParameterTypeTuple!Dg.length == 2; 184 static if (hasI) { 185 alias IType = ParameterTypeTuple!Dg[0]; 186 IType index = 0; 187 } 188 189 int result; 190 if (isFull()) { 191 foreach (int i, ref el; elements) { 192 static if (hasI) { 193 result = dg(i, el); 194 } else { 195 result = dg(el); 196 } 197 if (result) 198 break; 199 } 200 } else { 201 //the opApply is faster when this pice of code is in inner function 202 //probably because rare executing code is not inlined (less code in main execution path) 203 void byElementIteration() { 204 mixin(doNotInline); 205 upper: foreach (int k, b; emptyElements.bytes) { 206 if (b == 0) { 207 foreach (int i, ref el; elements[k * 8 .. k * 8 + 8]) { 208 static if (hasI) { 209 result = dg(index, el); 210 index++; 211 } else { 212 result = dg(el); 213 } 214 if (result) 215 break upper; 216 } 217 } else { 218 foreach (uint i; 0 .. 8) { 219 if (emptyElements.get(k * 8 + i) == false) { 220 static if (hasI) { 221 result = dg(index, elements[k * 8 + i]); 222 index++; 223 } else { 224 result = dg(elements[k * 8 + i]); 225 } 226 if (result) 227 break upper; 228 } 229 } 230 } 231 } 232 } 233 234 byElementIteration(); 235 } 236 237 return result; 238 } 239 240 struct Range { 241 BucketWithBits!(T, elementsNum)* bucket; 242 int lastElementNum = -1; 243 244 bool empty() { 245 return lastElementNum >= elementsNum; 246 } 247 248 ref T front() { 249 return bucket.elements[lastElementNum]; 250 } 251 252 void popFront() { 253 lastElementNum++; 254 while (lastElementNum < elementsNum && bucket.emptyElements.get(lastElementNum) == true) { 255 lastElementNum++; 256 } 257 } 258 } 259 260 Range getRange() { 261 Range rng; 262 rng.bucket = &this; 263 if (rng.empty) { 264 rng.popFront(); 265 } 266 return rng; 267 } 268 } 269 270 unittest { 271 alias WWWW = BucketWithBits!(long, 16); 272 WWWW bucket; 273 bucket.initialize(); 274 assert(bucket.length == 0); 275 276 long* ptr; 277 ptr = bucket.add(0); 278 ptr = bucket.add(1); 279 ptr = bucket.add(1); 280 assert(bucket.isIn(ptr)); 281 bucket.add(2); 282 bucket.add(3); 283 bucket.remove(ptr); 284 assert(bucket.length == 4); 285 foreach (int i, ref long el; bucket) { 286 assert(i == el); 287 bucket.remove(&el); 288 } 289 assert(bucket.length == 0); 290 //test one byte full 291 bucket.clear(); 292 assert(bucket.length == 0); 293 foreach (i; 0 .. 12) { 294 bucket.add(11); 295 } 296 foreach (int i, long el; bucket) { 297 assert(el == 11); 298 } 299 //test all used 300 bucket.clear(); 301 assert(bucket.length == 0); 302 foreach (i; 0 .. 16) { 303 bucket.add(15); 304 } 305 foreach (int i, long el; bucket) { 306 assert(el == 15); 307 } 308 } 309 310 unittest { 311 BucketWithBits!(long, 16) bucket; 312 bucket.initialize(); 313 314 bucket.add(100); 315 foreach (el; bucket.getRange()) { 316 assert(el == 100); 317 } 318 } 319 320 /** 321 * Not relocating container. 322 * Designed for storing a lot of objects in contiguous memory and iterating over them without performance loss. 323 * Adding and removing elements is slow (linear). 324 */ 325 struct BucketsChain(T, uint elementsInBucket = 64, bool addGCRange = hasIndirections!T) { 326 alias ElementType = T; 327 alias MyBucket = BucketWithBits!(T, elementsInBucket); 328 Vector!(MyBucket*) buckets; 329 330 @disable this(this); 331 332 void clear() { 333 foreach (b; buckets) { 334 b.clear(); 335 Mallocator.instance.dispose(b); 336 } 337 buckets.clear(); 338 } 339 340 ~this() { 341 clear(); 342 } 343 344 MyBucket* addBucket() { 345 MyBucket* b = Mallocator.instance.make!MyBucket; 346 b.initialize(); 347 static if (addGCRange) { 348 349 GC.addRange(b.elements.ptr, b.elements.length * T.sizeof); 350 } 351 buckets ~= b; 352 return b; 353 } 354 355 size_t length() { 356 size_t len; 357 foreach (b; buckets) { 358 len += b.length; 359 } 360 return len; 361 } 362 363 private MyBucket* getFreeBucket() { 364 MyBucket* bucket; 365 foreach (b; buckets) { 366 if (!b.isFull) { 367 bucket = b; 368 break; 369 } 370 } 371 if (bucket is null) { 372 bucket = addBucket(); 373 } 374 375 return bucket; 376 } 377 378 T* add() { 379 return getFreeBucket.add(); 380 } 381 382 T* add()(auto ref T obj) { 383 return getFreeBucket().add(obj); 384 } 385 386 void opOpAssign(string op)(auto ref T obj) { 387 static assert(op == "~"); 388 add(obj); 389 } 390 391 void opOpAssign(string op)(T[] obj) { 392 static assert(op == "~"); 393 foreach (o; obj) { 394 add(obj); 395 } 396 } 397 398 void remove(T* obj) { 399 foreach (b; buckets) { 400 if (b.isIn(obj)) { 401 b.remove(obj); 402 return; 403 } 404 } 405 assert(0); 406 } 407 408 int opApply(scope int delegate(ref T) dg) { 409 alias Dg = typeof(dg); 410 static assert(ParameterTypeTuple!Dg.length == 1 || ParameterTypeTuple!Dg.length == 2); 411 enum hasI = ParameterTypeTuple!Dg.length == 2; 412 static if (hasI) { 413 alias IType = ParameterTypeTuple!Dg[0]; 414 IType index = 0; 415 } 416 417 int result; 418 foreach (bucket; buckets) { 419 foreach (ref T el; *bucket) { 420 static if (hasI) { 421 result = dg(index, el); 422 index++; 423 } else { 424 result = dg(el); 425 } 426 if (result) 427 break; 428 } 429 430 } 431 return result; 432 } 433 434 static struct Range { 435 BucketsChain!(T, elementsInBucket, addGCRange)* buckets; 436 int lastBucketNum = 0; 437 int lastElementNum = -1; 438 439 @property bool empty() { 440 return lastBucketNum >= buckets.buckets.length; // && lastElementNum >= elementsInBucket; 441 } 442 443 @property ref T front() { 444 return buckets.buckets[lastBucketNum].elements[lastElementNum]; 445 } 446 447 void popFront() { 448 lastElementNum++; 449 while (lastBucketNum < buckets.buckets.length) { 450 auto bucket = buckets.buckets[lastBucketNum]; 451 if (lastElementNum >= elementsInBucket) { 452 lastBucketNum++; 453 lastElementNum = 0; 454 continue; 455 } 456 if (bucket.emptyElements.get(lastElementNum) == false) { 457 break; 458 } 459 lastElementNum++; 460 } 461 } 462 } 463 464 Range getRange() { 465 Range rng; 466 rng.buckets = &this; 467 //if(!rng.empty){ 468 rng.popFront(); 469 //} 470 return rng; 471 } 472 } 473 474 unittest { 475 BucketsChain!(int, 8) buckets; 476 //buckets.initialize(); 477 478 buckets.add(0); 479 buckets.add(1); 480 buckets.add(2); 481 buckets.add(3); 482 auto ptr = buckets.add(4); 483 buckets.add(5); 484 buckets.add(6); 485 buckets.add(7); 486 buckets.add(8); 487 buckets.add(9); 488 buckets.remove(ptr); 489 int i; 490 foreach (el; buckets.getRange()) { 491 assert(el == i); 492 if (el == 3) 493 i++; 494 i++; 495 } 496 } 497 498 unittest { 499 BucketsChain!(long, 16) vec; 500 long* ptr; 501 ptr = vec.add(1); 502 foreach (i; 0 .. 100) { 503 vec.add(2); 504 } 505 assert(ptr == &vec.buckets[0].elements[0]); 506 vec.remove(ptr); 507 ptr = vec.add(1); 508 assert(ptr == &vec.buckets[0].elements[0]); 509 vec.remove(ptr); 510 511 foreach (ref long el; vec) { 512 assert(el == 2); 513 } 514 foreach (ref long el; vec) { 515 vec.remove(&el); 516 } 517 assert(vec.length == 0); 518 } 519 520 struct BucketWithList(T, uint elementsNum = 128) { 521 union Element { 522 Element* next; 523 T obj; 524 } 525 526 Element[elementsNum] elements; 527 Element* emptyOne; 528 529 void initialize() { 530 foreach (i, ref el; elements[0 .. elementsNum - 1]) { 531 el.next = &elements[i + 1]; 532 } 533 elements[elementsNum - 1].next = null; 534 emptyOne = &elements[0]; 535 } 536 537 void reset() { 538 initialize(); 539 } 540 541 void clear() { 542 Element* next = emptyOne; 543 Vector!(Element*) emptyOnes; 544 while (next) { 545 emptyOnes ~= next; 546 next = next.next; 547 } 548 sort(emptyOnes[]); 549 size_t lastMatched = 0; 550 outer: foreach (i, ref el; elements[0 .. elementsNum - 1]) { 551 foreach (k, emptyEl; emptyOnes[lastMatched .. $]) { 552 if (&el == emptyEl) { 553 lastMatched = k + 1; 554 continue outer; 555 } 556 } 557 destroy(el.obj); 558 } 559 initialize(); 560 } 561 562 T* add() { 563 assert(emptyOne !is null); 564 Element* el = emptyOne; 565 emptyOne = el.next; 566 //T ini=T.init; 567 //el.obj=T.init; 568 /*static if(isArray!T){ 569 moveEmplaceAll(ini[], el.obj[]); 570 }else{ 571 moveEmplace(ini, el.obj); 572 }*/ 573 emplace(&el.obj); 574 return &el.obj; 575 } 576 577 T* add(T obj) { 578 assert(emptyOne !is null); 579 Element* el = emptyOne; 580 emptyOne = el.next; 581 //el.obj=obj; 582 //moveEmplace(obj, el.obj); 583 /*static if(isArray!T){ 584 moveEmplaceAll(obj[], el.obj[]); 585 }else{ 586 moveEmplace(obj, el.obj); 587 }*/ 588 emplace(&el.obj, obj); 589 return &el.obj; 590 } 591 592 void remove(T* obj) { 593 Element* el = cast(Element*) obj; 594 el.next = emptyOne; 595 emptyOne = el; 596 } 597 598 bool isIn(T* obj) { 599 size_t ptr = cast(size_t) obj; 600 size_t ptrBeg = cast(size_t)&elements[0]; 601 size_t ptrEnd = cast(size_t)&elements[elementsNum - 1]; 602 if (ptr >= ptrBeg && ptr <= ptrEnd) { 603 return true; 604 } 605 return false; 606 } 607 608 bool isFull() { 609 return emptyOne is null; 610 } 611 } 612 613 struct BucketsListChain(T, uint elementsInBucket = 64, bool addGCRange = hasIndirections!T) { 614 alias ElementType = T; 615 alias MyBucket = BucketWithList!(T, elementsInBucket); 616 Vector!(MyBucket*) buckets; 617 618 @disable this(this); 619 620 void initialize() { 621 } 622 623 void clear() { 624 foreach (b; buckets) { 625 b.clear(); 626 Mallocator.instance.dispose(b); 627 } 628 buckets.clear(); 629 } 630 631 MyBucket* addBucket() { 632 MyBucket* b = Mallocator.instance.make!MyBucket; 633 b.initialize(); 634 static if (addGCRange) { 635 636 GC.addRange(b.elements.ptr, b.elements.length * T.sizeof); 637 } 638 buckets ~= b; 639 return b; 640 } 641 642 private MyBucket* getFreeBucket() { 643 MyBucket* bucket; 644 foreach (b; buckets) { 645 if (!b.isFull) { 646 bucket = b; 647 break; 648 } 649 } 650 if (bucket is null) { 651 bucket = addBucket(); 652 } 653 654 return bucket; 655 } 656 657 T* add() { 658 return getFreeBucket.add(); 659 } 660 661 static if (isImplicitlyConvertible!(T, T)) { // @disable this(this) - don't support this type of add 662 T* add(T obj) { 663 return getFreeBucket().add(obj); 664 } 665 } 666 667 void remove(T* obj) { 668 foreach (b; buckets) { 669 if (b.isIn(obj)) { 670 b.remove(obj); 671 return; 672 } 673 } 674 assert(0); 675 } 676 }