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