1 /** 2 Copyright: Copyright (c) 2017-2019 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module vox.utils; 7 8 public import core.bitop : bsr, popcnt; 9 public import core.time : MonoTime, Duration, usecs, dur; 10 public import std.algorithm : min, max, swap, map; 11 public import std.conv : to; 12 public import std.exception : enforce; 13 public import std.format : formattedWrite; 14 public import std.stdio : stdout, write, writef, writeln, writefln; 15 public import std.string : format; 16 public import std.range : enumerate; 17 18 public import vox.utils.arena; 19 public import vox.utils.arenapool; 20 public import vox.utils.array; 21 public import vox.utils.arrayarena; 22 public import vox.utils.buffer; 23 public import vox.utils.har : parseHar; 24 public import vox.utils.hash; 25 public import vox.utils.mem; 26 public import vox.utils.numfmt; 27 public import vox.utils.textsink; 28 public import vox.utils.time_measure; 29 30 enum PAGE_SIZE = 4096; 31 enum ulong GiB = 1024UL*1024*1024; 32 33 string[] gatherEnumStrings(E)() 34 { 35 string[] res = new string[__traits(allMembers, E).length]; 36 foreach (i, m; __traits(allMembers, E)) 37 { 38 res[i] = __traits(getAttributes, __traits(getMember, E, m))[0]; 39 } 40 return res; 41 } 42 43 enum PrintAscii { no = false, yes = true } 44 // prints indentStr, then indentation number of spaces, then data. 45 void printHex(ubyte[] buffer, size_t lineLength, PrintAscii printAscii = PrintAscii.no, const(char)[] indentStr = null, uint indentation = 0) 46 { 47 import std.stdio : write, writef, writefln; 48 import std.range : repeat; 49 50 size_t index = 0; 51 52 static char toAscii(ubyte b) { 53 if (b < 32) return '.'; 54 if (b > 126) return '.'; 55 return cast(char)b; 56 } 57 58 if (lineLength) { 59 while (index + lineLength <= buffer.length) { 60 write(indentStr); 61 write(' '.repeat(indentation)); 62 writef("%(%02X %)", buffer[index..index+lineLength]); 63 if (printAscii) { 64 write(' '); 65 buffer[index..index+lineLength].map!toAscii.write; 66 } 67 writeln; 68 index += lineLength; 69 } 70 } 71 72 if (index < buffer.length) { 73 write(indentStr); 74 write(' '.repeat(indentation)); 75 writef("%(%02X %)", buffer[index..buffer.length]); 76 if (printAscii) { 77 foreach(_; 0..(index + lineLength - buffer.length)*3 + 1) write(' '); 78 buffer[index..buffer.length].map!toAscii.write; 79 } 80 writeln; 81 } 82 } 83 84 T divCeil(T)(T a, T b) 85 { 86 return a / b + (a % b > 0); 87 } 88 89 T nextPOT(T)(T x) 90 { 91 --x; 92 x |= x >> 1; // handle 2 bit numbers 93 x |= x >> 2; // handle 4 bit numbers 94 x |= x >> 4; // handle 8 bit numbers 95 static if (T.sizeof >= 2) x |= x >> 8; // handle 16 bit numbers 96 static if (T.sizeof >= 4) x |= x >> 16; // handle 32 bit numbers 97 static if (T.sizeof >= 8) x |= x >> 32; // handle 64 bit numbers 98 ++x; 99 100 return x; 101 } 102 103 bool isPowerOfTwo(T)(T x) 104 { 105 return (x != 0) && ((x & (~x + 1)) == x); 106 } 107 108 /// alignment is POT 109 T alignValue(T)(T value, T alignment) pure 110 { 111 assert(isPowerOfTwo(alignment), format("alignment is not power of two (%s)", alignment)); 112 return cast(T)((value + (alignment-1)) & ~(alignment-1)); 113 } 114 115 /// multiple can be NPOT 116 T roundUp(T)(T value, T multiple) pure 117 { 118 assert(multiple != 0, "multiple must not be zero"); 119 return cast(T)(((value + multiple - 1) / multiple) * multiple); 120 } 121 122 /// alignment is POT 123 T paddingSize(T)(T address, T alignment) 124 { 125 return cast(T)(alignValue(address, alignment) - address); 126 } 127 128 ulong bitmask(size_t n) { 129 if (n >= 64) return ulong.max; 130 return (1UL << n) - 1; 131 } 132 133 MonoTime currTime() { return MonoTime.currTime(); } 134 135 T[] removeInPlace(T)(T[] array, T what) 136 { 137 size_t i = 0; 138 size_t length = array.length; 139 while(i < length) 140 { 141 if (array[i] == what) 142 { 143 array[i] = array[length-1]; 144 --length; 145 } 146 ++i; 147 } 148 return assumeSafeAppend(array[0..length]); 149 } 150 151 unittest 152 { 153 assert(removeInPlace([], 1) == []); 154 assert(removeInPlace([1], 1) == []); 155 assert(removeInPlace([1], 2) == [1]); 156 assert(removeInPlace([1, 2], 2) == [1]); 157 assert(removeInPlace([2, 1], 2) == [1]); 158 } 159 160 struct FileDataSlicer 161 { 162 ubyte[] fileData; 163 size_t fileCursor = 0; 164 165 // Returns array of Ts of length 'length' stating from fileCursor offset in fileData 166 T[] getArrayOf(T)(size_t length) 167 { 168 enforce(fileData.length >= fileCursor + T.sizeof * length, format("Not enough bytes in the file")); 169 auto res = (cast(T*)(fileData.ptr + fileCursor))[0..length]; 170 fileCursor += T.sizeof * length; 171 return res; 172 } 173 174 T* getPtrTo(T)() { return getArrayOf!T(1).ptr; } 175 T parseBigEndian(T)() { 176 ubyte[T.sizeof] buf = getArrayOf!ubyte(T.sizeof); 177 return bigEndianToNative!T(buf); 178 } 179 180 void advanceToAlignment(size_t alignment) { fileCursor += paddingSize(fileCursor, alignment); } 181 } 182 183 import core.bitop : bsf, bt, bts, btr; 184 bool setBitAt(T)(T[] bitmap, size_t at) { return bts(cast(size_t*)bitmap.ptr, at) != 0; } 185 bool resetBitAt(T)(T[] bitmap, size_t at) { return btr(cast(size_t*)bitmap.ptr, at) != 0; } 186 bool getBitAt(T)(T[] bitmap, size_t at) { return bt(cast(size_t*)bitmap.ptr, at) != 0; } 187 188 // Most efficient with ulong 189 // Iterates all set bits in increasing order 190 BitsSet!T bitsSet(T)(T[] bitmap) { return BitsSet!T(bitmap); } 191 192 struct BitsSet(T) 193 { 194 T[] bitmap; 195 196 int opApply(scope int delegate(size_t) dg) 197 { 198 foreach (size_t slotIndex, T slotBits; bitmap) 199 { 200 while (slotBits != 0) 201 { 202 // Extract lowest set isolated bit 203 // 111000 -> 001000; 0 -> 0 204 T lowestSetBit = slotBits & -slotBits; 205 206 size_t lowestSetBitIndex = bsf(slotBits); 207 if (int res = dg(slotIndex * T.sizeof * 8 + lowestSetBitIndex)) return res; 208 209 // Disable lowest set isolated bit 210 // 111000 -> 110000 211 slotBits ^= lowestSetBit; 212 } 213 } 214 215 return 0; 216 } 217 }