1 /***************************************************************************************************
2  * 
3  * Helper functions that serve general purposes.
4  * 
5  * Authors:
6  *   $(LINK2 mailto:Marco.Leise@gmx.de, Marco Leise)
7  * 
8  * Copyright:
9  *   © 2017-2023 $(LINK2 mailto:Marco.Leise@gmx.de, Marco Leise), $(LINK2 mailto:etienne@cimons.com, Etienne Cimon)
10  * 
11  * License:
12  *   $(LINK2 https://mit-license.org/, GNU General Public License 3.0)
13  * 
14  **************************************************************************************************/
15 module fast.internal.helpers;
16 
17 import std.traits;
18 import fast.internal.sysdef;
19 
20 static void function(string) nothrow @safe logInfo;
21 static void function(string) nothrow @safe logError;
22 
23 static enum isX86 = false;
24 static enum isAMD64 = false;
25 private enum 一META一PROGRAMMING一;
26 
27 // 2.071 fixed visibility rules, so we need to roll our own staticIota.
28 static if (__VERSION__ >= 2071)
29 {
30 	import std.meta : AliasSeq;
31 
32 	template staticIota(int beg, int end)
33 	{
34 		static if (beg + 1 >= end)
35 		{
36 			static if (beg >= end)
37 			{
38 				alias staticIota = AliasSeq!();
39 			}
40 			else
41 			{
42 				alias staticIota = AliasSeq!(+beg);
43 			}
44 		}
45 		else
46 		{
47 			enum mid = beg + (end - beg) / 2;
48 			alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end));
49 		}
50 	}
51 }
52 else
53 {
54 	import std.typecons : staticIota;
55 }
56 
57 /**
58  * For any integral type, returns the unsigned type of the same bit-width.
59  */
60 template UnsignedOf(I) if (isIntegral!I)
61 {
62 	static if (isUnsigned!I)
63 		alias UnsignedOf = I;
64 	else static if (is(I == long))
65 		alias UnsignedOf = ulong;
66 	else static if (is(I == int))
67 		alias UnsignedOf = uint;
68 	else static if (is(I == short))
69 		alias UnsignedOf = ushort;
70 	else static if (is(I == byte))
71 		alias UnsignedOf = ubyte;
72 	else
73 		static assert(0, "Not implemented");
74 }
75 
76 /**
77  * Generates a mixin string for repeating code. It can be used to unroll variadic arguments.
78  * A format string is instantiated a certain number times with an incrementing parameter.
79  * The results are then concatenated using an optional joiner.
80  *
81  * Params:
82  *   length = Number of elements you want to join. It is passed into format() as an incrementing number from [0 .. count$(RPAREN).
83  *   fmt = The format string to apply on each instanciation. Use %1d$ to refer to the current index multiple times when necessary.
84  *   joiner = Optional string that will be placed between instances. It could be a space or an arithmetic operation.
85  *
86  * Returns:
87  *   The combined elements as a mixin string.
88  *
89  * See_Also:
90  *   $(LINK2 http://forum.dlang.org/thread/vqfvihyezbmwcjkmpzin@forum.dlang.org, A simple way to do compile time loop unrolling)
91  */
92 enum ctfeJoin(size_t length)(in string fmt, in string joiner = null)
93 {
94 	import std.range : iota;
95 	import std.algorithm : map;
96 
97 	// BUG: Cannot use, join(), as it "cannot access the nested function 'ctfeJoin'".
98 	string result;
99 	foreach (inst; map!(i => format(fmt, i))(iota(length)))
100 	{
101 		if (result && joiner)
102 			result ~= joiner;
103 		result ~= inst;
104 	}
105 	return result;
106 }
107 
108 enum getUDA(alias sym, T)()
109 {
110 	foreach (uda; __traits(getAttributes, sym))
111 		static if (is(typeof(uda) == T))
112 			return uda;
113 	return T.init;
114 }
115 
116 private enum 一BIT一OPERATIONS一;
117 
118 static import core.bitop;
119 
120 alias bsr = core.bitop.bsr;
121 alias bsf = core.bitop.bsf;
122 
123 /*******************************************************************************
124  * 
125  * Count leading zeroes.
126  *
127  * Params:
128  *   u = the unsigned value to scan
129  *
130  * Returns:
131  *   The number of leading zero bits before the first one bit. If `u` is `0`,
132  *   the result is undefined.
133  *
134  **************************************/
135 version (DigitalMars)
136 {
137 	@safe @nogc pure nothrow U clz(U)(U u)
138 			if (is(Unqual!U == uint) || is(Unqual!U == size_t))
139 	{
140 		pragma(inline, true);
141 		enum U max = 8 * U.sizeof - 1;
142 		return max - bsr(u);
143 	}
144 
145 	static if (isX86)
146 	{
147 		@safe @nogc pure nothrow uint clz(U)(U u) if (is(Unqual!U == ulong))
148 		{
149 			pragma(inline, true);
150 			uint hi = u >> 32;
151 			return hi ? 31 - bsr(hi) : 63 - bsr(cast(uint) u);
152 		}
153 	}
154 }
155 else version (GNU)
156 {
157 	import gcc.builtins;
158 
159 	alias clz = __builtin_clz;
160 	static if (isX86)
161 	{
162 		@safe @nogc pure nothrow uint clz(ulong u)
163 		{
164 			uint hi = u >> 32;
165 			return hi ? __builtin_clz(hi) : 32 + __builtin_clz(cast(uint) u);
166 		}
167 	}
168 	else
169 		alias clz = __builtin_clzl;
170 }
171 else version (LDC)
172 {
173 	@safe @nogc pure nothrow U clz(U)(U u)
174 			if (is(Unqual!U == uint) || is(Unqual!U == size_t))
175 	{
176 		pragma(inline, true);
177 		import ldc.intrinsics;
178 
179 		return llvm_ctlz(u, false);
180 	}
181 
182 	static if (is(size_t == uint))
183 	{
184 		@safe @nogc pure nothrow uint clz(U)(U u) if (is(Unqual!U == ulong))
185 		{
186 			pragma(inline, true);
187 			import ldc.intrinsics;
188 
189 			return cast(uint) llvm_ctlz(u, false);
190 		}
191 	}
192 }
193 static if (__VERSION__ < 2071)
194 {
195 	// < 2.071 did not have 64-bit bsr/bsf on x86.
196 	@safe @nogc pure nothrow uint bsr(U)(U u) if (is(Unqual!U == ulong))
197 	{
198 		pragma(inline, true);
199 		uint hi = u >> 32;
200 		return hi ? bsr(hi) + 32 : bsr(cast(uint) u);
201 	}
202 
203 	@safe @nogc pure nothrow uint bsf(U)(U u) if (is(Unqual!U == ulong))
204 	{
205 		pragma(inline, true);
206 		uint lo = cast(uint) u;
207 		return lo ? bsf(lo) : 32 + bsf(u >> 32);
208 	}
209 }
210 unittest
211 {
212 	assert(clz(uint(0x01234567)) == 7);
213 	assert(clz(ulong(0x0123456701234567)) == 7);
214 	assert(clz(ulong(0x0000000001234567)) == 7 + 32);
215 	assert(bsr(uint(0x01234567)) == 24);
216 	assert(bsr(ulong(0x0123456701234567)) == 24 + 32);
217 	assert(bsr(ulong(0x0000000001234567)) == 24);
218 	assert(bsf(uint(0x76543210)) == 4);
219 	assert(bsf(ulong(0x7654321076543210)) == 4);
220 	assert(bsf(ulong(0x7654321000000000)) == 4 + 32);
221 }
222 
223 private enum 一UNITTESTING一;
224 
225 // Insert a dummy main when unittesting outside of dub.
226 version (VibeCustomMain)
227 {
228 }
229 else version (unittest)
230 	void main()
231 {
232 }
233 
234 private enum 一MISCELLANEOUS一;
235 
236 pure nothrow @nogc
237 {
238 	/**
239 	 * Aligns a pointer to the closest multiple of $(D pot) (a power of two),
240 	 * which is equal to or larger than $(D value).
241 	 */
242 	T* alignPtrNext(T)(scope T* ptr, in size_t pot)
243 	in
244 	{
245 		assert(pot > 0 && pot.isPowerOf2);
246 	}
247 	body
248 	{
249 		return cast(T*)((cast(size_t) ptr + (pot - 1)) & -pot);
250 	}
251 
252 	unittest
253 	{
254 		assert(alignPtrNext(cast(void*) 65, 64) == cast(void*) 128);
255 	}
256 }
257 
258 @nogc @safe pure nothrow
259 {
260 	/// Returns whether the (positive) argument is an integral power of two.
261 	@property bool isPowerOf2(in size_t n)
262 	in
263 	{
264 		assert(n > 0);
265 	}
266 	body
267 	{
268 		return (n & n - 1) == 0;
269 	}
270 
271 	version (LDC)
272 	{
273 		import core.simd : ubyte16;
274 
275 		pragma(LDC_intrinsic, "llvm.x86.sse2.pmovmskb.128")
276 		uint moveMask(ubyte16);
277 	}
278 	else version (GNU)
279 	{
280 		import gcc.builtins;
281 
282 		alias moveMask = __builtin_ia32_pmovmskb128;
283 	}
284 
285 	template SIMDFromScalar(V, alias scalar)
286 	{
287 		// This wrapper is needed for optimal performance with LDC and
288 		// doesn't hurt GDC's inlining.
289 		V SIMDFromScalar()
290 		{
291 			enum V asVectorEnum = scalar;
292 			return asVectorEnum;
293 		}
294 	}
295 
296 }