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