1 module nxt.bit_traits; 2 3 static if (__VERSION__ >= 2083) 4 { 5 version(LDC) static assert(!__traits(compiles, { enum _ = __traits(isZeroInit, T); }), 6 "Remove checks for __traits(compiles, { enum _ = __traits(isZeroInit, T); }) now that it compiles with LDC"); 7 } 8 9 /** Get number of bits needed to represent the range (0 .. `length`-1). 10 */ 11 template bitsNeeded(size_t length) 12 { 13 // TODO optimize by removing need for a linear search 14 static if (length <= 2) { enum bitsNeeded = 1; } 15 else static if (length <= 4) { enum bitsNeeded = 2; } 16 else static if (length <= 8) { enum bitsNeeded = 3; } 17 else static if (length <= 16) { enum bitsNeeded = 4; } 18 else static if (length <= 32) { enum bitsNeeded = 5; } 19 else static if (length <= 64) { enum bitsNeeded = 6; } 20 else static if (length <= 128) { enum bitsNeeded = 7; } 21 else static if (length <= 256) { enum bitsNeeded = 8; } 22 else static if (length <= 512) { enum bitsNeeded = 9; } 23 else { static assert(0, `Too large length`); } 24 } 25 26 /** Number of bits required to store a packed instance of `T`. 27 See_Also: http://forum.dlang.org/thread/okonqhnxzqlqtxijxsfg@forum.dlang.org 28 29 TODO Extend to continuous version; use std.numeric.sumOfLog2s. Ask on 30 StackExchange Computer Science for the correct terminology. 31 32 See: http://dlang.org/phobos/std_numeric.html#.sumOfLog2s 33 34 TODO merge with `UsageOf` 35 */ 36 template packedBitSizeOf(T) 37 { 38 static if (is(T == enum)) 39 { 40 static assert(T.min != T.max, "enum T must have at least two enumerators"); 41 import core.bitop : bsr; 42 enum range = T.max - T.min; // TODO use uniqueEnumMembers.length instead? 43 enum packedBitSizeOf = range.bsr + 1; 44 } 45 // TODO 46 // else static if (isAggregate!T) 47 // { 48 // foreach (E; T.tupleof) 49 // { 50 // ....; 51 // } 52 // } 53 else 54 { 55 enum packedBitSizeOf = 8*T.sizeof; 56 } 57 } 58 59 @safe pure nothrow @nogc unittest 60 { 61 static assert(packedBitSizeOf!ubyte == 8); 62 static assert(!__traits(compiles, 63 { 64 enum E1 { x } static assert(packedBitSizeOf!E1 == 1); 65 })); 66 enum E2 { x, y } 67 static assert(packedBitSizeOf!E2 == 1); 68 enum E3 { x, y, z } 69 static assert(packedBitSizeOf!E3 == 2); 70 enum E4 { x, y, z, w } 71 static assert(packedBitSizeOf!E4 == 2); 72 enum E5 { a, b, c, d, e } 73 static assert(packedBitSizeOf!E5 == 3); 74 enum E6 { a, b, c, d, e, f } 75 static assert(packedBitSizeOf!E6 == 3); 76 enum E7 { a, b, c, d, e, f, g } 77 static assert(packedBitSizeOf!E7 == 3); 78 enum E8 { a, b, c, d, e, f, g, h } 79 static assert(packedBitSizeOf!E8 == 3); 80 enum E9 { a, b, c, d, e, f, g, h, i } 81 static assert(packedBitSizeOf!E9 == 4); 82 } 83 84 85 /+ Is the representation of `T.init` known at compile time to consist of nothing 86 + but zero bits? Padding between a struct's fields is not considered. 87 +/ 88 template isInitAllZeroBits(T) 89 { 90 static if (__traits(compiles, { enum _ = __traits(isZeroInit, T); })) 91 { 92 enum isInitAllZeroBits = __traits(isZeroInit, T); 93 // pragma(msg, "TODO: use `enum isInitAllZeroBits = __traits(isZeroInit, T);` here and in `isAllZeroBits` and remove the test of isInitAllZeroBits"); 94 } 95 else static if (T.sizeof == 0) 96 { 97 enum isInitAllZeroBits = true; 98 } 99 else 100 { 101 import std.traits : isStaticArray; 102 static if (isStaticArray!T && __traits(compiles, T.init[0])) 103 { 104 enum isInitAllZeroBits = __traits(compiles, { 105 static assert(isAllZeroBits!(typeof(T.init[0]), T.init[0])); 106 }); 107 } 108 else 109 { 110 enum isInitAllZeroBits = __traits(compiles, { 111 static assert(isAllZeroBits!(T, T.init)); 112 }); 113 } 114 } 115 } 116 117 @nogc nothrow pure @safe unittest 118 { 119 static assert(isInitAllZeroBits!int); 120 static assert(isInitAllZeroBits!(Object)); 121 static assert(isInitAllZeroBits!(void*)); 122 static assert(isInitAllZeroBits!uint); 123 static assert(isInitAllZeroBits!(uint[2])); 124 static assert(isInitAllZeroBits!(string)); 125 static assert(isInitAllZeroBits!(wstring)); 126 static assert(isInitAllZeroBits!(dstring)); 127 128 static assert(!isInitAllZeroBits!float); 129 // static assert(isInitAllZeroBits!(float[0])); 130 static assert(!isInitAllZeroBits!(float[2])); 131 132 static struct S1 133 { 134 int a; 135 } 136 static assert(isInitAllZeroBits!S1); 137 138 static struct S2 139 { 140 int a = 1; 141 } 142 static assert(!isInitAllZeroBits!S2); 143 144 static struct S3 145 { 146 S1 a; 147 int b; 148 } 149 static assert(isInitAllZeroBits!S3); 150 static assert(isInitAllZeroBits!(S3[2])); 151 152 static struct S4 153 { 154 S1 a; 155 S2 b; 156 } 157 static assert(!isInitAllZeroBits!S4); 158 159 static struct S5 160 { 161 real r = 0; 162 } 163 static assert(isInitAllZeroBits!S5); 164 165 static struct S6 166 { 167 168 } 169 static assert(isInitAllZeroBits!S6); 170 171 static struct S7 172 { 173 float[0] a; 174 } 175 // TODO static assert(isInitAllZeroBits!S7); 176 177 static class C1 178 { 179 int a = 1; 180 } 181 static assert(isInitAllZeroBits!C1); 182 183 // Ensure Tuple can be read. 184 import std.typecons : Tuple; 185 static assert(isInitAllZeroBits!(Tuple!(int, int))); 186 static assert(!isInitAllZeroBits!(Tuple!(float, float))); 187 188 // Ensure private fields of structs from other modules 189 // are taken into account. 190 import std.random : Mt19937; 191 static assert(!isInitAllZeroBits!Mt19937); 192 // Check that it works with const. 193 static assert(isInitAllZeroBits!(const(Mt19937)) == isInitAllZeroBits!Mt19937); 194 static assert(isInitAllZeroBits!(const(S5)) == isInitAllZeroBits!S5); 195 } 196 197 /+ Can the representation be determined at compile time to consist of nothing 198 + but zero bits? Padding between a struct's fields is not considered. 199 +/ 200 template isAllZeroBits(T, T value) 201 { 202 static if ((is(T == class) || is(T == typeof(null))) && // need this special case 203 value is null) // because pointer must be compared with `is` instead of `==` for `SSOString` case below 204 { 205 enum isAllZeroBits = true; 206 } 207 else static if (value == T.init && // NOTE `value is T.init` crashes compiler for `SSOString` 208 __traits(compiles, { enum _ = __traits(isZeroInit, T); })) 209 { 210 enum isAllZeroBits = __traits(isZeroInit, T); 211 } 212 else 213 { 214 // pragma(msg, "T: ", T.stringof, " value:", value); 215 import std.traits : isDynamicArray, isStaticArray; 216 static if (isDynamicArray!(T)) 217 { 218 enum isAllZeroBits = value is null && value.length is 0; 219 } 220 else static if (is(typeof(value is null))) 221 { 222 enum isAllZeroBits = value is null; 223 } 224 else static if (is(typeof(value is 0))) 225 { 226 enum isAllZeroBits = value is 0; 227 } 228 else static if (isStaticArray!(T)) 229 { 230 enum isAllZeroBits = () 231 { 232 // Use index so this works when T.length is 0. 233 static foreach (i; 0 .. T.length) 234 { 235 if (!isAllZeroBits!(typeof(value[i]), value[i])) { return false; } 236 } 237 return true; 238 }(); 239 } 240 else static if (is(T == struct) || 241 is(T == union)) 242 { 243 enum isAllZeroBits = () 244 { 245 static foreach (e; value.tupleof) 246 { 247 if (!isAllZeroBits!(typeof(e), e)) { return false; } 248 } 249 return true; 250 }(); 251 } 252 else 253 { 254 enum isAllZeroBits = false; 255 } 256 } 257 } 258 259 @nogc nothrow pure @safe unittest 260 { 261 static assert(isAllZeroBits!(int, 0)); 262 static assert(!isAllZeroBits!(int, 1)); 263 264 import std.meta : AliasSeq; 265 foreach (Float; AliasSeq!(float, double, real)) 266 { 267 assert(isAllZeroBits!(Float, 0.0)); 268 assert(!isAllZeroBits!(Float, -0.0)); 269 assert(!isAllZeroBits!(Float, Float.nan)); 270 } 271 272 static assert(isAllZeroBits!(void*, null)); 273 static assert(isAllZeroBits!(int*, null)); 274 static assert(isAllZeroBits!(Object, null)); 275 } 276 277 /+ Can the representation be determined at compile time to consist of nothing 278 but 1 bits? This is reported as $(B false) for structs with padding between 279 their fields because `opEquals` and hashing may rely on those bits being zero. 280 281 Note: 282 A bool occupies 8 bits so `isAllOneBits!(bool, true) == false` 283 284 See_Also: 285 https://forum.dlang.org/post/hn11oh$1usk$1@digitalmars.com 286 https://github.com/dlang/phobos/pull/6024 287 +/ 288 template isAllOneBits(T, T value) 289 { 290 import std.traits : isIntegral, isSomeChar, Unsigned; 291 static if (isIntegral!T || isSomeChar!T) 292 { 293 import core.bitop : popcnt; 294 static if (T.min < T(0)) 295 enum isAllOneBits = popcnt(cast(Unsigned!T) value) == T.sizeof * 8; 296 else 297 enum isAllOneBits = popcnt(value) == T.sizeof * 8; 298 } 299 else static if (__traits(isStaticArray, typeof(value))) 300 { 301 enum isAllOneBits = () 302 { 303 bool b = true; 304 // Use index so this works when T.length is 0. 305 static foreach (i; 0 .. T.length) 306 { 307 b &= isAllOneBits!(typeof(value[i]), value[i]); 308 if (b == false) return b; 309 } 310 311 return b; 312 }(); 313 } 314 else static if (is(typeof(value) == struct)) 315 { 316 enum isAllOneBits = () 317 { 318 bool b = true; 319 size_t fieldSizeSum = 0; 320 static foreach (e; value.tupleof) 321 { 322 b &= isAllOneBits!(typeof(e), e); 323 if (b == false) return b; 324 fieldSizeSum += typeof(e).sizeof; 325 } 326 // If fieldSizeSum == T.sizeof then there can be no gaps 327 // between fields. 328 return b && fieldSizeSum == T.sizeof; 329 }(); 330 } 331 else 332 { 333 enum isAllOneBits = false; 334 } 335 } 336 337 @nogc nothrow pure @safe unittest 338 { 339 static assert(isAllOneBits!(char, 0xff)); 340 static assert(isAllOneBits!(wchar, 0xffff)); 341 static assert(isAllOneBits!(byte, cast(byte) 0xff)); 342 static assert(isAllOneBits!(int, 0xffff_ffff)); 343 static assert(isAllOneBits!(char[4], [0xff, 0xff, 0xff, 0xff])); 344 345 static assert(!isAllOneBits!(bool, true)); 346 static assert(!isAllOneBits!(wchar, 0xff)); 347 static assert(!isAllOneBits!(Object, Object.init)); 348 } 349 350 /+ Can the representation be determined at compile time to consist of nothing 351 but 1 bits? This is reported as $(B false) for structs with padding between 352 their fields because `opEquals` and hashing may rely on those bits being zero. 353 354 See_Also: 355 https://forum.dlang.org/post/hn11oh$1usk$1@digitalmars.com 356 https://github.com/dlang/phobos/pull/6024 357 +/ 358 template isInitAllOneBits(T) 359 { 360 static if (__traits(isStaticArray, T) && __traits(compiles, T.init[0])) 361 enum isInitAllOneBits = __traits(compiles, { 362 static assert(isAllOneBits!(typeof(T.init[0]), T.init[0])); 363 }); 364 else 365 enum isInitAllOneBits = __traits(compiles, { 366 static assert(isAllOneBits!(T, T.init)); 367 }); 368 } 369 370 @nogc nothrow pure @safe unittest 371 { 372 static assert(isInitAllOneBits!char); 373 static assert(isInitAllOneBits!wchar); 374 static assert(!isInitAllOneBits!dchar); 375 376 static assert(isInitAllOneBits!(char[4])); 377 static assert(!isInitAllOneBits!(int[4])); 378 static assert(!isInitAllOneBits!Object); 379 380 static struct S1 381 { 382 char a; 383 char b; 384 } 385 static assert(isInitAllOneBits!S1); 386 387 static struct S2 388 { 389 char a = 1; 390 } 391 static assert(!isInitAllOneBits!S2); 392 393 static struct S3 394 { 395 S1 a; 396 char b; 397 } 398 static assert(isInitAllOneBits!S3); 399 static assert(isInitAllOneBits!(S3[2])); 400 401 static struct S4 402 { 403 S1 a; 404 S2 b; 405 } 406 static assert(!isInitAllOneBits!S4); 407 408 static struct Sshort 409 { 410 short r = cast(short)0xffff; 411 } 412 static assert(isInitAllOneBits!Sshort); 413 414 static struct Sint 415 { 416 int r = 0xffff_ffff; 417 } 418 static assert(isInitAllOneBits!Sint); 419 420 static struct Slong 421 { 422 long r = 0xffff_ffff_ffff_ffff; 423 } 424 static assert(isInitAllOneBits!Slong); 425 426 // Verify that when there is padding between fields isInitAllOneBits is false. 427 static struct S10 428 { 429 align(4) char a; 430 align(4) char b; 431 } 432 static assert(!isInitAllOneBits!S10); 433 434 static class C1 435 { 436 char c; 437 } 438 static assert(!isInitAllOneBits!C1); 439 }