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 * © 2015 $(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.helpers; 16 17 import std.traits; 18 19 20 /+ 21 ╔══════════════════════════════════════════════════════════════════════════════ 22 ║ ⚑ Meta programming 23 ╚══════════════════════════════════════════════════════════════════════════════ 24 +/ 25 26 version (Posix) 27 enum isPosix = true; 28 else 29 enum isPosix = false; 30 31 version (X86_64) { 32 enum isAMD64 = true; 33 enum isX86 = false; 34 } else version (X86) { 35 enum isAMD64 = false; 36 enum isX86 = true; 37 } 38 39 version (LDC) { 40 enum isLDC = true; 41 enum isGDC = false; 42 enum isDMD = false; 43 } else version (GNU) { 44 enum isLDC = false; 45 enum isGDC = true; 46 enum isDMD = false; 47 } else version (DigitalMars) { 48 enum isLDC = false; 49 enum isGDC = false; 50 enum isDMD = true; 51 } 52 53 54 static if (__VERSION__ < 2067) 55 { 56 import std.typetuple; 57 58 //Required for FieldNameTuple 59 private enum NameOf(alias T) = T.stringof; 60 61 /** 62 * Get as an expression tuple the names of the fields of a struct, class, or 63 * union. This consists of the fields that take up memory space, excluding the 64 * hidden fields like the virtual function table pointer or a context pointer 65 * for nested types. If $(D T) isn't a struct, class, or union returns an 66 * expression tuple with an empty string. 67 */ 68 template FieldNameTuple(T) 69 { 70 static if (is(T == struct) || is(T == union)) 71 alias FieldNameTuple = staticMap!(NameOf, T.tupleof[0 .. $ - isNested!T]); 72 else static if (is(T == class)) 73 alias FieldNameTuple = staticMap!(NameOf, T.tupleof); 74 else 75 alias FieldNameTuple = TypeTuple!""; 76 } 77 } 78 79 80 /** 81 * For any integral type, returns the unsigned type of the same bit-width. 82 */ 83 template UnsignedOf(I) if (isIntegral!I) 84 { 85 static if (isUnsigned!I) 86 alias UnsignedOf = I; 87 else static if (is(I == long)) 88 alias UnsignedOf = ulong; 89 else static if (is(I == int)) 90 alias UnsignedOf = uint; 91 else static if (is(I == short)) 92 alias UnsignedOf = ushort; 93 else static if (is(I == byte)) 94 alias UnsignedOf = ubyte; 95 else static assert (0, "Not implemented"); 96 } 97 98 99 version (DigitalMars) 100 { 101 enum noinline; 102 enum forceinline; 103 enum sse4; 104 } 105 else version (GNU) 106 { 107 import gcc.attribute; 108 enum noinline = gcc.attribute.attribute("noinline"); 109 enum forceinline = gcc.attribute.attribute("forceinline"); 110 enum sse4 = gcc.attribute.attribute("target", "sse4"); 111 } 112 else version (LDC) 113 { 114 import ldc.attribute; 115 enum noinline = ldc.attribute.attribute("noinline"); 116 enum forceinline = ldc.attribute.attribute("alwaysinline"); 117 enum sse4; 118 } 119 120 121 version (X86_64) 122 enum hasSSE2 = true; 123 else 124 enum hasSSE2 = false; 125 126 127 version (assert) 128 enum isRelease = false; 129 else 130 enum isRelease = true; 131 132 133 /** 134 * Generates a mixin string for repeating code. It can be used to unroll variadic arguments. 135 * A format string is instantiated a certain number times with an incrementing parameter. 136 * The results are then concatenated using an optional joiner. 137 * 138 * Params: 139 * length = Number of elements you want to join. It is passed into format() as an incrementing number from [0 .. count$(RPAREN). 140 * fmt = The format string to apply on each instanciation. Use %1d$ to refer to the current index multiple times when necessary. 141 * joiner = Optional string that will be placed between instances. It could be a space or an arithmetic operation. 142 * 143 * Returns: 144 * The combined elements as a mixin string. 145 * 146 * See_Also: 147 * $(LINK2 http://forum.dlang.org/thread/vqfvihyezbmwcjkmpzin@forum.dlang.org, A simple way to do compile time loop unrolling) 148 */ 149 enum ctfeJoin(size_t length)(in string fmt, in string joiner = null) 150 { 151 import std.range : iota; 152 import std.string : format; 153 import std.algorithm : map; 154 155 // BUG: Cannot use, join(), as it "cannot access the nested function 'ctfeJoin'". 156 string result; 157 foreach (inst; map!(i => format(fmt, i))(iota(length))) { 158 if (result && joiner) result ~= joiner; 159 result ~= inst; 160 } 161 return result; 162 } 163 164 165 enum getUDA(alias sym, T)() 166 { 167 foreach (uda; __traits(getAttributes, sym)) 168 static if (is(typeof(uda) == T)) 169 return uda; 170 return T.init; 171 } 172 173 174 /+ 175 ╔══════════════════════════════════════════════════════════════════════════════ 176 ║ ⚑ Bit operations 177 ╚══════════════════════════════════════════════════════════════════════════════ 178 +/ 179 180 /******************************************************************************* 181 * 182 * Count leading zeroes. 183 * 184 * Params: 185 * u = the unsigned value to scan 186 * 187 * Returns: 188 * The number of leading zero bits before the first one bit. If `u` is `0`, 189 * the result is undefined. 190 * 191 **************************************/ 192 version (DigitalMars) 193 { 194 pragma(inline, true) @safe @nogc pure nothrow 195 ubyte clz(U)(U u) if (is(U == uint) || is(U == ulong)) 196 { 197 import core.bitop; 198 enum max = 8 * U.sizeof - 1; 199 static if (isX86 && is(U == ulong)) 200 { 201 uint a = u >> 32; 202 return cast(ubyte) (max - (a ? 32 + bsr(a) : bsr(cast(uint) u))); 203 } 204 else return cast(ubyte) (max - bsr(u)); 205 } 206 } 207 else version (GNU) 208 { 209 import gcc.builtins; 210 alias clz = __builtin_clz; 211 version (X86_64) 212 { 213 @safe @nogc pure nothrow 214 ubyte clz(ulong u) 215 { 216 return cast(ubyte) __builtin_clzl(u); 217 } 218 } 219 else 220 { 221 @safe @nogc pure nothrow 222 ubyte clz(ulong u) 223 { 224 uint a = u >> 32; 225 return cast(ubyte) (a ? __builtin_clzl(a) : 32 + __builtin_clzl(cast(uint) u)); 226 } 227 } 228 } 229 else version (LDC) 230 { 231 @safe @nogc pure nothrow 232 ubyte clz(U)(U u) if (is(U == uint) || is(U == ulong)) 233 { 234 import ldc.intrinsics; 235 return cast(ubyte) llvm_ctlz(u, false); 236 } 237 } 238 239 240 version (X86) 241 { 242 @safe @nogc pure nothrow 243 int bsr(ulong u) 244 { 245 import core.bitop; 246 uint a = u >> 32; 247 return a ? 32 + core.bitop.bsr(a) : core.bitop.bsr(cast(uint) u); 248 } 249 250 251 @safe @nogc pure nothrow 252 int bsf(ulong u) 253 { 254 import core.bitop; 255 uint a = cast(uint) u; 256 return a ? core.bitop.bsf(a) : 32 + core.bitop.bsf(u >> 32); 257 } 258 } 259 260 261 /+ 262 ╔══════════════════════════════════════════════════════════════════════════════ 263 ║ ⚑ 264 ╚══════════════════════════════════════════════════════════════════════════════ 265 +/ 266 267 pure nothrow @nogc 268 { 269 /** 270 * Aligns a pointer to the closest multiple of $(D pot) (a power of two), 271 * which is equal to or larger than $(D value). 272 */ 273 T* alignPtrNext(T)(scope T* ptr, in size_t pot) 274 in { assert(pot > 0 && pot.isPowerOf2); } 275 body { return cast(T*) ((cast(size_t) ptr + (pot - 1)) & -pot); } 276 unittest { assert(alignPtrNext(cast(void*) 65, 64) == cast(void*) 128); } 277 } 278 279 280 @nogc @safe pure nothrow 281 { 282 /// Returns whether the (positive) argument is an integral power of two. 283 @property bool isPowerOf2(in size_t n) 284 in { assert(n > 0); } 285 body { return (n & n - 1) == 0; } 286 287 version (LDC) { 288 import core.simd; 289 pragma(LDC_intrinsic, "llvm.x86.sse2.pmovmskb.128") 290 uint moveMask(ubyte16); 291 } else version (GNU) { 292 import gcc.builtins; 293 alias moveMask = __builtin_ia32_pmovmskb128; 294 } 295 296 template SIMDFromScalar(V, alias scalar) 297 { 298 // This wrapper is needed for optimal performance with LDC and 299 // doesn't hurt GDC's inlining. 300 V SIMDFromScalar() { 301 enum V asVectorEnum = scalar; 302 return asVectorEnum; 303 } 304 } 305 306 307 template SIMDFromString(string str) if (str.length <= 16) 308 { 309 import core.simd, std.algorithm, std.range, std.string; 310 311 private enum data = chain(str.representation, 0.repeat(16 - str.length)).array; 312 313 static if (!isDMD) 314 immutable ubyte16 SIMDFromString = data; 315 else static if (isX86) 316 align(16) static __gshared ubyte[16] SIMDFromString = data; 317 else 318 static __gshared ubyte16 SIMDFromString = data; 319 } 320 321 322 version (unittest) void main() {} 323 }