1 /**
2   Miscelanious stuff.
3 
4   Copyright Chris Jones 2020.
5   Distributed under the Boost Software License, Version 1.0.
6   See accompanying file Licence.txt or copy at...
7   https://www.boost.org/LICENSE_1_0.txt
8 */
9 
10 module dg2d.misc;
11 
12 public import inteli;
13 public import std.math : sqrt, abs;
14 
15 version(LDC)
16 {
17     import ldc.intrinsics;
18 
19     alias intr_bsf = llvm_ctlz;
20     alias intr_bsr = llvm_cttz;
21     alias fabs = llvm_fabs;        // DMD fabs sucks
22 }
23 else version(DigitalMars)
24 {
25     import core.bitop;
26 
27     T intr_bsr(T)(T src, bool isZeroUndefined)
28     {
29         assert(isZeroUndefined);
30         return bsf(src); // Note: llvm_cttz corresponds to bsf in DMD not bsr
31     }
32 }
33 
34 T min(T)(T a, T b)
35 {
36     return (a < b) ? a : b;
37 }
38 
39 T max(T)(T a, T b)
40 {
41     return (a > b) ? a : b;
42 }
43 
44 T clip(T)(T x, T min, T max)
45 {
46     if (x < min) return min;
47     if (x > max) return max;
48     return x;
49 }
50 
51 void swap(T)(ref T a, ref T b)
52 {
53     T tmp = a;
54     a = b;
55     b = tmp;
56 }
57 
58 T sqr(T)(T x)
59 {
60     return x*x;
61 }
62 
63 // round x up to next multiple of q
64 
65 uint roundUpTo(uint x, uint q)
66 {
67     uint tmp = x % q;
68     return (tmp) ? x - tmp + q : x;
69 }
70 
71 // round x up to next power of 2
72 
73 uint roundUpPow2(uint x)
74 {
75     x--;
76     x |= x >> 1;
77     x |= x >> 2;
78     x |= x >> 4;
79     x |= x >> 8;
80     x |= x >> 16;
81     return x+1;
82 }
83 
84 ulong roundUpPow2(ulong x)
85 {
86     x--;
87     x |= x >> 1;
88     x |= x >> 2;
89     x |= x >> 4;
90     x |= x >> 8;
91     x |= x >> 16;
92     x |= x >> 32;
93     return x+1;
94 }
95 
96 // is power of 2
97 
98 bool isPow2(int x)
99 {
100     return ! ((x - 1) & x);
101 }
102 
103 // is float or double,
104 
105 enum bool isFloatOrDouble(T) = (is(T == float) || is(T == double));
106 
107 // is float, double or int
108 
109 enum bool isFloatDoubleInt(T) = (is(T == float) || is(T == double) || is (T == int));
110 
111 // load file into malloced memory
112 
113 ubyte[] loadFileMalloc(string filename)
114 {
115     import std.stdio;
116 
117     File file = File(filename, "r"); 
118     if (!file.isOpen) return null;
119     if (file.size > size_t.max) return null;
120     ubyte* p = dg2dMalloc!ubyte(cast(size_t) file.size);   
121     ubyte[] tmp = file.rawRead(p[0..cast(size_t)file.size]);
122     if (tmp.length != file.size)
123     {
124         dg2dFree(p);
125         return null;
126     }
127     else
128     {
129         return tmp;
130     }
131 }
132 
133 /**
134   nextSetBit, searches the bit mask for the next set bit. 
135 
136   mask  - array that holds the bits
137   start - start position
138   end   - end position
139 
140   returns : index of next set bit, or "end" if none found
141 
142   note the mask should be long enough in the given word size to hold
143   the bits, IE. If end = 65, then the uint mask should be 3 uints,
144   the ulong mask should be 2 ulongs. If end = 64, then it only
145   need be 2 uints or 1 ulong.
146 */
147 
148 int nextSetBit(ulong* mask, int start, int end)
149 {
150     assert((start >= 0) && (start < end));
151 
152     int nsb = start;
153     int idx = nsb>>6;
154     ulong bits = mask[idx] >> (nsb & 63); 
155 
156     if (bits == 0)
157     {
158         idx++;
159         int msklen = (end+63)>>6;
160         while (idx < msklen)
161         {
162             if (mask[idx] != 0)
163             {
164                 nsb = idx*64 + cast(int) intr_bsr(mask[idx],true);
165                 if (nsb > end) nsb = end;
166                 return nsb;
167             }
168             idx++;
169         }
170         return end;
171     }
172     nsb = nsb + cast(int) intr_bsr(bits,true);
173     if (nsb > end) nsb = end;
174     return nsb;
175 }
176 
177 int nextSetBit(uint* mask, int start, int end)
178 {
179     assert((start >= 0) && (start < end));
180 
181     int nsb = start;
182     int idx = nsb>>5;
183     uint bits = mask[idx] >> (nsb & 31); 
184 
185     if (bits == 0)
186     {
187         idx++;
188         int msklen = (end+31)>>5;
189         while (idx < msklen)
190         {
191             if (mask[idx] != 0)
192             {
193                 nsb = idx*32 + cast(int) intr_bsr(mask[idx],true);
194                 if (nsb > end) nsb = end;
195                 return nsb;
196             }
197             idx++;
198         }
199         return end;
200     }
201     nsb = nsb + cast(int) intr_bsr(bits,true);
202     if (nsb > end) nsb = end;
203     return nsb;
204 }
205 
206 /*
207   Arena Allocator, very fast allocation, free all memory at once. Essentialy
208   it is a linked list of memory blocks and allocation is sequential through
209   each block and on to the next. If it runs out of blocks it allocates and
210   adds a new one to the end of the linked list. Reset() resets the allocator
211   to the begining of the first block. Nothing is freed back to the C allocator
212   until the destructor is called. No init or clean up is done of the memory.
213 */
214 
215 struct ArenaAllocator(T, uint blockSize)
216 {  
217     struct EABlock
218     {
219         EABlock* next;
220         T[blockSize] items;
221     }
222 
223     EABlock* m_root;
224     EABlock* m_block;
225     uint m_pos = uint.max;
226 
227     // note: m_pos is set to uint.max if no blocks are allocated yet. This avoids
228     // having to do two conditional tests in the fast path of allocate() method.
229 
230 public:
231 
232     ~this()
233     {
234         while (m_root)
235         {
236             EABlock* tmp = m_root;
237             m_root = m_root.next;
238             dg2dFree(tmp);
239         }
240     }
241 
242     T* allocate()
243     {
244         if (m_pos < blockSize)
245         {
246             return &m_block.items[m_pos++];
247         }
248         else
249         {
250             if (m_block)
251             {
252                 if (m_block.next)
253                 {
254                     m_block = m_block.next;
255                     m_pos = 0;
256                     return &m_block.items[m_pos++];
257                 }
258                 else
259                 {
260                     void* tmp = dg2dMalloc!EABlock();
261                     if (!tmp) assert(0); // no mem abandon ship!
262                     m_block.next = cast(EABlock*) tmp;
263                     m_block = m_block.next;
264                     m_block.next = null;
265                     m_pos = 0;
266                     return &m_block.items[m_pos++];
267                 }
268             }
269             else
270             {
271                 void* tmp = dg2dMalloc!EABlock();
272                 if (!tmp) assert(0); // no mem abandon ship!
273                 m_root = cast(EABlock*) tmp;
274                 m_block = m_root;
275                 m_block.next = null;
276                 m_pos = 0;
277                 return &m_block.items[m_pos++];
278             }
279         }
280     }
281 
282     void reset()
283     {
284         m_block = m_root;
285         m_pos = (m_root) ? 0 : uint.max;
286     }
287 }
288 
289 /*
290   readCycleCounter
291 */
292 
293 version(LDC)
294 {
295     long readCycleCounter()
296     {
297         import ldc.intrinsics: readcyclecounter;
298         return readcyclecounter;
299     }
300 }
301 else version(DigitalMars)
302 {
303     long readCycleCounter()
304     {
305         long result;
306         asm nothrow @nogc pure
307         {
308             rdtscp;
309             mov dword ptr [result+0], EAX;
310             mov dword ptr [result+4], EDX;
311         }
312         return result;
313     }
314 }
315 
316 /*
317    Simple array which uses the C heap
318    Keeps track of capacity to minimize reallocations
319    Frees the memory when it is destroyed
320    Disables copying and assignment
321    can either init new items in the array, or not
322 */
323 
324 struct Array(T, bool voidInit = false)
325 {
326     @disable this(this);
327 
328     @disable void opAssign(Array other);
329 
330     ~this()
331     {
332     //    if(m_elements) free(m_elements);
333     }
334 
335     bool empty()
336     {
337         return (m_length == 0);
338     }
339 
340     void reset()
341     {
342         m_length = 0;
343     }
344 
345     size_t length() 
346     {
347         return m_length;
348     }
349 
350     void length(size_t newlen)
351     {
352         import std.algorithm.mutation : initializeAll;
353 
354         if(newlen > m_capacity) setCapacity(newlen);
355         static if (!voidInit) 
356             if (newlen > m_length) initializeAll(m_elements[m_length..newlen]);
357         m_length = newlen;
358     }
359 
360     size_t capacity()
361     {
362         return m_capacity;
363     }
364 
365     void reserve(size_t newcap)
366     {
367         setCapacity(newcap);
368     }
369 
370     ref T opIndex(size_t idx)
371     {
372         assert(idx < m_length);
373         return m_elements[idx];
374     }
375 
376     T opIndexAssign(T what, size_t idx)
377     {
378         assert(idx < m_length);
379         m_elements[idx] = what;
380         return what;
381     }
382 
383     T[] opSlice()
384     {
385         return m_elements[0..m_length];
386     }
387 
388     T[] opSlice(size_t from, size_t to)
389     {
390         assert((from < to) && (to < m_length));
391         return m_elements[from..to];
392     }
393 
394     void opSliceAssign(T value)
395     {
396        m_elements[0..m_length] = value;
397     }
398 
399     size_t opDollar()
400     {
401         return m_length;
402     }
403 
404     void append(T item)
405     {
406         length = m_length+1;
407         m_elements[m_length-1] = item;
408     }
409 
410     void append(T[] items)
411     {
412         size_t newlen = m_length+items.length;
413         setCapacity(newlen);
414         m_elements[m_length..newlen] = items[];
415     }
416 
417     T* ptr()
418     {
419         return m_elements;
420     }
421 
422 
423 private:
424 
425     void setCapacity(size_t newcap)
426     {
427         import core.stdc.stdlib : realloc;
428 
429         if (newcap <= m_length) return;
430         newcap = roundUpPow2(newcap|15);
431         if (newcap == 0) assert(0); // overflowed
432         if (newcap > (size_t.max/T.sizeof)) assert(0); // too big
433         m_data = realloc(m_data, newcap*T.sizeof + 15);
434         size_t alignmentMask = (cast(size_t)-1) - 15;
435         size_t elements = (cast(size_t)m_data + 15) & alignmentMask;
436         m_elements = cast(T*) elements;
437         if (!m_elements) assert(0); // allocate failed
438         m_capacity = newcap;
439     }
440 
441     T* m_elements;
442     size_t m_length;
443     size_t m_capacity;
444     void* m_data; // unaligned data
445 }
446 
447 /*
448   Realloc and malloc memory from c std heap
449   Adds type safety and overflow / out of mem checks
450 */
451 
452 /* some logging stuff */
453 
454 private:
455 
456 struct allocinfo { void* ptr; size_t size; }
457 
458 allocinfo[] allocations;
459 
460 void logAlloc(void* ptr, size_t size)
461 {
462     foreach(i, ref info; allocations)
463     {
464         if (info.ptr == ptr)
465         {
466             info.size = size;
467             dumpAllocInfo();
468             return;
469         }
470     }
471     allocations ~= allocinfo(ptr, size);
472     dumpAllocInfo();
473 }
474 
475 void dumpAllocInfo()
476 {
477     size_t asize = 0;
478     foreach(info; allocations) asize += info.size;
479     import std.stdio;
480     writeln("Allocated = ",asize);
481 }
482 
483 public:
484 
485 T* dg2dRealloc(T)(T* ptr, size_t length)
486 {
487 //    logAlloc(ptr, 0); // TESTING
488 
489     import core.stdc.stdlib : realloc;
490     if (length > size_t.max/T.sizeof) assert(0); // Too large
491     ptr = cast(T*) realloc(ptr, length * T.sizeof);
492     if (ptr == null) assert(0); // Alloc failed abandon ship! 
493 
494 //    logAlloc(ptr, length * T.sizeof); // TESTING
495 
496     return ptr;
497 }
498 
499 T* dg2dMalloc(T)(size_t length = 1)
500 {
501     import core.stdc.stdlib : malloc;
502     if (length > size_t.max/T.sizeof) assert(0); // Too large
503     T* ptr = cast(T*) malloc(length * T.sizeof);
504     if (ptr == null) assert(0); // Alloc failed abandon ship!
505 
506 //    logAlloc(ptr, length * T.sizeof); // TESTING
507 
508     return ptr;
509 }
510 
511 void dg2dFree(void* ptr)
512 {
513     import core.stdc.stdlib : free;
514 
515 //   logAlloc(ptr, 0); // TESTING
516 
517     free(ptr);
518 }