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 }