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 }