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 }