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 }