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 static if (__traits(isStaticArray, T) && __traits(compiles, T.init[0])) 102 { 103 enum isInitAllZeroBits = __traits(compiles, { 104 static assert(isAllZeroBits!(typeof(T.init[0]), T.init[0])); 105 }); 106 } 107 else 108 { 109 enum isInitAllZeroBits = __traits(compiles, { 110 static assert(isAllZeroBits!(T, T.init)); 111 }); 112 } 113 } 114 } 115 116 @nogc nothrow pure @safe unittest 117 { 118 static assert(isInitAllZeroBits!int); 119 static assert(isInitAllZeroBits!(Object)); 120 static assert(isInitAllZeroBits!(void*)); 121 static assert(isInitAllZeroBits!uint); 122 static assert(isInitAllZeroBits!(uint[2])); 123 static assert(isInitAllZeroBits!(string)); 124 static assert(isInitAllZeroBits!(wstring)); 125 static assert(isInitAllZeroBits!(dstring)); 126 127 static assert(!isInitAllZeroBits!float); 128 // static assert(isInitAllZeroBits!(float[0])); 129 static assert(!isInitAllZeroBits!(float[2])); 130 131 static struct S1 132 { 133 int a; 134 } 135 static assert(isInitAllZeroBits!S1); 136 137 static struct S2 138 { 139 int a = 1; 140 } 141 static assert(!isInitAllZeroBits!S2); 142 143 static struct S3 144 { 145 S1 a; 146 int b; 147 } 148 static assert(isInitAllZeroBits!S3); 149 static assert(isInitAllZeroBits!(S3[2])); 150 151 static struct S4 152 { 153 S1 a; 154 S2 b; 155 } 156 static assert(!isInitAllZeroBits!S4); 157 158 static struct S5 159 { 160 real r = 0; 161 } 162 static assert(isInitAllZeroBits!S5); 163 164 static struct S6 165 { 166 167 } 168 static assert(isInitAllZeroBits!S6); 169 170 static struct S7 171 { 172 float[0] a; 173 } 174 // TODO: static assert(isInitAllZeroBits!S7); 175 176 static class C1 177 { 178 int a = 1; 179 } 180 static assert(isInitAllZeroBits!C1); 181 182 // Ensure Tuple can be read. 183 import std.typecons : Tuple; 184 static assert(isInitAllZeroBits!(Tuple!(int, int))); 185 static assert(!isInitAllZeroBits!(Tuple!(float, float))); 186 187 // Ensure private fields of structs from other modules 188 // are taken into account. 189 import std.random : Mt19937; 190 static assert(!isInitAllZeroBits!Mt19937); 191 // Check that it works with const. 192 static assert(isInitAllZeroBits!(const(Mt19937)) == isInitAllZeroBits!Mt19937); 193 static assert(isInitAllZeroBits!(const(S5)) == isInitAllZeroBits!S5); 194 } 195 196 /+ Can the representation be determined at compile time to consist of nothing 197 + but zero bits? Padding between a struct's fields is not considered. 198 +/ 199 template isAllZeroBits(T, T value) 200 { 201 static if ((is(T == class) || is(T == typeof(null))) && // need this special case 202 value is null) // because pointer must be compared with `is` instead of `==` for `SSOString` case below 203 { 204 enum isAllZeroBits = true; 205 } 206 else static if (value == T.init && // NOTE `value is T.init` crashes compiler for `SSOString` 207 __traits(compiles, { enum _ = __traits(isZeroInit, T); })) 208 { 209 enum isAllZeroBits = __traits(isZeroInit, T); 210 } 211 else 212 { 213 // pragma(msg, "T: ", T.stringof, " value:", value); 214 import std.traits : isDynamicArray; 215 static if (isDynamicArray!(T)) 216 { 217 enum isAllZeroBits = value is null && value.length is 0; 218 } 219 else static if (is(typeof(value is null))) 220 { 221 enum isAllZeroBits = value is null; 222 } 223 else static if (is(typeof(value is 0))) 224 { 225 enum isAllZeroBits = value is 0; 226 } 227 else static if (__traits(isStaticArray, T)) 228 { 229 enum isAllZeroBits = () 230 { 231 // Use index so this works when T.length is 0. 232 static foreach (i; 0 .. T.length) 233 { 234 if (!isAllZeroBits!(typeof(value[i]), value[i])) 235 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)) 248 return false; 249 } 250 return true; 251 }(); 252 } 253 else 254 enum isAllZeroBits = false; 255 } 256 } 257 258 @nogc nothrow pure @safe unittest 259 { 260 static assert(isAllZeroBits!(int, 0)); 261 static assert(!isAllZeroBits!(int, 1)); 262 263 import std.meta : AliasSeq; 264 foreach (Float; AliasSeq!(float, double, real)) 265 { 266 assert(isAllZeroBits!(Float, 0.0)); 267 assert(!isAllZeroBits!(Float, -0.0)); 268 assert(!isAllZeroBits!(Float, Float.nan)); 269 } 270 271 static assert(isAllZeroBits!(void*, null)); 272 static assert(isAllZeroBits!(int*, null)); 273 static assert(isAllZeroBits!(Object, null)); 274 } 275 276 /+ Can the representation be determined at compile time to consist of nothing 277 but 1 bits? This is reported as $(B false) for structs with padding between 278 their fields because `opEquals` and hashing may rely on those bits being zero. 279 280 Note: 281 A bool occupies 8 bits so `isAllOneBits!(bool, true) == false` 282 283 See_Also: 284 https://forum.dlang.org/post/hn11oh$1usk$1@digitalmars.com 285 https://github.com/dlang/phobos/pull/6024 286 +/ 287 template isAllOneBits(T, T value) 288 { 289 import std.traits : isIntegral, isSomeChar, Unsigned; 290 static if (isIntegral!T || isSomeChar!T) 291 { 292 import core.bitop : popcnt; 293 static if (T.min < T(0)) 294 enum isAllOneBits = popcnt(cast(Unsigned!T) value) == T.sizeof * 8; 295 else 296 enum isAllOneBits = popcnt(value) == T.sizeof * 8; 297 } 298 else static if (__traits(isStaticArray, typeof(value))) 299 { 300 enum isAllOneBits = () 301 { 302 bool b = true; 303 // Use index so this works when T.length is 0. 304 static foreach (i; 0 .. T.length) 305 { 306 b &= isAllOneBits!(typeof(value[i]), value[i]); 307 if (b == false) 308 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 alias v = value.tupleof; 321 static foreach (const i, e; v) 322 { 323 b &= isAllOneBits!(typeof(e), v[i]); 324 if (b == false) 325 return b; 326 fieldSizeSum += typeof(e).sizeof; 327 } 328 // If fieldSizeSum == T.sizeof then there can be no gaps 329 // between fields. 330 return b && fieldSizeSum == T.sizeof; 331 }(); 332 } 333 else 334 { 335 enum isAllOneBits = false; 336 } 337 } 338 339 @nogc nothrow pure @safe unittest 340 { 341 static assert(isAllOneBits!(char, 0xff)); 342 static assert(isAllOneBits!(wchar, 0xffff)); 343 static assert(isAllOneBits!(byte, cast(byte) 0xff)); 344 static assert(isAllOneBits!(int, 0xffff_ffff)); 345 static assert(isAllOneBits!(char[4], [0xff, 0xff, 0xff, 0xff])); 346 347 static assert(!isAllOneBits!(bool, true)); 348 static assert(!isAllOneBits!(wchar, 0xff)); 349 static assert(!isAllOneBits!(Object, Object.init)); 350 351 static struct S1 352 { 353 char a; 354 char b; 355 } 356 static assert(isAllOneBits!(S1, S1.init)); 357 } 358 359 /+ Can the representation be determined at compile time to consist of nothing 360 but 1 bits? This is reported as $(B false) for structs with padding between 361 their fields because `opEquals` and hashing may rely on those bits being zero. 362 363 See_Also: 364 https://forum.dlang.org/post/hn11oh$1usk$1@digitalmars.com 365 https://github.com/dlang/phobos/pull/6024 366 +/ 367 template isInitAllOneBits(T) 368 { 369 static if (__traits(isStaticArray, T) && __traits(compiles, T.init[0])) // TODO: avoid traits compiles here 370 enum isInitAllOneBits = __traits(compiles, { // TODO: avoid traits compiles here 371 static assert(isAllOneBits!(typeof(T.init[0]), T.init[0])); 372 }); 373 else 374 enum isInitAllOneBits = __traits(compiles, { // TODO: avoid traits compiles here 375 static assert(isAllOneBits!(T, T.init)); 376 }); 377 } 378 379 /// 380 @nogc nothrow pure @safe unittest 381 { 382 static assert(isInitAllOneBits!char); 383 static assert(isInitAllOneBits!wchar); 384 static assert(!isInitAllOneBits!dchar); 385 386 static assert(isInitAllOneBits!(char[4])); 387 static assert(!isInitAllOneBits!(int[4])); 388 static assert(!isInitAllOneBits!Object); 389 390 static struct S1 391 { 392 char a; 393 char b; 394 } 395 static assert(isInitAllOneBits!S1); 396 397 static struct S2 398 { 399 char a = 1; 400 } 401 static assert(!isInitAllOneBits!S2); 402 403 static struct S3 404 { 405 S1 a; 406 char b; 407 } 408 static assert(isInitAllOneBits!S3); 409 static assert(isInitAllOneBits!(S3[2])); 410 411 static struct S4 412 { 413 S1 a; 414 S2 b; 415 } 416 static assert(!isInitAllOneBits!S4); 417 418 static struct Sshort 419 { 420 short r = cast(short)0xffff; 421 } 422 static assert(isInitAllOneBits!Sshort); 423 424 static struct Sint 425 { 426 int r = 0xffff_ffff; 427 } 428 static assert(isInitAllOneBits!Sint); 429 430 static struct Slong 431 { 432 long r = 0xffff_ffff_ffff_ffff; 433 } 434 static assert(isInitAllOneBits!Slong); 435 436 // Verify that when there is padding between fields isInitAllOneBits is false. 437 static struct S10 438 { 439 align(4) char a; 440 align(4) char b; 441 } 442 static assert(!isInitAllOneBits!S10); 443 444 static class C1 445 { 446 char c; 447 } 448 static assert(!isInitAllOneBits!C1); 449 }