1 /** 2 Checked integer arithmetic operations, functions, and types with improved handling of errors and corner cases compared 3 to the basic integral types. 4 5 $(B Note:) Normally this module should not be imported directly. Instead, import one of 6 $(LINK2 ./throws.html, `checkedint.throws`), $(LINK2 ./asserts.html, `checkedint.asserts`), or 7 $(LINK2 ./noex.html, `checkedint.noex`), depending on which error signalling policy you want to use. (See below.) 8 9 Copyright: Copyright Thomas Stuart Bockman 2015 10 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 11 Authors: Thomas Stuart Bockman 12 13 $(BIG $(B Problems solved by `checkedint`)) $(BR) 14 As in many other programming languages (C, C++, Java, etc.) D's basic integral types (such as `int` or `ulong`) are 15 surprisingly difficult to use correctly in the general case, due to variuos departures from the behaviour of ideal 16 mathematical integers: 17 $(UL 18 $(LI Overflow silently wraps around: `assert(uint.max + 1 == 0);`) 19 $(LI Mixed signed/unsigned comparisons often give the wrong result: `assert(-1 > 1u);`) 20 $(LI Mixed signed/unsigned arithmetic operations can also give the wrong result.) 21 $(LI Integer division by zero crashes the program with a mis-named and uncatchable `Floating Point Exception` 22 (FPE).) 23 $(LI `int.min / -1` and `int.min % -1` may also crash with an FPE, even though the latter should simply yield `0`.) 24 $(LI If `x` is any integer value, and `y` is any negative integer value, `x ^^ y` will crash with an FPE.) 25 $(LI No bounds checking is done when casting from one integer type to another.) 26 $(LI The result of the bitshift operations (`<<`, `>>`, `>>>`) is formally undefined if the shift size is less than 27 zero or greater than `(8 * N.sizeof) - 1`.) 28 ) 29 The `checkedint` package offers solutions to all of these issues and more. 30 31 $(BIG $(B `SafeInt` versus `SmartInt`)) $(BR) 32 Two different approaches are available: 33 $(UL 34 $(LI $(LINK2 ./package.html#SmartInt, `SmartInt`) and $(LINK2 ./package.html#smartOp, `smartOp`) strive to actually 35 give the mathematically correct answer whenever possible, rather than just signaling an error.) 36 $(LI $(LINK2 ./package.html#SafeInt, `SafeInt`) and $(LINK2 ./package.html#safeOp, `safeOp`) strive to match the 37 behaviour of the basic integral types exactly, $(B except) that where the behaviour of the basic type is 38 wrong, or very unintuitive, an error is signaled instead.) 39 ) 40 There is no meaningful performance difference between `SafeInt` and `SmartInt`. For general use, choose `SmartInt` to 41 simplify your code and maximize the range of inputs it accepts. 42 43 `SafeInt` is intended mainly as a debugging tool, to help identify problems in code that must also work correctly with 44 the basic integral types. The $(LINK2 ./package.html#DebugInt, `DebugInt`) `template` `alias` makes it simple to use 45 of `SafeInt` in debug builds, and raw basic types in release builds. 46 47 $(TABLE 48 $(TR $(TD) $(TH `int` (basic type)) $(TH `SafeInt!int`) $(TH `SmartInt!int`)) 49 $(TR $(TH `int.max + 1`) $(TD `0`) $(TD `raise(IntFlag.over)`) $(TD `raise(IntFlag.over)`)) 50 $(TR $(TH `-1 > 1u`) $(TD `true`) $(TD compile-time error) $(TD `false`)) 51 $(TR $(TH `-1 - 2u`) $(TD `4294967293`) $(TD compile-time error) $(TD `-3`)) 52 $(TR $(TH `1 / 0`) $(TD crash by FPE) $(TD `raise(IntFlag.div0)`) $(TD `raise(IntFlag.div0)`)) 53 $(TR $(TH `int.min % -1`) $(TD crash by FPE) $(TD `raise(IntFlag.posOver)`) $(TD `0`)) 54 $(TR $(TH `-1 ^^ -7`) $(TD crash by FPE) $(TD `raise(IntFlag.undef)`) $(TD `-1`)) 55 $(TR $(TH `cast(uint)-1`) $(TD `4294967295`) $(TD compile-time error) $(TD `raise(IntFlag.negOver)`)) 56 $(TR $(TH `-1 >> 100`) $(TD undefined) $(TD `raise(IntFlag.undef)`) $(TD `-1`)) 57 ) 58 59 $(BIG $(B Error Signaling)) $(BR) 60 Some types of problem are signaled by a compile-time error, others at runtime. Runtime signaling is done through 61 $(LINK2 ./flags.html, `checkedint.flags`). Three different runtime signalling policies are available: 62 $(UL 63 $(LI With `IntFlagPolicy.throws`, a $(LINK2 ./flags.html#CheckedIntException, `CheckedIntException`) is thrown. 64 These are normal exceptions; not FPEs. As such, they can be caught and include a stack trace.) 65 $(LI With `IntFlagPolicy.asserts`, an assertion failure will be triggered. This policy is compatible with 66 `pure nothrow @nogc` code, but will crash the program in the event of a runtime integer math error.) 67 $(LI Alternatively, `IntFlagPolicy.noex` can be selected so that a thread-local flag is set when an operation fails. 68 This allows `checkedint` to be used from `nothrow` and `@nogc` (but not `pure`) code without crashing the 69 program, but requires the API user to manually insert checks of `IntFlags.local`.) 70 ) 71 In normal code, there is no performance penalty for allowing `checkedint` to `throw`. Doing so is highly recommended 72 because this makes it easier to use correctly, and yields more precise error messages when something goes wrong. 73 74 $(BIG $(B Generic Code)) $(BR) 75 The $(LINK2 ./traits.html, `checkedint.traits`) module provides `checkedint`-aware versions of various numerical type 76 traits from `std.traits`, such as `Signed`, `isSigned` and `isIntegral`. This allows writing generic algorithms that 77 work with any of `SmartInt`, `SafeInt`, and the built-in numeric types such as `uint` and `long`. 78 79 Also of note is the $(LINK2 ./package.html#idx, `idx()`) function, which concisely and safely casts from any integral type 80 (built-in, `SmartInt`, or `SafeInt`) to either `size_t` or `ptrdiff_t` for easy array indexing. 81 82 $(BIG $(B Performance)) $(BR) 83 Replacing all basic integer types with `SmartInt` or `SafeInt` will slow down exectuion somewhat. How much depends on 84 many factors, but for most code following a few simple rules should keep the penalty low: 85 $(OL 86 $(LI Build with $(B $(RED `--inline`)) and $(B `-O`) (DMD) or $(B `-O3`) (GDC and LDC). This by itself can improve 87 the performance of `checkedint` by around $(B 1,000%).) 88 $(LI With GDC or LDC, the performance hit in code that is bottlenecked by integer math will probably be between 30% 89 and 100%. The performance hit may be considerably larger with DMD, due to the weakness of the inliner.) 90 $(LI `checkedint` can't slow down code where it's not used! If you really need more speed, try switching to 91 $(LINK2 ./package.html#DebugInt, `DebugInt`) for the hottest code in your program (like inner loops) before 92 giving up on `checkedint` entirely.) 93 ) 94 The above guidelines should be more than sufficient for most programs. But, here are some micro-optimization tips as 95 well, if you need them: 96 $(UL 97 $(LI Always use $(LINK2 ./package.html#smartOp.mulPow2, `mulPow2()`), 98 $(LINK2 ./package.html#smartOp.divPow2, `divPow2()`), and $(LINK2 ./package.html#smartOp.modPow2, `modPow2()`) 99 whenever they can naturally express your intent - they're faster than a regular `/`, `%`, or `pow()`.) 100 $(LI Unsigned types are a little bit faster than signed types, assuming negative values aren't needed.) 101 $(LI Although they are perfectly safe with `checkedint`, mixed signed/unsigned operations are a little bit slower 102 than same-signedness ones.) 103 $(LI The assignment operators (`++` or `+=`, for example) should never be slower than the equivalent two operation 104 sequence, and are sometimes a little bit faster.) 105 ) 106 **/ 107 module checkedint; 108 import checkedint.flags; 109 110 import future.bitop, core.checkedint, std.algorithm, std.format, future.traits0, std.typecons; 111 static import std.math; 112 static if (__VERSION__ >= 2068) 113 { 114 version(GNU) { static assert(false); } 115 import std.meta; 116 } 117 else 118 { 119 import std.typetuple; 120 private alias AliasSeq = TypeTuple; 121 } 122 123 /+pragma(inline, true) 124 {+/ 125 // smart ///////////////////////////////////////////////// 126 /** 127 Wrapper for any basic integral type `N` that uses the checked operations from 128 $(LINK2 ./package.html#smartOp, `smartOp`) and bounds checks assignments with 129 $(LINK2 ./package.html#to, `checkedint.to()`). 130 131 $(UL 132 $(LI `policy` controls the error signalling policy (see `checkedint.flags`).) 133 $(LI `bitOps` may be set to `No.bitOps` if desired, to turn bitwise operations on this type into a 134 compile-time error.) 135 ) 136 **/ 137 struct SmartInt(N, IntFlagPolicy _policy, Flag!"bitOps" bitOps = Yes.bitOps) 138 if (isIntegral!N && isUnqual!N) 139 { 140 /// The error signalling policy used by this `SmartInt` type. 141 enum IntFlagPolicy policy = _policy; 142 143 static if (bitOps) 144 { 145 /** 146 The basic integral value of this `SmartInt`. Accessing this directly may be useful for: 147 $(UL 148 $(LI Intentionally doing modular (unchecked) arithmetic, or) 149 $(LI Interacting with APIs that are not `checkedint` aware.) 150 ) 151 **/ 152 N bscal; 153 /// 154 unittest 155 { 156 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 157 158 SmartInt!uint n; 159 static assert(is(typeof(n.bscal) == uint)); 160 161 n = 7; 162 assert(n.bscal == 7); 163 164 n.bscal -= 8; 165 assert(n == uint.max); 166 } 167 168 /// Get a view of this `SmartInt` that allows bitwise operations. 169 @property ref inout(SmartInt!(N, policy, Yes.bitOps)) bits() /+return+/ inout pure @safe nothrow @nogc 170 { 171 return this; 172 } 173 /// 174 unittest 175 { 176 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 177 178 SmartInt!(int, No.bitOps) n = 1; 179 static assert(!__traits(compiles, n << 2)); 180 assert(n.bits << 2 == 4); 181 } 182 } 183 else 184 { 185 @property ref inout(N) bscal() /+return+/ inout pure @safe nothrow @nogc 186 { 187 return bits.bscal; 188 } 189 SmartInt!(N, policy, Yes.bitOps) bits; 190 } 191 192 static if (__VERSION__ >= 2067) 193 { 194 version(GNU) { static assert(false); } 195 196 /// The most negative possible value of this `SmartInt` type. 197 enum SmartInt!(N, policy, bitOps) min = typeof(this)(trueMin!N); 198 /// 199 unittest 200 { 201 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 202 203 assert(SmartInt!(int).min == int.min); 204 assert(SmartInt!(uint).min == uint.min); 205 } 206 207 /// The most positive possible value of this `SmartInt` type. 208 enum SmartInt!(N, policy, bitOps) max = typeof(this)(trueMax!N); 209 /// 210 unittest 211 { 212 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws; 213 214 assert(SmartInt!(int).max == int.max); 215 assert(SmartInt!(uint).max == uint.max); 216 } 217 } 218 else 219 { 220 static @property auto min() pure @safe nothrow @nogc 221 { 222 return typeof(this)(trueMin!N); 223 } 224 static @property auto max() pure @safe nothrow @nogc 225 { 226 return typeof(this)(trueMax!N); 227 } 228 } 229 230 // Construction, assignment, and casting ///////////////////////////////////////////////// 231 /** 232 Assign the value of `that` to this `SmartInt` instance. 233 234 $(LINK2 ./package.html#to, `checkedint.to()`) is used to verify `that >= N.min && that <= N.max`. If not, an 235 $(LINK2 ./flags.html, `IntFlag`) will be raised. 236 **/ 237 this(M)(const M that) @safe 238 if (isCheckedInt!M || isScalarType!M) 239 { 240 this.bscal = to!(N, policy)(that); 241 } 242 /// ditto 243 ref typeof(this) opAssign(M)(const M that) @safe 244 if (isCheckedInt!M || isScalarType!M) 245 { 246 this.bscal = to!(N, policy)(that); 247 return this; 248 } 249 /// 250 unittest 251 { 252 import checkedint.noex : SmartInt; // use IntFlagPolicy.noex 253 254 // Any basic scalar or checkedint *type* is accepted... 255 SmartInt!int n = 0; 256 n = cast(ulong)0; 257 n = cast(dchar)0; 258 n = cast(byte)0; 259 n = cast(real)0; 260 assert(!IntFlags.local); 261 262 // ...but not any *value*. 263 n = uint.max; 264 n = long.min; 265 n = real.nan; 266 assert(IntFlags.local.clear() == (IntFlag.posOver | IntFlag.negOver | IntFlag.undef)); 267 } 268 269 /** 270 Convert this value to floating-point. This always succeeds, although some loss of precision may 271 occur if M.sizeof <= N.sizeof. 272 **/ 273 M opCast(M)() const pure @safe nothrow @nogc 274 if (isFloatingPoint!M) 275 { 276 return cast(M)bscal; 277 } 278 /// 279 unittest 280 { 281 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 282 283 SmartInt!int n = 92; 284 auto f = cast(double)n; 285 static assert(is(typeof(f) == double)); 286 assert(f == 92.0); 287 } 288 289 /// `this != 0` 290 M opCast(M)() const pure @safe nothrow @nogc 291 if (is(M == bool)) 292 { 293 return bscal != 0; 294 } 295 /// 296 unittest 297 { 298 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 299 300 SmartInt!int n = -315; 301 assert( cast(bool)n); 302 303 n = 0; 304 assert(!cast(bool)n); 305 } 306 307 /** 308 Convert this value to type `M` using $(LINK2 ./package.html#to, `checkedint.to()`) for bounds checking. An 309 $(LINK2 ./flags.html, `IntFlag`) will be raised if `M` cannot represent the current value of this `SmartInt`. 310 **/ 311 M opCast(M)() const @safe 312 if (isCheckedInt!M || isIntegral!M || isSomeChar!M) 313 { 314 return to!(M, policy)(bscal); 315 } 316 /// 317 unittest 318 { 319 import checkedint.noex : SmartInt; // use IntFlagPolicy.noex 320 321 SmartInt!ulong n = 52; 322 auto a = cast(int)n; 323 static assert(is(typeof(a) == int)); 324 assert(!IntFlags.local); 325 assert(a == 52); 326 327 auto m = SmartInt!long(-1).mulPow2(n); 328 auto b = cast(wchar)m; 329 static assert(is(typeof(b) == wchar)); 330 assert(IntFlags.local.clear() == IntFlag.negOver); 331 } 332 333 /** 334 Convert this value to a type suitable for indexing an array: 335 $(UL 336 $(LI If `N` is signed, a `ptrdiff_t` is returned.) 337 $(LI If `N` is unsigned, a `size_t` is returned.) 338 ) 339 $(LINK2 ./package.html#to, `checkedint.to()`) is used for bounds checking. 340 **/ 341 @property Select!(isSigned!N, ptrdiff_t, size_t) idx() const @safe 342 { 343 return to!(typeof(return), policy)(bscal); 344 } 345 /// 346 unittest 347 { 348 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 349 350 char[3] arr = ['a', 'b', 'c']; 351 SmartInt!long n = 1; 352 353 // On 32-bit, `long` cannot be used directly for array indexing, 354 static if (size_t.sizeof < long.sizeof) 355 static assert(!__traits(compiles, arr[n])); 356 // but idx can be used to concisely and safely cast to ptrdiff_t: 357 assert(arr[n.idx] == 'b'); 358 359 // The conversion is bounds checked: 360 static if (size_t.sizeof < long.sizeof) 361 { 362 n = long.min; 363 try 364 { 365 arr[n.idx] = '?'; 366 } 367 catch (CheckedIntException e) 368 { 369 assert(e.intFlags == IntFlag.negOver); 370 } 371 } 372 } 373 374 /// Get a simple hashcode for this value. 375 size_t toHash() const pure @safe nothrow @nogc 376 { 377 static if (N.sizeof > size_t.sizeof) 378 { 379 static assert(N.sizeof == (2 * size_t.sizeof)); 380 return cast(size_t)bscal ^ cast(size_t)(bscal >>> 32); 381 } 382 else 383 return cast(size_t)bscal; 384 } 385 386 /// Get a `string` representation of this value. 387 string toString() const @safe 388 { 389 return to!(string, IntFlagPolicy.noex)(bscal); 390 } 391 /// ditto 392 void toString(Writer, Char = char)(Writer sink, FormatSpec!Char fmt = (FormatSpec!Char).init) const 393 { 394 formatValue(sink, bscal, fmt); 395 } 396 /// 397 unittest 398 { 399 import checkedint.throws : smartInt; // use IntFlagPolicy.throws 400 assert(smartInt(-753).toString() == "-753"); 401 } 402 403 // Comparison ///////////////////////////////////////////////// 404 /// Returns `true` if this value is mathematically precisely equal to `right`. 405 bool opEquals(M)(const M right) const pure @safe nothrow @nogc 406 if (isCheckedInt!M || isScalarType!M) 407 { 408 return smartOp!(policy).cmp!"=="(this.bscal, right.bscal); 409 } 410 /** 411 Perform a mathematically correct comparison to `right`. 412 413 Returns: $(UL 414 $(LI `-1` if this value is less than `right`.) 415 $(LI ` 0` if this value is precisely equal to `right`.) 416 $(LI ` 1` if this value is greater than `right`.) 417 $(LI `float.nan` if `right` is a floating-point `nan` value.) 418 ) 419 **/ 420 auto opCmp(M)(const M right) const pure @safe nothrow @nogc 421 if (isFloatingPoint!M) 422 { 423 return 424 (bscal < right)? -1 : 425 (bscal > right)? 1 : 426 (bscal == right)? 0 : float.nan; 427 } 428 /// ditto 429 int opCmp(M)(const M right) const pure @safe nothrow @nogc 430 if (isCheckedInt!M || isScalarType!M) 431 { 432 return smartOp!(policy).cmp(this.bscal, right.bscal); 433 } 434 435 // Unary ///////////////////////////////////////////////// 436 /// See $(LINK2 ./package.html#smartOp, `smartOp`). 437 typeof(this) opUnary(string op)() const pure @safe nothrow @nogc 438 if (op == "~") 439 { 440 static assert(bitOps, 441 "Bitwise operations are disabled."); 442 443 return typeof(return)(smartOp!(policy).unary!op(bscal)); 444 } 445 /// ditto 446 SmartInt!(Signed!N, policy, bitOps) opUnary(string op)() const @safe 447 if (op == "+" || op == "-") 448 { 449 return typeof(return)(smartOp!(policy).unary!op(bscal)); 450 } 451 /// ditto 452 ref typeof(this) opUnary(string op)() /+return+/ @safe 453 if (op.among!("++", "--")) 454 { 455 smartOp!(policy).unary!op(bscal); 456 return this; 457 } 458 459 /// ditto 460 SmartInt!(Unsigned!N, policy, bitOps) abs() const pure @safe nothrow @nogc 461 { 462 return typeof(return)(smartOp!(policy).abs(bscal)); 463 } 464 465 /// Count the number of set bits using `core.bitop.popcnt()`. 466 SmartInt!(int, policy, bitOps) popcnt()() const pure @safe nothrow @nogc 467 { 468 static assert(bitOps, "Bitwise operations are disabled."); 469 470 import future.bitop : stdPC = popcnt; 471 return typeof(return)(stdPC(bscal)); 472 } 473 474 /// See $(LINK2 ./package.html#smartOp, `smartOp`). 475 SmartInt!(ubyte, policy, bitOps) bsf()() const @safe 476 { 477 static assert(bitOps, "Bitwise operations are disabled."); 478 479 return typeof(return)(smartOp!(policy).bsf(bscal)); 480 } 481 /// ditto 482 SmartInt!(ubyte, policy, bitOps) bsr()() const @safe 483 { 484 static assert(bitOps, "Bitwise operations are disabled. Consider using ilogb() instead?"); 485 486 return typeof(return)(smartOp!(policy).bsr(bscal)); 487 } 488 489 /// ditto 490 SmartInt!(ubyte, policy, bitOps) ilogb() const @safe 491 { 492 return typeof(return)(smartOp!(policy).ilogb(bscal)); 493 } 494 495 // Binary ///////////////////////////////////////////////// 496 /// ditto 497 auto opBinaryRight(string op, M)(const M left) const pure @safe nothrow @nogc 498 if (isFloatingPoint!M) 499 { 500 return smartOp!(policy).binary!op(left, bscal); 501 } 502 /// ditto 503 auto opBinary(string op, M)(const M right) const pure @safe nothrow @nogc 504 if (isFloatingPoint!M) 505 { 506 return smartOp!(policy).binary!op(bscal, right); 507 } 508 /// ditto 509 auto opBinaryRight(string op, M)(const M left) const @safe 510 if (isSafeInt!M || isFixedPoint!M) 511 { 512 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 513 enum mixBitOps = bitOps && hasBitOps!M; 514 static assert(mixBitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 515 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 516 517 const wret = smartOp!(mixPolicy).binary!op(left.bscal, this.bscal); 518 return SmartInt!(typeof(wret), mixPolicy, mixBitOps)(wret); 519 } 520 /// ditto 521 auto opBinary(string op, M)(const M right) const @safe 522 if (isCheckedInt!M || isFixedPoint!M) 523 { 524 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 525 enum mixBitOps = bitOps && hasBitOps!M; 526 static assert(mixBitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 527 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 528 529 const wret = smartOp!(mixPolicy).binary!op(this.bscal, right.bscal); 530 return SmartInt!(typeof(wret), mixPolicy, mixBitOps)(wret); 531 } 532 /// ditto 533 ref typeof(this) opOpAssign(string op, M)(const M right) /+return+/ @safe 534 if (isCheckedInt!M || isFixedPoint!M) 535 { 536 static assert((bitOps && hasBitOps!M) || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 537 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 538 539 smartOp!(.max(policy, intFlagPolicyOf!M)).binary!(op ~ "=")(this.bscal, right.bscal); 540 return this; 541 } 542 543 /// ditto 544 auto mulPow2(M)(const M exp) const pure @safe nothrow @nogc 545 if (isFloatingPoint!M) 546 { 547 return smartOp!(policy).mulPow2(bscal, exp); 548 } 549 /// ditto 550 auto mulPow2(M)(const M exp) const @safe 551 if (isCheckedInt!M || isFixedPoint!M) 552 { 553 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 554 const wret = smartOp!(mixPolicy).mulPow2(this.bscal, exp.bscal); 555 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 556 } 557 /// ditto 558 auto divPow2(M)(const M exp) const pure @safe nothrow @nogc 559 if (isFloatingPoint!M) 560 { 561 return smartOp!(policy).divPow2(bscal, exp); 562 } 563 /// ditto 564 auto divPow2(M)(const M exp) const @safe 565 if (isCheckedInt!M || isFixedPoint!M) 566 { 567 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 568 const wret = smartOp!(mixPolicy).divPow2(this.bscal, exp.bscal); 569 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 570 } 571 /// ditto 572 auto modPow2(M)(const M exp) const pure @safe nothrow @nogc 573 if (isFloatingPoint!M) 574 { 575 return smartOp!(policy).modPow2(bscal, exp); 576 } 577 /// ditto 578 auto modPow2(M)(const M exp) const @safe 579 if (isCheckedInt!M || isFixedPoint!M) 580 { 581 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 582 const wret = smartOp!(mixPolicy).modPow2(this.bscal, exp.bscal); 583 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 584 } 585 586 /// Raise `this` to the `exp` power using `std.math.pow()`. 587 auto pow(M)(const M exp) const pure @safe nothrow @nogc 588 if (isFloatingPoint!M) 589 { 590 return std.math.pow(bscal, exp); 591 } 592 /// See $(LINK2 ./package.html#smartOp, `smartOp`). 593 auto pow(M)(const M exp) const @safe 594 if (isCheckedInt!M || isFixedPoint!M) 595 { 596 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 597 const wret = smartOp!(mixPolicy).pow(this.bscal, exp.bscal); 598 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 599 } 600 } 601 /// ditto 602 template SmartInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps) 603 if ((isIntegral!N && !isUnqual!N) || isCheckedInt!N) 604 { 605 alias SmartInt = SmartInt!(BasicScalar!N, policy, bitOps); 606 } 607 /// 608 unittest 609 { 610 // Mixing standard signed and unsigned types is dangerous, but... 611 int ba = -1; 612 uint bb = 0; 613 assert(ba > bb); 614 615 auto bc = ba + bb; 616 assert(is(typeof(bc) == uint)); 617 assert(bc == 4294967295u); 618 619 // ...with SmartInt, mixed signed/unsigned operations "just work": 620 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 621 622 SmartInt!int ma = -1; 623 SmartInt!uint mb = 0; 624 assert(ma < mb); 625 626 auto mc = ma + mb; 627 assert(is(typeof(mc) == SmartInt!int)); 628 assert(mc != 4294967295u); 629 assert(mc == -1); 630 } 631 /// 632 unittest 633 { 634 // When IntFlagPolicy.throws is used, failed SmartInt operations will throw a CheckedIntException. 635 import checkedint.throws : SmartInt; 636 637 SmartInt!uint ma = 1; 638 SmartInt!uint mb = 0; 639 640 bool overflow = false; 641 try 642 { 643 SmartInt!uint mc = mb - ma; 644 assert(false); 645 } 646 catch (CheckedIntException e) 647 { 648 assert(e.intFlags == IntFlag.negOver); 649 overflow = true; 650 } 651 assert(overflow); 652 653 bool div0 = false; 654 try 655 { 656 // With standard integers, this would crash the program with an unrecoverable FPE... 657 SmartInt!uint mc = ma / mb; 658 assert(false); 659 } 660 catch (CheckedIntException e) 661 { 662 // ...but with SmartInt, it just throws a normal Exception. 663 assert(e.intFlags == IntFlag.div0); 664 div0 = true; 665 } 666 assert(div0); 667 } 668 /// 669 unittest 670 { 671 // When IntFlagPolicy.noex is used, failed SmartInt operations set one or more bits in IntFlags.local. 672 import checkedint.noex : SmartInt; 673 674 SmartInt!uint ma = 1; 675 SmartInt!uint mb = 0; 676 SmartInt!uint mc; 677 678 mc = mb - ma; 679 assert(IntFlags.local == IntFlag.negOver); 680 681 // With standard integers, this would crash the program with an unrecoverable FPE... 682 mc = ma / mb; 683 // ...but with SmartInt, it just sets a bit in IntFlags.local. 684 assert(IntFlags.local & IntFlag.div0); 685 686 // Each flag will remain set until cleared: 687 assert(IntFlags.local.clear() == (IntFlag.negOver | IntFlag.div0)); 688 assert(!IntFlags.local); 689 } 690 691 private template SmartInt(N, IntFlagPolicy policy, bool bitOps) 692 if (isIntegral!N) 693 { 694 alias SmartInt = SmartInt!( 695 Unqual!N, 696 policy, 697 cast(Flag!"bitOps")bitOps); 698 } 699 700 /// Get the value of `num` as a `SmartInt!N`. The integral type `N` can be infered from the argument. 701 SmartInt!(N, policy, bitOps) smartInt(IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps, N)(N num) @safe 702 if (isIntegral!N || isCheckedInt!N) 703 { 704 return typeof(return)(num.bscal); 705 } 706 /// 707 unittest 708 { 709 import checkedint.throws : smartInt, SmartInt; // use IntFlagPolicy.throws 710 711 auto a = smartInt(55uL); 712 static assert(is(typeof(a) == SmartInt!ulong)); 713 assert(a == 55); 714 } 715 716 /** 717 Implements various integer math operations with error checking. 718 719 `smartOp` strives to give the mathematically correct result, with integer-style rounding, for all inputs. Only 720 if the correct result is undefined or not representable by the return type is an error signalled, using 721 `checkedint.flags`. 722 723 The error-signalling policy may be selected using the `policy` template parameter. 724 **/ 725 template smartOp(IntFlagPolicy policy) 726 { 727 // NOTE: ddoc only scans the first branch of a static if 728 static if (policy == IntFlagPolicy.none) 729 { 730 // No need to redundantly instantiate members which don't depend on `policy`. 731 732 private void cmpTypeCheck(N, M)() pure nothrow @nogc 733 { 734 static assert(isBoolean!N == isBoolean!M, 735 "The intent of a direct comparison of " ~ 736 N.stringof ~ " with " ~ M.stringof ~ 737 " is unclear. Add an explicit cast." 738 ); 739 } 740 741 /** 742 Compare `left` and `right` using `op`. 743 $(UL 744 $(LI Unlike the standard integer comparison operator, this function correctly handles negative 745 values in signed/unsigned comparisons.) 746 $(LI Like the standard operator, comparisons involving any floating-point `nan` value always return 747 `false`.) 748 ) $(BR) 749 Direct comparisons between boolean values and numeric ones are forbidden. Make your intention explicit: 750 $(UL 751 $(LI `numeric == cast(N)boolean`) 752 $(LI `(numeric != 0) == boolean`) 753 ) 754 **/ 755 bool cmp(string op, N, M)(const N left, const M right) pure @safe nothrow @nogc 756 if (isScalarType!N && isScalarType!M) 757 { 758 cmpTypeCheck!(N, M)(); 759 760 static if (isSigned!N != isSigned!M) 761 { 762 static if (isSigned!N) 763 { 764 if (left < 0) 765 return mixin("-1 " ~ op ~ " 0"); 766 } 767 else 768 { 769 if (right < 0) 770 return mixin("0 " ~ op ~ " -1"); 771 } 772 } 773 774 return mixin("left " ~ op ~ " right"); 775 } 776 /// 777 unittest 778 { 779 import checkedint.noex : smartOp; // smartOp.cmp() never throws 780 781 assert(uint.max == -1); 782 assert( smartOp.cmp!"!="(uint.max, -1)); 783 assert(-3156 > 300u); 784 assert( smartOp.cmp!"<"(-3156, 300u)); 785 786 assert(!smartOp.cmp!"<"(1, real.nan)); 787 assert(!smartOp.cmp!"<"(real.nan, 1)); 788 } 789 790 /** 791 Defines a total order on all basic scalar values, using the same rules as `std.math.cmp()`. 792 793 $(UL 794 $(LI Mixed signed/unsigned comparisons return the mathematically correct result.) 795 $(LI If neither `left` nor `right` is floating-point, this function is faster than 796 `std.math.cmp()`.) 797 $(LI If either `left` or `right` $(I is) floating-point, this function forwards to 798 `std.math.cmp()`.) 799 ) $(BR) 800 Direct comparisons between boolean values and numeric ones are forbidden. Make your intention explicit: 801 $(UL 802 $(LI `numeric == cast(N)boolean`) 803 $(LI `(numeric != 0) == boolean`) 804 ) 805 **/ 806 int cmp(N, M)(const N left, const M right) pure @safe nothrow @nogc 807 if (isScalarType!N && isScalarType!M) 808 { 809 cmpTypeCheck!(N, M)(); 810 811 static if (isFloatingPoint!N || isFloatingPoint!M) 812 { 813 import future.math : stdCmp = cmp; 814 return stdCmp(left, right); 815 } 816 else 817 { 818 static if (isSigned!N != isSigned!M) 819 { 820 static if (isSigned!N) 821 { 822 if (left < 0) 823 return -1; 824 } 825 else 826 { 827 if (right < 0) 828 return 1; 829 } 830 } 831 832 return (left < right)? -1 : (right < left); 833 } 834 } 835 /// 836 unittest 837 { 838 import checkedint.noex : smartOp; // smartOp.cmp() never throws 839 840 assert(smartOp.cmp(325.0, 325u) == 0); 841 assert(smartOp.cmp(uint.max, -1) == 1); 842 assert(smartOp.cmp(-3156, 300u) == -1); 843 } 844 845 /// Get the absolute value of `num`. Because the return type is always unsigned, overflow is not possible. 846 Unsigned!N abs(N)(const N num) pure @safe nothrow @nogc 847 if (isIntegral!N) 848 { 849 static if (!isSigned!N) 850 return num; 851 else 852 return cast(typeof(return))(num < 0? 853 -num : // -num doesn't need to be checked for overflow 854 num); 855 } 856 /// ditto 857 IntFromChar!N abs(N)(const N num) pure @safe nothrow @nogc 858 if (isSomeChar!N) 859 { 860 return num; 861 } 862 /// 863 unittest 864 { 865 import checkedint.noex : smartOp; // smartOp.abs() never throws 866 867 assert(smartOp.abs(int.min) == std.math.pow(2.0, 31)); 868 assert(smartOp.abs(-25) == 25u); 869 assert(smartOp.abs(745u) == 745u); 870 } 871 872 private template Result(N, string op, M) 873 if (isNumeric!N && isNumeric!M) 874 { 875 private: 876 enum reqFloat = isFloatingPoint!N || isFloatingPoint!M; 877 enum precN = precision!N, precM = precision!M; 878 enum precStd = reqFloat? precision!float : precision!uint; 879 enum smallSub = (op == "-") && precN < precision!int && precM < precision!int; 880 881 enum reqSign = reqFloat || 882 (op.among!("+", "-", "*" , "/") && (isSigned!N || isSigned!M || smallSub)) || 883 (op.among!("%", "^^", "<<", ">>", ">>>") && isSigned!N) || 884 (op.among!("&", "|", "^") && (isSigned!N && isSigned!M)); 885 886 enum reqPrec = reqFloat? max(precStd, precN, precM) : 887 op.among!("+", "-", "*")? max(precStd, precN, precM) - 1 : 888 op == "/"? (isSigned!M? max(precStd, precN) - 1 : precN) : 889 op == "%"? min(precision!N, precision!M) : 890 op == "^^"? max(precStd - 1, precN) : 891 op.among!("<<", ">>", ">>>")? precN : 892 /+op.among!("&", "|", "^")?+/ max(precN, precM); 893 894 public: 895 alias Result = Select!(reqFloat, 896 Select!(reqPrec <= double.mant_dig || double.mant_dig >= real.mant_dig, 897 Select!(reqPrec <= float.mant_dig, float, double), 898 real), 899 Select!(reqSign, 900 Select!(reqPrec <= 15, 901 Select!(reqPrec <= 7, byte, short), 902 Select!(reqPrec <= 31, int, long)), 903 Select!(reqPrec <= 16, 904 Select!(reqPrec <= 8, ubyte, ushort), 905 Select!(reqPrec <= 32, uint, ulong)))); 906 } 907 private template Result(N, string op, M) 908 if (isScalarType!N && isScalarType!M && 909 (!isNumeric!N || !isNumeric!M)) 910 { 911 alias Result = Result!(NumFromScal!N, op, NumFromScal!M); 912 } 913 } 914 else 915 { 916 alias cmp = smartOp!(IntFlagPolicy.none).cmp; 917 alias abs = smartOp!(IntFlagPolicy.none).abs; 918 private alias Result = smartOp!(IntFlagPolicy.none).Result; 919 } 920 921 /** 922 Perform the unary (single-argument) integer operation specified by `op`. 923 924 Key differences from the standard unary operators: 925 $(UL 926 $(LI `-` and `+` always return a signed value.) 927 $(LI `-` is checked for overflow, because `-int.min` is greater than `int.max`.) 928 $(LI `++` and `--` are checked for overflow.) 929 ) $(BR) 930 Note that like the standard operators, `++` and `--` take the operand by `ref` and overwrite its value with 931 the result. 932 **/ 933 N unary(string op, N)(const N num) pure @safe nothrow @nogc 934 if (isIntegral!N && op == "~") 935 { 936 return ~num; 937 } 938 /// ditto 939 IntFromChar!N unary(string op, N)(const N num) pure @safe nothrow @nogc 940 if (isSomeChar!N && op == "~") 941 { 942 return ~num; 943 } 944 /// ditto 945 Signed!(Promoted!N) unary(string op, N)(const N num) @safe 946 if (isFixedPoint!N && op.among!("-", "+")) 947 { 948 alias R = typeof(return); 949 alias UR = Unsigned!R; 950 951 static if (op == "-") 952 { 953 static if (isSigned!N) 954 { 955 if (num < -trueMax!R) 956 IntFlag.posOver.raise!policy(); 957 } 958 else 959 { 960 if (num > cast(UR)trueMin!R) 961 IntFlag.negOver.raise!policy(); 962 } 963 964 return -cast(R)num; 965 } 966 else 967 { 968 static if (!isSigned!N) 969 { 970 if (num > trueMax!R) 971 IntFlag.posOver.raise!policy(); 972 } 973 974 return num; 975 } 976 } 977 /// ditto 978 /+ref+/ N unary(string op, N)(/+return+/ ref N num) @safe 979 if (isIntegral!N && op.among!("++", "--")) 980 { 981 static if (op == "++") 982 { 983 if (num >= trueMax!N) 984 IntFlag.posOver.raise!policy(); 985 986 return ++num; 987 } 988 else static if (op == "--") 989 { 990 if (num <= trueMin!N) 991 IntFlag.negOver.raise!policy(); 992 993 return --num; 994 } 995 else 996 static assert(false); 997 } 998 /// 999 unittest 1000 { 1001 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1002 1003 assert(smartOp.unary!"~"(0u) == uint.max); 1004 1005 auto a = smartOp.unary!"-"(20uL); 1006 static assert(is(typeof(a) == long)); 1007 assert(a == -20); 1008 1009 auto b = smartOp.unary!"+"(uint.max); 1010 static assert(is(typeof(b) == int)); 1011 assert(IntFlags.local.clear() == IntFlag.posOver); 1012 1013 uint c = 1u; 1014 assert(smartOp.unary!"--"(c) == 0u); 1015 assert(c == 0u); 1016 smartOp.unary!"--"(c); 1017 assert(IntFlags.local.clear() == IntFlag.negOver); 1018 1019 int d = 7; 1020 assert(smartOp.unary!"++"(d) == 8); 1021 assert(d == 8); 1022 } 1023 1024 /// `core.bitop.bsf` without the undefined behaviour. `smartOp.bsf(0)` will raise `IntFlag.undef`. 1025 ubyte bsf(N)(const N num) @safe 1026 if (isFixedPoint!N) 1027 { 1028 return cast(ubyte) bsfImpl!policy(num); 1029 } 1030 /// 1031 unittest 1032 { 1033 import checkedint.noex : smartOp; 1034 1035 assert(smartOp.bsf(20) == 2); 1036 1037 smartOp.bsf(0); 1038 assert(IntFlags.local.clear() == IntFlag.undef); 1039 } 1040 1041 /// `core.bitop.bsr` without the undefined behaviour. `smartOp.bsr(0)` will raise `IntFlag.undef`. 1042 ubyte bsr(N)(const N num) @safe 1043 if (isFixedPoint!N) 1044 { 1045 return cast(ubyte) bsrImpl!policy(num); 1046 } 1047 /// 1048 unittest 1049 { 1050 import checkedint.noex : smartOp; 1051 1052 assert(smartOp.bsr( 20) == 4); 1053 assert(smartOp.bsr(-20) == 31); 1054 1055 smartOp.bsr(0); 1056 assert(IntFlags.local.clear() == IntFlag.undef); 1057 } 1058 1059 /** 1060 Get the base 2 logarithm of `abs(num)`, rounded down to the nearest integer. 1061 1062 `smartOp.ilogb(0)` will raise `IntFlag.undef`. 1063 **/ 1064 ubyte ilogb(N)(const N num) @safe 1065 if (isFixedPoint!N) 1066 { 1067 return cast(ubyte) bsrImpl!policy(abs(num)); 1068 } 1069 /// 1070 unittest 1071 { 1072 import checkedint.noex : smartOp; 1073 1074 assert(smartOp.ilogb(20) == 4); 1075 assert(smartOp.ilogb(-20) == 4); 1076 1077 smartOp.ilogb(0); 1078 assert(IntFlags.local.clear() == IntFlag.undef); 1079 } 1080 1081 private auto binaryImpl(string op, N, M)(const N left, const M right) @safe 1082 if (isIntegral!N && isIntegral!M) 1083 { 1084 enum wop = (op[$-1] == '=')? op[0 .. $-1] : op; 1085 alias 1086 UN = Unsigned!N, 1087 UM = Unsigned!M, 1088 W = Result!(N, wop, M), 1089 R = Select!(wop == op, W, N); 1090 1091 static if (wop.among!("+", "-", "*")) 1092 { 1093 enum safePrec = (wop == "*")? 1094 precision!N + precision!M : 1095 max(precision!N, precision!M) + 1; 1096 enum safeR = precision!R >= safePrec; 1097 1098 static if (safeR) 1099 return mixin("cast(R)left " ~ wop ~ " cast(R)right"); 1100 else 1101 { 1102 enum safeW = precision!W >= safePrec; 1103 enum matched = (isSigned!N == isSigned!M); 1104 enum oX = staticIndexOf!(wop, "+", "-", "*") << 1; 1105 alias cops = AliasSeq!(addu, adds, subu, subs, mulu, muls); 1106 1107 static if (safeW || matched || wop == "*") 1108 { 1109 bool over; 1110 static if (safeW) 1111 const wR = mixin("cast(W)left " ~ wop ~ " cast(W)right"); 1112 else 1113 { 1114 static if (matched) 1115 { 1116 alias cop = cops[oX + isSigned!W]; 1117 const wR = cop(left, right, over); 1118 } 1119 else 1120 { 1121 // integer multiplication is commutative 1122 static if (isSigned!N) 1123 W sa = left, ub = right; 1124 else 1125 W ub = left, sa = right; 1126 1127 static if (isSigned!R) 1128 { 1129 W wR = muls(sa, ub, over); 1130 if (ub < 0) 1131 over = (sa != 0) && (ub != trueMin!W || sa != -1); 1132 } 1133 else 1134 { 1135 over = (sa < 0) && (ub != 0); 1136 const wR = mulu(sa, ub, over); 1137 } 1138 } 1139 } 1140 1141 alias WR = typeof(wR); 1142 static if (isSigned!WR && trueMin!WR < trueMin!R) 1143 { 1144 if (wR < trueMin!R) 1145 over = true; 1146 } 1147 static if (trueMax!WR > trueMax!R) 1148 { 1149 if (wR > trueMax!R) 1150 over = true; 1151 } 1152 } 1153 else 1154 { 1155 alias UW = Unsigned!W; 1156 alias WR = Select!(isSigned!R, W, UW); 1157 alias cop = cops[oX]; 1158 1159 bool hiBit = false; 1160 const wR = cast(WR) cop(cast(UW)left, cast(UW)right, hiBit); 1161 const bool wSign = (Select!(isSigned!N, left, right) < 0) ^ hiBit; 1162 1163 static if (isSigned!WR) 1164 { 1165 static if (trueMax!WR > trueMax!R) 1166 { 1167 const over = (wR < 0)? 1168 !wSign || (wR < trueMin!R) : 1169 wSign || (wR > trueMax!R); 1170 } 1171 else 1172 const over = (wR < 0) != wSign; 1173 } 1174 else 1175 { 1176 static if (trueMax!WR > trueMax!R) 1177 const over = wSign || (wR > trueMax!R); 1178 else 1179 alias over = wSign; 1180 } 1181 } 1182 1183 if (over) 1184 IntFlag.over.raise!policy(); 1185 return cast(R) wR; 1186 } 1187 } 1188 else static if (wop == "/") 1189 { 1190 static if (!isSigned!N && !isSigned!M) 1191 { 1192 R ret = void; 1193 if (right == 0) 1194 { 1195 IntFlag.div0.raise!policy(); 1196 ret = 0; 1197 } 1198 else 1199 ret = cast(R)(left / right); 1200 1201 return ret; 1202 } 1203 else 1204 { 1205 alias P = Select!(precision!N <= 32 && precision!M <= 32, uint, ulong); 1206 1207 IntFlag flag; 1208 R ret = void; 1209 if (right == 0) 1210 { 1211 flag = IntFlag.div0; 1212 ret = 0; 1213 } 1214 else 1215 { 1216 static if (isSigned!N && isSigned!M) 1217 { 1218 if (left == trueMin!R && right == -1) 1219 { 1220 flag = IntFlag.posOver; 1221 ret = 0; 1222 } 1223 else 1224 ret = cast(R)(left / right); 1225 } 1226 else 1227 { 1228 alias UR = Unsigned!R; 1229 1230 P wL = void; 1231 P wG = void; 1232 static if (isSigned!N) 1233 { 1234 const negR = left < 0; 1235 alias side = left; 1236 alias wS = wL; 1237 wG = cast(P)right; 1238 } 1239 else 1240 { 1241 const negR = right < 0; 1242 wL = cast(P)left; 1243 alias side = right; 1244 alias wS = wG; 1245 } 1246 1247 if (negR) 1248 { 1249 wS = -cast(P)side; 1250 const P wR = wL / wG; 1251 1252 if (wR > cast(UR)trueMin!R) 1253 flag = IntFlag.negOver; 1254 ret = -cast(R)wR; 1255 } 1256 else 1257 { 1258 wS = cast(P)side; 1259 const P wR = wL / wG; 1260 1261 if (wR > cast(UR)trueMax!R) 1262 flag = IntFlag.posOver; 1263 ret = cast(R)wR; 1264 } 1265 } 1266 } 1267 1268 if (!flag.isNull) 1269 flag.raise!policy(); 1270 return ret; 1271 } 1272 } 1273 else static if (wop == "%") 1274 { 1275 R ret = void; 1276 static if (isSigned!M) 1277 const wG = cast(UM)((right < 0)? -right : right); 1278 else 1279 const wG = right; 1280 1281 if (wG <= trueMax!N) 1282 { 1283 if (wG) 1284 ret = cast(R)(left % cast(N)wG); 1285 else 1286 { 1287 IntFlag.div0.raise!policy(); 1288 ret = 0; 1289 } 1290 } 1291 else 1292 { 1293 static if (isSigned!N) 1294 { 1295 ret = (wG != cast(UN)trueMin!N || left != trueMin!N)? 1296 cast(R)left : 1297 cast(R)0; 1298 } 1299 else 1300 ret = cast(R)left; 1301 } 1302 1303 return ret; 1304 } 1305 else static if (wop.among!("<<", ">>", ">>>")) 1306 { 1307 const negG = right < 0; 1308 const shR = (wop == "<<")? 1309 negG : 1310 !negG; 1311 1312 R ret = void; 1313 static if (wop == ">>>") 1314 const wL = cast(UN)left; 1315 else 1316 alias wL = left; 1317 const absG = negG? 1318 -cast(UM)right : 1319 cast(UM)right; 1320 1321 enum maxSh = precision!UN - 1; 1322 if (absG <= maxSh) 1323 { 1324 const wG = cast(int)absG; 1325 ret = cast(R)(shR? 1326 wL >> wG : 1327 wL << wG); 1328 } 1329 else 1330 { 1331 ret = cast(R)((isSigned!N && (wop != ">>>") && shR)? 1332 (wL >> maxSh) : 1333 0); 1334 } 1335 1336 return ret; 1337 } 1338 else static if (wop.among!("&", "|", "^")) 1339 return cast(R)mixin("left " ~ wop ~ " right"); 1340 else 1341 static assert(false); 1342 } 1343 1344 /** 1345 Perform the binary (two-argument) integer operation specified by `op`. 1346 1347 Key differences from the standard binary operators: 1348 $(UL 1349 $(LI `+`, `-`, `*`, `/`, and `%` return a signed type if the result could be negative, unless $(B both) 1350 inputs are unsigned.) 1351 $(LI `+`, `-`, `*`, and `/` are checked for overflow.) 1352 $(LI `/` and `%` are checked for divide-by-zero, and will never generate an FPE.) 1353 $(LI `<<`, `>>`, and `>>>` are well-defined for all possible values of `right`. Large shifts return the 1354 same result as shifting by `1` `right` times in a row. (But, much faster because no actual loop is 1355 used.)) 1356 ) $(BR) 1357 Note also: 1358 $(UL 1359 $(LI The shift operators are $(B not) checked for overflow and should not be used for 1360 multiplication, division, or exponentiation. Instead, use `mulPow2()` and `divPow2()`, which internally 1361 use the bitshifts for speed, but check for overflow and correctly handle negative values.) 1362 $(LI Likewise, `modPow2()` should be used for remainders instead of `&`.) 1363 $(LI `^^` and `^^=` will remain disabled in favour of `pow` until DMD issues 15288 and 15412 are fixed.) 1364 ) $(BR) 1365 Like the standard equiavlents, the assignment operators (`+=`, `-=`, `*=`, etc.) take `left` by `ref` and will 1366 overwrite it with the result of the operation. 1367 **/ 1368 auto binary(string op, N, M)(const N left, const M right) @safe 1369 if (isFixedPoint!N && isFixedPoint!M && 1370 op.among!("+", "-", "*", "/", "%", "^^", "<<", ">>", ">>>", "&", "|", "^")) 1371 { 1372 static assert(op != "^^", 1373 "pow() should be used instead of operator ^^ because of issue 15288."); 1374 1375 return binaryImpl!(op, NumFromScal!N, NumFromScal!M)(left, right); 1376 } 1377 /// ditto 1378 /+ref+/ N binary(string op, N, M)(/+return+/ ref N left, const M right) @safe 1379 if (isIntegral!N && isFixedPoint!M && (op[$ - 1] == '=')) 1380 { 1381 static assert(op != "^^=", 1382 "pow() should be used instead of operator ^^= because of issue 15412."); 1383 1384 left = binaryImpl!(op, NumFromScal!N, NumFromScal!M)(left, right); 1385 return left; 1386 } 1387 /// 1388 unittest 1389 { 1390 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1391 1392 ulong a = 18_446_744_073_709_551_615uL; 1393 long b = -6_744_073_709_551_615L; 1394 auto c = smartOp.binary!"+"(a, b); 1395 static assert(isSigned!(typeof(c))); 1396 assert(IntFlags.local.clear() == IntFlag.posOver); 1397 1398 assert(smartOp.binary!"+="(a, b) == 18_440_000_000_000_000_000uL); 1399 assert(a == 18_440_000_000_000_000_000uL); 1400 1401 uint d = 25u; 1402 int e = 32; 1403 auto f = smartOp.binary!"-"(d, e); 1404 static assert(isSigned!(typeof(f))); 1405 assert(f == -7); 1406 1407 smartOp.binary!"-="(d, e); 1408 assert(IntFlags.local.clear() == IntFlag.negOver); 1409 1410 uint g = 1u << 31; 1411 int h = -1; 1412 auto i = smartOp.binary!"*"(g, h); 1413 static assert(isSigned!(typeof(i))); 1414 assert(i == int.min); 1415 1416 smartOp.binary!"*="(g, h); 1417 assert(IntFlags.local.clear() == IntFlag.negOver); 1418 1419 long j = long.min; 1420 ulong k = 1uL << 63; 1421 auto m = smartOp.binary!"/"(j, k); 1422 static assert(isSigned!(typeof(m))); 1423 assert(m == -1); 1424 1425 smartOp.binary!"/="(j, -1); 1426 assert(IntFlags.local.clear() == IntFlag.posOver); 1427 1428 ushort n = 20u; 1429 ulong p = ulong.max; 1430 auto q = smartOp.binary!"%"(n, p); 1431 static assert(is(typeof(q) == ushort)); 1432 assert(q == 20u); 1433 1434 smartOp.binary!"%="(n, 0); 1435 assert(IntFlags.local.clear() == IntFlag.div0); 1436 } 1437 /// 1438 unittest 1439 { 1440 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1441 1442 assert(smartOp.binary!"<<"(-0x80, -2) == -0x20); 1443 ubyte a = 0x3u; 1444 long b = long.max; 1445 auto c = smartOp.binary!"<<"(a, b); 1446 static assert(is(typeof(c) == ubyte)); 1447 assert(c == 0u); 1448 1449 assert(smartOp.binary!"<<="(a, 7) == 0x80u); 1450 assert(a == 0x80u); 1451 1452 short d = -0xC; 1453 ubyte e = 5u; 1454 auto f = smartOp.binary!">>"(d, e); 1455 static assert(is(typeof(f) == short)); 1456 assert(f == -0x1); 1457 1458 assert(smartOp.binary!">>="(d, -8) == -0xC00); 1459 assert(d == -0xC00); 1460 1461 int g = -0x80; 1462 ulong h = 2u; 1463 auto i = smartOp.binary!">>>"(g, h); 1464 static assert(is(typeof(i) == int)); 1465 assert(i == 0x3FFF_FFE0); 1466 1467 assert(smartOp.binary!">>>="(g, 32) == 0); 1468 assert(g == 0); 1469 1470 ubyte j = 0x6Fu; 1471 short k = 0x4076; 1472 auto m = smartOp.binary!"&"(j, k); 1473 static assert(is(typeof(m) == ushort)); 1474 assert(m == 0x66u); 1475 1476 assert(smartOp.binary!"&="(j, k) == 0x66u); 1477 assert(j == 0x66u); 1478 1479 byte n = 0x6F; 1480 ushort p = 0x4076u; 1481 auto q = smartOp.binary!"|"(n, p); 1482 static assert(is(typeof(q) == ushort)); 1483 assert(q == 0x407Fu); 1484 1485 assert(smartOp.binary!"|="(n, p) == 0x7F); 1486 assert(n == 0x7F); 1487 1488 int r = 0x6F; 1489 int s = 0x4076; 1490 auto t = smartOp.binary!"^"(r, s); 1491 static assert(is(typeof(t) == int)); 1492 assert(t == 0x4019); 1493 1494 assert(smartOp.binary!"^="(r, s) == 0x4019); 1495 assert(r == 0x4019); 1496 1497 assert(!IntFlags.local); 1498 } 1499 1500 /** 1501 Equivalent to `left * pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer 1502 alternative to `left << exp` that is still very fast. 1503 1504 Note that (conceptually) rounding occurs $(I after) the `*`, meaning that `mulPow2(left, -exp)` is 1505 equivalent to `divPow2(left, exp)`. 1506 **/ 1507 auto mulPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 1508 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1509 { 1510 return byPow2Impl!("*", NumFromScal!N, NumFromScal!M)(left, exp); 1511 } 1512 /// ditto 1513 auto mulPow2(N, M)(const N left, const M exp) @safe 1514 if (isFixedPoint!N && isFixedPoint!M) 1515 { 1516 return byPow2Impl!("*", policy, NumFromScal!N, NumFromScal!M)(left, exp); 1517 } 1518 /// 1519 unittest 1520 { 1521 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1522 1523 assert(smartOp.mulPow2(-23, 5) == -736); 1524 smartOp.mulPow2(10_000_000, 10); 1525 assert(IntFlags.local.clear() == IntFlag.posOver); 1526 1527 assert(smartOp.mulPow2(65536, -8) == 256); 1528 assert(smartOp.mulPow2(-100, -100) == 0); 1529 } 1530 1531 /** 1532 Equivalent to `left / pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer 1533 alternative to `left >> exp` that is still very fast. 1534 1535 Note that (conceptually) rounding occurs $(I after) the `/`, meaning that `divPow2(left, -exp)` is 1536 equivalent to `mulPow2(left, exp)`. 1537 **/ 1538 auto divPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 1539 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1540 { 1541 return byPow2Impl!("/", NumFromScal!N, NumFromScal!M)(left, exp); 1542 } 1543 /// ditto 1544 auto divPow2(N, M)(const N left, const M exp) @safe 1545 if (isFixedPoint!N && isFixedPoint!M) 1546 { 1547 return byPow2Impl!("/", policy, NumFromScal!N, NumFromScal!M)(left, exp); 1548 } 1549 /// 1550 unittest 1551 { 1552 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1553 1554 assert(smartOp.divPow2(65536, 8) == 256); 1555 assert(smartOp.divPow2(-100, 100) == 0); 1556 assert(smartOp.divPow2(-23, -5) == -736); 1557 1558 smartOp.divPow2(10_000_000, -10); 1559 assert(IntFlags.local.clear() == IntFlag.posOver); 1560 } 1561 1562 /** 1563 Equivalent to `left % pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer 1564 alternative to `left & ((1 << exp) - 1)` that is still very fast. 1565 **/ 1566 auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 1567 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1568 { 1569 return byPow2Impl!("%", NumFromScal!N, NumFromScal!M)(left, exp); 1570 } 1571 /// ditto 1572 auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 1573 if (isFixedPoint!N && isFixedPoint!M) 1574 { 1575 return byPow2Impl!("%", IntFlagPolicy.noex, NumFromScal!N, NumFromScal!M)(left, exp); 1576 } 1577 /// 1578 unittest 1579 { 1580 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1581 1582 assert(smartOp.modPow2( 101, 1) == 1); 1583 assert(smartOp.modPow2( 101, 3) == 5); 1584 assert(smartOp.modPow2(-101, 3) == -5); 1585 1586 assert(smartOp.modPow2(101, -2) == 0); 1587 assert(smartOp.modPow2(101, 1_000) == 101); 1588 } 1589 1590 /** 1591 Raise `base` to the `exp` power. 1592 1593 Errors that may be signalled if neither input is floating-point: 1594 $(UL 1595 $(LI `IntFlag.posOver` or `IntFlag.negOver` if the absolute value of the result is too large to 1596 represent with the return type.) 1597 $(LI `IntFlag.div0` if `base == 0` and `exp < 0`.) 1598 ) 1599 **/ 1600 auto pow(N, M)(const N base, const M exp) pure @safe nothrow @nogc 1601 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1602 { 1603 alias R = Result!(N, "^^", M); 1604 static assert(is(typeof(return) == R)); 1605 return std.math.pow(cast(R)base, exp); 1606 } 1607 /// ditto 1608 auto pow(N, M)(const N base, const M exp) @safe 1609 if (isFixedPoint!N && isFixedPoint!M) 1610 { 1611 alias R = Result!(N, "^^", M); 1612 1613 const po = powImpl!(R, Select!(isSigned!M, long, ulong))(base, exp); 1614 static assert(is(typeof(po.num) == const(R))); 1615 1616 if (!po.flag.isNull) 1617 po.flag.raise!policy(); 1618 return po.num; 1619 } 1620 /// 1621 unittest 1622 { 1623 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1624 1625 assert(smartOp.pow(-10, 3) == -1_000); 1626 assert(smartOp.pow(16, 4uL) == 65536); 1627 assert(smartOp.pow(2, -1) == 0); 1628 1629 smartOp.pow(-3, 27); 1630 assert(IntFlags.local.clear() == IntFlag.negOver); 1631 smartOp.pow(0, -5); 1632 assert(IntFlags.local.clear() == IntFlag.div0); 1633 } 1634 } 1635 private alias smartOp(bool throws) = smartOp!(cast(Flag!"throws")throws); 1636 1637 // debug ///////////////////////////////////////////////// 1638 /** 1639 `template` `alias` that evaluates to `SafeInt!(N, policy, bitOps)` in debug mode, and `N` in release mode. This 1640 way, you can use `SafeInt!N` to debug your integer logic in testing, but switch to basic `N` in release mode for 1641 maximum speed and the smallest binaries. 1642 1643 While this may be very helpful for debuggin your algorithms, note that `DebugInt` is $(B not) a substitute 1644 for input validation in release mode. Unrecoverable FPEs or silent data-corrupting overflow can still occur in 1645 release mode if you get your algorithm wrong, or fail to add manual bounds checks in the right places. 1646 1647 If performance is your only motivation for using `DebugInt` rather than 1648 $(LINK2 ./package.html#SmartInt, `SmartInt`), consider limiting `DebugInt` to only the hotest code paths - inner 1649 loops and the like. For most programs, this should provide nearly the same performance boost as using it 1650 everywhere, with far less loss of safety. 1651 **/ 1652 template DebugInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps) 1653 if (isIntegral!N || isCheckedInt!N) 1654 { 1655 version (Debug) 1656 alias DebugInt = SafeInt!(N, policy, bitOps); 1657 else 1658 alias DebugInt = Unqual!(BasicScalar!N); 1659 } 1660 1661 // safe ///////////////////////////////////////////////// 1662 /** 1663 Wrapper for any basic integral type `N` that uses the checked operations from `safeOp` and rejects attempts to 1664 directly assign values that cannot be proven to be within the range representable by `N`. 1665 ($(LINK2 ./package.html#to, `checkedint.to()`) can be used to safely assign values of incompatible types, with 1666 runtime bounds checking.) 1667 1668 `SafeInt` is designed to be as interchangeable with `N` as possible, without compromising safety. The 1669 $(LINK2 ./package.html#DebugInt, `DebugInt`) `template` allows a variable to use `SafeInt` in debug mode to find 1670 bugs, and `N` directly in release mode for greater speed and a smaller binary. 1671 1672 If you're not trying to write generic code that works with both `SafeInt!N` and `N`, you should probably be using 1673 $(LINK2 ./package.html#SmartInt, `SmartInt`) instead. It generates far fewer error messages; mostly it 1674 "just works". 1675 1676 $(UL 1677 $(LI `policy` controls the error signalling policy (see `checkedint.flags`).) 1678 $(LI `bitOps` may be set to `No.bitOps` if desired, to turn bitwise operations on this type into a compile-time 1679 error.) 1680 ) 1681 **/ 1682 struct SafeInt(N, IntFlagPolicy _policy, Flag!"bitOps" bitOps = Yes.bitOps) 1683 if (isIntegral!N && isUnqual!N) 1684 { 1685 /// The error signalling policy used by this `SafeInt` type. 1686 enum IntFlagPolicy policy = _policy; 1687 1688 static if (bitOps) 1689 { 1690 /** 1691 The basic integral value of this `SafeInt`. Accessing this directly may be useful for: 1692 $(UL 1693 $(LI Intentionally doing modular (unchecked) arithmetic, or) 1694 $(LI Interacting with APIs that are not `checkedint` aware.) 1695 ) 1696 **/ 1697 N bscal; 1698 /// 1699 unittest 1700 { 1701 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1702 1703 SafeInt!uint n; 1704 static assert(is(typeof(n.bscal) == uint)); 1705 1706 n = 7u; 1707 assert(n.bscal == 7u); 1708 1709 n.bscal -= 8u; 1710 assert(n == uint.max); 1711 } 1712 1713 /// Get a view of this `SafeInt` that allows bitwise operations. 1714 @property ref inout(SafeInt!(N, policy, Yes.bitOps)) bits() /+return+/ inout pure @safe nothrow @nogc 1715 { 1716 return this; 1717 } 1718 /// 1719 unittest 1720 { 1721 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1722 1723 SafeInt!(int, No.bitOps) n = 1; 1724 static assert(!__traits(compiles, n << 2)); 1725 assert(n.bits << 2 == 4); 1726 } 1727 } 1728 else 1729 { 1730 @property ref inout(N) bscal() /+return+/ inout pure @safe nothrow @nogc 1731 { 1732 return bits.bscal; 1733 } 1734 SafeInt!(N, policy, Yes.bitOps) bits; 1735 } 1736 1737 static if (__VERSION__ >= 2067) 1738 { 1739 version(GNU) { static assert(false); } 1740 1741 /// The most negative possible value of this `SafeInt` type. 1742 enum SafeInt!(N, policy, bitOps) min = typeof(this)(trueMin!N); 1743 /// 1744 unittest 1745 { 1746 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1747 1748 assert(SafeInt!(int).min == int.min); 1749 assert(SafeInt!(uint).min == uint.min); 1750 } 1751 1752 /// The most positive possible value of this `SafeInt` type. 1753 enum SafeInt!(N, policy, bitOps) max = typeof(this)(trueMax!N); 1754 /// 1755 unittest 1756 { 1757 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws; 1758 1759 assert(SafeInt!(int).max == int.max); 1760 assert(SafeInt!(uint).max == uint.max); 1761 } 1762 } 1763 else 1764 { 1765 static @property auto min() pure @safe nothrow @nogc 1766 { 1767 return typeof(this)(trueMin!N); 1768 } 1769 static @property auto max() pure @safe nothrow @nogc 1770 { 1771 return typeof(this)(trueMax!N); 1772 } 1773 } 1774 1775 // Construction, assignment, and casting ///////////////////////////////////////////////// 1776 private void checkImplicit(M)() const @safe 1777 if (isCheckedInt!M || isScalarType!M) 1778 { 1779 alias MB = BasicScalar!M; 1780 static assert(trueMin!MB >= cast(real)N.min && MB.max <= cast(real)N.max, 1781 "SafeInt does not support implicit conversions from " ~ 1782 MB.stringof ~ " to " ~ N.stringof ~ 1783 ", because they are unsafe when unchecked. Use the explicit checkedint.to()." 1784 ); 1785 } 1786 1787 /** 1788 Assign the value of `that` to this `SafeInt` instance. 1789 1790 Trying to assign a value that cannot be proven at compile time to be representable by `N` is an error. Use 1791 $(LINK2 ./package.html#to, `checkedint.to()`) to safely convert `that` with runtime bounds checking, instead. 1792 **/ 1793 this(M)(const M that) pure @safe nothrow @nogc 1794 if (isCheckedInt!M || isScalarType!M) 1795 { 1796 checkImplicit!M(); 1797 this.bscal = that.bscal; 1798 } 1799 /// ditto 1800 ref typeof(this) opAssign(M)(const M that) /+return+/ pure @safe nothrow @nogc 1801 if (isCheckedInt!M || isScalarType!M) 1802 { 1803 checkImplicit!M(); 1804 this.bscal = that.bscal; 1805 return this; 1806 } 1807 /// 1808 unittest 1809 { 1810 import checkedint.noex : SafeInt, to; // use IntFlagPolicy.noex 1811 1812 // Only types that for which N can represent all values are accepted directly: 1813 SafeInt!int n = int.max; 1814 n = byte.max; 1815 n = wchar.max; 1816 1817 // Values of a type that could be `< N.min` or `> N.max` are rejected at compile time: 1818 static assert(!__traits(compiles, n = long.max)); 1819 static assert(!__traits(compiles, n = uint.max)); 1820 static assert(!__traits(compiles, n = real.max)); 1821 1822 // Instead, use checkedint.to(), which does runtime bounds checking: 1823 n = to!int(315L); 1824 assert(n == 315); 1825 1826 n = to!int(long.max); 1827 assert(IntFlags.local.clear() == IntFlag.posOver); 1828 } 1829 1830 /** 1831 Convert this value to floating-point. This always succeeds, although some loss of precision may 1832 occur if M.sizeof <= N.sizeof. 1833 **/ 1834 M opCast(M)() const pure @safe nothrow @nogc 1835 if (isFloatingPoint!M) 1836 { 1837 return cast(M)bscal; 1838 } 1839 /// 1840 unittest 1841 { 1842 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1843 1844 SafeInt!int n = 92; 1845 auto f = cast(double)n; 1846 static assert(is(typeof(f) == double)); 1847 assert(f == 92.0); 1848 } 1849 1850 /// `this != 0` 1851 M opCast(M)() const pure @safe nothrow @nogc 1852 if (is(M == bool)) 1853 { 1854 return bscal != 0; 1855 } 1856 /// 1857 unittest 1858 { 1859 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1860 1861 SafeInt!int n = -315; 1862 assert( cast(bool)n); 1863 1864 n = 0; 1865 assert(!cast(bool)n); 1866 } 1867 1868 /** 1869 Convert this value to type `M` using $(LINK2 ./package.html#to, `checkedint.to()`) for bounds checking. An 1870 $(LINK2 ./flags.html, `IntFlag`) will be raised if `M` cannot represent the current value of this `SafeInt`. 1871 **/ 1872 M opCast(M)() const @safe 1873 if (isCheckedInt!M || isIntegral!M || isSomeChar!M) 1874 { 1875 return to!(M, policy)(bscal); 1876 } 1877 /// 1878 unittest 1879 { 1880 import checkedint.noex : SafeInt; // use IntFlagPolicy.noex 1881 1882 SafeInt!ulong n = 52uL; 1883 auto a = cast(int)n; 1884 static assert(is(typeof(a) == int)); 1885 assert(!IntFlags.local); 1886 assert(a == 52); 1887 1888 auto m = SafeInt!long(-1).mulPow2(n); 1889 auto b = cast(wchar)m; 1890 static assert(is(typeof(b) == wchar)); 1891 assert(IntFlags.local.clear() == IntFlag.negOver); 1892 } 1893 1894 /** 1895 Convert this value to a type suitable for indexing an array: 1896 $(UL 1897 $(LI If `N` is signed, a `ptrdiff_t` is returned.) 1898 $(LI If `N` is unsigned, a `size_t` is returned.) 1899 ) 1900 $(LINK2 ./package.html#to, `checkedint.to()`) is used for bounds checking. 1901 **/ 1902 @property Select!(isSigned!N, ptrdiff_t, size_t) idx() const @safe 1903 { 1904 return to!(typeof(return), policy)(bscal); 1905 } 1906 /// 1907 unittest 1908 { 1909 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1910 1911 char[3] arr = ['a', 'b', 'c']; 1912 SafeInt!long n = 1; 1913 1914 // On 32-bit, `long` cannot be used directly for array indexing, 1915 static if (size_t.sizeof < long.sizeof) 1916 static assert(!__traits(compiles, arr[n])); 1917 // but idx can be used to concisely and safely cast to ptrdiff_t: 1918 assert(arr[n.idx] == 'b'); 1919 1920 // The conversion is bounds checked: 1921 static if (size_t.sizeof < long.sizeof) 1922 { 1923 n = long.min; 1924 try 1925 { 1926 arr[n.idx] = '?'; 1927 } 1928 catch (CheckedIntException e) 1929 { 1930 assert(e.intFlags == IntFlag.negOver); 1931 } 1932 } 1933 } 1934 1935 /// Get a simple hashcode for this value. 1936 size_t toHash() const pure @safe nothrow @nogc 1937 { 1938 static if (N.sizeof > size_t.sizeof) 1939 { 1940 static assert(N.sizeof == (2 * size_t.sizeof)); 1941 return cast(size_t)bscal ^ cast(size_t)(bscal >>> 32); 1942 } 1943 else 1944 return cast(size_t)bscal; 1945 } 1946 1947 /// Get a `string` representation of this value. 1948 string toString() const @safe 1949 { 1950 return to!(string, IntFlagPolicy.noex)(bscal); 1951 } 1952 /// ditto 1953 void toString(Writer, Char = char)(Writer sink, FormatSpec!Char fmt = (FormatSpec!Char).init) const 1954 { 1955 formatValue(sink, bscal, fmt); 1956 } 1957 /// 1958 unittest 1959 { 1960 import checkedint.throws : safeInt; // use IntFlagPolicy.throws 1961 assert(safeInt(-753).toString() == "-753"); 1962 } 1963 1964 // Comparison ///////////////////////////////////////////////// 1965 /// See $(LINK2 ./package.html#safeOp, `safeOp`). 1966 bool opEquals(M)(const M right) const pure @safe nothrow @nogc 1967 if (isSafeInt!M || isScalarType!M) 1968 { 1969 return safeOp!(policy).cmp!"=="(this.bscal, right.bscal); 1970 } 1971 1972 /** 1973 Perform a floating-point comparison to `right`. 1974 1975 Returns: $(UL 1976 $(LI `-1` if this value is less than `right`.) 1977 $(LI ` 0` if this value is equal to `right`.) 1978 $(LI ` 1` if this value is greater than `right`.) 1979 $(LI `float.nan` if `right` is a floating-point `nan` value.)) 1980 **/ 1981 auto opCmp(M)(const M right) const pure @safe nothrow @nogc 1982 if (isFloatingPoint!M) 1983 { 1984 return 1985 (left < right)? -1 : 1986 (left > right)? 1 : 1987 (left == right)? 0 : float.nan; 1988 } 1989 /// See $(LINK2 ./package.html#safeOp, `safeOp`). 1990 int opCmp(M)(const M right) const pure @safe nothrow @nogc 1991 if (isSafeInt!M || isFixedPoint!M) 1992 { 1993 return 1994 safeOp!(policy).cmp!"<"(this.bscal, right.bscal)? -1 : 1995 safeOp!(policy).cmp!">"(this.bscal, right.bscal); 1996 } 1997 1998 // Unary ///////////////////////////////////////////////// 1999 /// ditto 2000 typeof(this) opUnary(string op)() const @safe 2001 if (op.among!("-", "+", "~")) 2002 { 2003 static assert(bitOps || (op != "~"), 2004 "Bitwise operations are disabled."); 2005 2006 return typeof(return)(safeOp!(policy).unary!op(bscal)); 2007 } 2008 /// ditto 2009 ref typeof(this) opUnary(string op)() /+return+/ @safe 2010 if (op.among!("++", "--")) 2011 { 2012 safeOp!(policy).unary!op(bscal); 2013 return this; 2014 } 2015 2016 /// ditto 2017 typeof(this) abs() const @safe 2018 { 2019 return typeof(return)(safeOp!(policy).abs(bscal)); 2020 } 2021 2022 /// Count the number of set bits using `core.bitop.popcnt()`. 2023 SafeInt!(int, policy, bitOps) popcnt()() const pure @safe nothrow @nogc 2024 { 2025 static assert(bitOps, "Bitwise operations are disabled."); 2026 2027 import future.bitop : stdPC = popcnt; 2028 return typeof(return)(stdPC(bscal)); 2029 } 2030 2031 /// See $(LINK2 ./package.html#safeOp, `safeOp`). 2032 SafeInt!(int, policy, bitOps) bsf()() const @safe 2033 { 2034 static assert(bitOps, "Bitwise operations are disabled."); 2035 2036 return typeof(return)(safeOp!(policy).bsf(bscal)); 2037 } 2038 /// ditto 2039 SafeInt!(int, policy, bitOps) bsr()() const @safe 2040 { 2041 static assert(bitOps, "Bitwise operations are disabled. Consider using ilogb() instead?"); 2042 2043 return typeof(return)(safeOp!(policy).bsr(bscal)); 2044 } 2045 2046 /// ditto 2047 SafeInt!(int, policy, bitOps) ilogb() const @safe 2048 { 2049 return typeof(return)(safeOp!(policy).ilogb(bscal)); 2050 } 2051 2052 // Binary ///////////////////////////////////////////////// 2053 /// Perform a floating-point math operation. 2054 M opBinaryRight(string op, M)(const M left) const pure @safe nothrow @nogc 2055 if (isFloatingPoint!M) 2056 { 2057 return mixin("left " ~ op ~ " bscal"); 2058 } 2059 /// ditto 2060 M opBinary(string op, M)(const M right) const pure @safe nothrow @nogc 2061 if (isFloatingPoint!M) 2062 { 2063 return mixin("bscal " ~ op ~ " right"); 2064 } 2065 /// See $(LINK2 ./package.html#safeOp, `safeOp`). 2066 SafeInt!(OpType!(M, op, N), policy, bitOps) opBinaryRight(string op, M)(const M left) const @safe 2067 if (isFixedPoint!M) 2068 { 2069 static assert(bitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 2070 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 2071 2072 return typeof(return)(safeOp!(policy).binary!op(left, bscal)); 2073 } 2074 /// ditto 2075 SafeInt!(OpType!(N, op, BasicScalar!M), .max(policy, intFlagPolicyOf!M), bitOps && hasBitOps!M) opBinary(string op, M)(const M right) const @safe 2076 if (isSafeInt!M || isFixedPoint!M) 2077 { 2078 static assert(bitOps && hasBitOps!M || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 2079 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 2080 2081 return typeof(return)(safeOp!(.max(policy, intFlagPolicyOf!M)).binary!op(this.bscal, right.bscal)); 2082 } 2083 /// ditto 2084 ref typeof(this) opOpAssign(string op, M)(const M right) /+return+/ @safe 2085 if (isCheckedInt!M || isFixedPoint!M) 2086 { 2087 static assert((bitOps && hasBitOps!M) || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 2088 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 2089 checkImplicit!(OpType!(N, op, BasicScalar!M))(); 2090 2091 safeOp!(.max(policy, intFlagPolicyOf!M)).binary!(op ~ "=")(this.bscal, right.bscal); 2092 return this; 2093 } 2094 2095 /// ditto 2096 auto mulPow2(M)(const M exp) const pure @safe nothrow @nogc 2097 if (isFloatingPoint!M) 2098 { 2099 return safeOp!(policy).mulPow2(bscal, exp); 2100 } 2101 /// ditto 2102 auto mulPow2(M)(const M exp) const @safe 2103 if (isCheckedInt!M || isFixedPoint!M) 2104 { 2105 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 2106 const wret = safeOp!(mixPolicy).mulPow2(this.bscal, exp.bscal); 2107 return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 2108 } 2109 /// ditto 2110 auto divPow2(M)(const M exp) const pure @safe nothrow @nogc 2111 if (isFloatingPoint!M) 2112 { 2113 return safeOp!(policy).divPow2(bscal, exp); 2114 } 2115 /// ditto 2116 auto divPow2(M)(const M exp) const @safe 2117 if (isCheckedInt!M || isFixedPoint!M) 2118 { 2119 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 2120 const wret = safeOp!(mixPolicy).divPow2(this.bscal, exp.bscal); 2121 return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 2122 } 2123 /// ditto 2124 auto modPow2(M)(const M exp) const pure @safe nothrow @nogc 2125 if (isFloatingPoint!M) 2126 { 2127 return safeOp!(policy).modPow2(bscal, exp); 2128 } 2129 /// ditto 2130 auto modPow2(M)(const M exp) const @safe 2131 if (isCheckedInt!M || isFixedPoint!M) 2132 { 2133 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 2134 const wret = safeOp!(mixPolicy).modPow2(this.bscal, exp.bscal); 2135 return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 2136 } 2137 2138 /// Raise `this` to the `exp` power using `std.math.pow()`. 2139 M pow(M)(const M exp) const pure @safe nothrow @nogc 2140 if (isFloatingPoint!M) 2141 { 2142 return std.math.pow(bscal, exp); 2143 } 2144 /// See $(LINK2 ./package.html#safeOp, `safeOp`). 2145 SafeInt!(CallType!(std.math.pow, N, BasicScalar!M), .max(policy, intFlagPolicyOf!M), bitOps && hasBitOps!M) pow(M)(const M exp) const @safe 2146 if (isCheckedInt!M || isFixedPoint!M) 2147 { 2148 return typeof(return)(safeOp!(.max(policy, intFlagPolicyOf!M)).pow(this.bscal, exp.bscal)); 2149 } 2150 } 2151 /// ditto 2152 template SafeInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps) 2153 if ((isIntegral!N && !isUnqual!N) || isCheckedInt!N) 2154 { 2155 alias SafeInt = SafeInt!(BasicScalar!N, policy, bitOps); 2156 } 2157 /// 2158 unittest 2159 { 2160 // Mixing standard signed and unsigned types is dangerous... 2161 int ba = -1; 2162 uint bb = 0; 2163 assert(ba > bb); 2164 2165 auto bc = ba + bb; 2166 assert(is(typeof(bc) == uint)); 2167 assert(bc == 4294967295u); 2168 2169 // ...that's why SafeInt won't let you do it. 2170 import checkedint.throws : SafeInt, to; // use IntFlagPolicy.throws 2171 2172 SafeInt!int sa = -1; 2173 SafeInt!uint sb = 0u; 2174 static assert(!__traits(compiles, sa < sb)); 2175 static assert(!__traits(compiles, sa + sb)); 2176 2177 // Instead, use checkedint.to() to safely convert to a common type... 2178 auto sbi = to!(SafeInt!int)(sb); 2179 assert(sa < sbi); 2180 auto sc = sa + sbi; 2181 assert(sc == -1); 2182 // (...or just switch to SmartInt.) 2183 } 2184 /// 2185 unittest 2186 { 2187 // When IntFlagPolicy.throws is set, SafeInt operations that fail at runtime will throw a CheckedIntException. 2188 import checkedint.throws : SafeInt; 2189 2190 SafeInt!uint sa = 1u; 2191 SafeInt!uint sb = 0u; 2192 2193 bool overflow = false; 2194 try 2195 { 2196 SafeInt!uint sc = sb - sa; 2197 assert(false); 2198 } 2199 catch (CheckedIntException e) 2200 { 2201 assert(e.intFlags == IntFlag.negOver); 2202 overflow = true; 2203 } 2204 assert(overflow); 2205 2206 bool div0 = false; 2207 try 2208 { 2209 // With standard integers, this would crash the program with an unrecoverable FPE... 2210 SafeInt!uint sc = sa / sb; 2211 assert(false); 2212 } 2213 catch (CheckedIntException e) 2214 { 2215 // ...but with SafeInt, it just throws a normal Exception. 2216 assert(e.intFlags == IntFlag.div0); 2217 div0 = true; 2218 } 2219 assert(div0); 2220 } 2221 /// 2222 unittest 2223 { 2224 // When IntFlagPolicy.noex is set, SafeInt operations that fail at runtime set one or more bits in IntFlags.local. 2225 import checkedint.noex : SafeInt; 2226 2227 SafeInt!uint sa = 1u; 2228 SafeInt!uint sb = 0u; 2229 SafeInt!uint sc; 2230 2231 sc = sb - sa; 2232 assert(IntFlags.local == IntFlag.negOver); 2233 2234 // With standard integers, this would crash the program with an unrecoverable FPE... 2235 sc = sa / sb; 2236 // ...but with SmartInt, it just sets a bit in IntFlags.local. 2237 assert(IntFlags.local & IntFlag.div0); 2238 2239 // Each flag will remain set until cleared: 2240 assert(IntFlags.local.clear() == (IntFlag.negOver | IntFlag.div0)); 2241 assert(!IntFlags.local); 2242 } 2243 2244 private template SafeInt(N, IntFlagPolicy policy, bool bitOps) 2245 if (isIntegral!N) 2246 { 2247 alias SafeInt = SafeInt!( 2248 Unqual!N, 2249 policy, 2250 cast(Flag!"bitOps")bitOps); 2251 } 2252 2253 /// Get the value of `num` as a `SafeInt!N`. The integral type `N` can be infered from the argument. 2254 SafeInt!(N, policy, bitOps) safeInt(IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps, N)(N num) @safe 2255 if (isIntegral!N || isCheckedInt!N) 2256 { 2257 return typeof(return)(num.bscal); 2258 } 2259 /// 2260 unittest 2261 { 2262 import checkedint.throws : safeInt, SafeInt; // use IntFlagPolicy.throws 2263 2264 auto a = safeInt(55uL); 2265 static assert(is(typeof(a) == SafeInt!ulong)); 2266 assert(a == 55u); 2267 } 2268 2269 /** 2270 Implements various integer math operations with error checking. 2271 2272 `safeOp` strives to mimic the standard integer math operations in every way, except: 2273 $(UL 2274 $(LI If the operation is generally untrustworthy - for example, signed/unsigned comparisons - a compile-time error 2275 is generated. The message will usually suggest a workaround.) 2276 $(LI At runtime, if the result is mathematically incorrect an appropriate $(LINK2 ./flags.html, `IntFlag`) 2277 will be raised.) 2278 ) 2279 The runtime error-signalling policy may be selected using the `policy` template parameter. 2280 **/ 2281 template safeOp(IntFlagPolicy policy) 2282 { 2283 // NOTE: ddoc only scans the first branch of a static if 2284 static if (policy == IntFlagPolicy.none) 2285 { 2286 // No need to redundantly instantiate members which don't depend on `policy`. 2287 2288 private void cmpTypeCheck(N, M)() pure @safe nothrow @nogc 2289 { 2290 static assert(isBoolean!N == isBoolean!M, 2291 "The intent of a direct comparison of " ~ 2292 N.stringof ~ " with " ~ M.stringof ~ 2293 " is unclear. Add an explicit cast." 2294 ); 2295 2296 alias OT = OpType!(N, "+", M); 2297 static assert(isFloatingPoint!OT || isSigned!OT || !(isSigned!N || isSigned!M), 2298 "The standard signed/unsigned comparisons of " ~ N.stringof ~ " to " ~ M.stringof ~ 2299 " are unsafe. Use an explicit cast, or switch to smartOp/SmartInt." 2300 ); 2301 } 2302 2303 /** 2304 Compare `left` and `right` using `op`. 2305 2306 Unsafe signed/unsigned comparisons will trigger a compile-time error. Possible solutions include: 2307 $(UL 2308 $(LI Should the inputs really have different signedness? Changing the type of one to match the other 2309 is the simplest solution.) 2310 $(LI Consider using `smartOp.cmp()`, instead, as it can safely do signed/unsigned comparisons.) 2311 $(LI Alternately, $(LINK2 ./package.html#to, `checkedint.to()`) can be used to safely convert the 2312 type of one input, with runtime bounds checking.) 2313 ) $(BR) 2314 Direct comparisons between boolean values and numeric ones are also forbidden. Make your intention explicit: 2315 $(UL 2316 $(LI `numeric == cast(N)boolean`) 2317 $(LI `(numeric != 0) == boolean`) 2318 ) 2319 **/ 2320 bool cmp(string op, N, M)(const N left, const M right) pure @safe nothrow @nogc 2321 if (isScalarType!N && isScalarType!M) 2322 { 2323 cmpTypeCheck!(N, M)(); 2324 return mixin("left " ~ op ~ " right"); 2325 } 2326 /// 2327 unittest 2328 { 2329 import checkedint.noex : safeOp; // safeOp.cmp() never throws 2330 2331 assert(safeOp.cmp!"=="(int.max, 0x7FFF_FFFF)); 2332 assert(safeOp.cmp!"!="(uint.min, 5u)); 2333 assert(safeOp.cmp!"<="(int.min, 0)); 2334 2335 static assert(!__traits(compiles, safeOp.cmp!"=="(uint.max, -1))); 2336 static assert(!__traits(compiles, safeOp.cmp!">"(-1, 1u))); 2337 } 2338 } 2339 else 2340 alias cmp = safeOp!(IntFlagPolicy.none).cmp; 2341 2342 /** 2343 Perform the unary (single-argument) integer operation specified by `op`. 2344 2345 Trying to negate `-` an unsigned value will generate a compile-time error, because mathematically, the result should 2346 always be negative (except for -0), but the unsigned return type cannot represent this. 2347 2348 `++` and `--` are checked for overflow at runtime, and will raise `IntFlag.posOver` or `IntFlag.negOver` if needed. 2349 **/ 2350 N unary(string op, N)(const N num) @safe 2351 if ((isIntegral!N) && op.among!("-", "+", "~")) 2352 { 2353 static assert(isSigned!N || op != "-", 2354 "The standard unary - operation for " ~ N.stringof ~ 2355 " is unsafe. Use an explicit cast to a signed type, or switch to smartOp/SmartInt." 2356 ); 2357 2358 static if (op == "-") 2359 { 2360 static if (is(N == int) || is(N == long)) 2361 { 2362 bool over = false; 2363 const N ret = negs(num, over); 2364 } 2365 else 2366 { 2367 const over = (num <= trueMin!N); 2368 const N ret = -num; 2369 } 2370 2371 if (over) 2372 IntFlag.posOver.raise!policy(); 2373 2374 return ret; 2375 } 2376 else 2377 return mixin(op ~ "num"); 2378 } 2379 /// ditto 2380 /+ref+/ N unary(string op, N)(/+return+/ ref N num) @safe 2381 if ((isIntegral!N) && op.among!("++", "--")) 2382 { 2383 static if (op == "++") 2384 { 2385 if (num >= trueMax!N) 2386 IntFlag.posOver.raise!policy(); 2387 } 2388 else static if (op == "--") 2389 { 2390 if (num <= trueMin!N) 2391 IntFlag.negOver.raise!policy(); 2392 } 2393 2394 return mixin(op ~ "num"); 2395 } 2396 /// 2397 unittest 2398 { 2399 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2400 2401 assert(safeOp.unary!"~"(0u) == uint.max); 2402 2403 assert(safeOp.unary!"-"(20L) == -20L); 2404 static assert(!__traits(compiles, safeOp.unary!"-"(20uL))); 2405 safeOp.unary!"-"(long.min); 2406 assert(IntFlags.local.clear() == IntFlag.posOver); 2407 2408 auto a = safeOp.unary!"+"(uint.max); 2409 static assert(is(typeof(a) == uint)); 2410 assert(a == uint.max); 2411 2412 uint b = 1u; 2413 assert(safeOp.unary!"--"(b) == 0u); 2414 assert(b == 0u); 2415 safeOp.unary!"--"(b); 2416 assert(IntFlags.local.clear() == IntFlag.negOver); 2417 2418 int c = 7; 2419 assert(safeOp.unary!"++"(c) == 8); 2420 assert(c == 8); 2421 } 2422 2423 /** 2424 Get the absolute value of `num`. 2425 2426 `IntFlag.posOver` is raised if `N` is signed and `num == N.min`. 2427 **/ 2428 N abs(N)(const N num) @safe 2429 if (isIntegral!N || isBoolean!N) 2430 { 2431 static if (isSigned!N) 2432 { 2433 if (num < 0) 2434 return unary!"-"(num); 2435 } 2436 return num; 2437 } 2438 /// 2439 unittest 2440 { 2441 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2442 2443 assert(safeOp.abs(-25) == 25); 2444 assert(safeOp.abs(745u) == 745u); 2445 2446 safeOp.abs(int.min); 2447 assert(IntFlags.local.clear() == IntFlag.posOver); 2448 } 2449 2450 /// `core.bitop.bsf` without the undefined behaviour. `safeOp.bsf(0)` will raise `IntFlag.undef`. 2451 int bsf(N)(const N num) @safe 2452 if (isFixedPoint!N) 2453 { 2454 return bsfImpl!policy(num); 2455 } 2456 /// 2457 unittest 2458 { 2459 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2460 2461 assert(safeOp.bsf(20) == 2); 2462 2463 safeOp.bsf(0); 2464 assert(IntFlags.local.clear() == IntFlag.undef); 2465 } 2466 2467 /// `core.bitop.bsr` without the undefined behaviour. `safeOp.bsr(0)` will raise `IntFlag.undef`. 2468 int bsr(N)(const N num) @safe 2469 if (isFixedPoint!N) 2470 { 2471 return bsrImpl!policy(num); 2472 } 2473 /// 2474 unittest 2475 { 2476 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2477 2478 assert(safeOp.bsr( 20) == 4); 2479 assert(safeOp.bsr(-20) == 31); 2480 2481 safeOp.bsr(0); 2482 assert(IntFlags.local.clear() == IntFlag.undef); 2483 } 2484 2485 /** 2486 Get the base 2 logarithm of `abs(num)`, rounded down to the nearest integer. 2487 2488 `safeOp.ilogb(0)` will raise `IntFlag.undef`. 2489 **/ 2490 int ilogb(N)(const N num) @safe 2491 if (isFixedPoint!N) 2492 { 2493 static if (isSigned!N) 2494 const absN = cast(Unsigned!N) (num < 0? -num : num); 2495 else 2496 alias absN = num; 2497 2498 return bsrImpl!policy(absN); 2499 } 2500 /// 2501 unittest 2502 { 2503 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2504 2505 assert(safeOp.ilogb( 20) == 4); 2506 assert(safeOp.ilogb(-20) == 4); 2507 2508 safeOp.ilogb(0); 2509 assert(IntFlags.local.clear() == IntFlag.undef); 2510 } 2511 2512 private auto binaryImpl(string op, N, M)(const N left, const M right) @safe 2513 if (isFixedPoint!N && isFixedPoint!M) 2514 { 2515 enum wop = (op[$-1] == '=')? op[0 .. $-1] : op; 2516 alias P = OpType!(N, wop, M); 2517 alias R = Select!(wop == op, P, N); 2518 2519 static if (wop.among!("+", "-", "*")) 2520 { 2521 enum isPromSafe = !(isSigned!N || isSigned!M) || (isSigned!P && isSigned!R); 2522 enum needCOp = (wop == "*")? 2523 (precision!N + precision!M) > precision!P : 2524 (max(precision!N, precision!M) + 1) > precision!P; 2525 2526 bool over = false; 2527 static if (needCOp) 2528 { 2529 enum cx = (staticIndexOf!(wop, "+", "-", "*") << 1) + isSigned!P; 2530 alias cop = AliasSeq!(addu, adds, subu, subs, mulu, muls)[cx]; 2531 2532 const pR = cop(cast(P)left, cast(P)right, over); 2533 } 2534 else 2535 const pR = mixin("left " ~ wop ~ " right"); 2536 2537 static if (isSigned!P && trueMin!P < trueMin!R) 2538 { 2539 if (pR < trueMin!R) 2540 over = true; 2541 } 2542 static if (trueMax!P > trueMax!R) 2543 { 2544 if (pR > trueMax!R) 2545 over = true; 2546 } 2547 2548 if (over) 2549 IntFlag.over.raise!policy(); 2550 return cast(R)pR; 2551 } 2552 else static if (wop.among!("/", "%")) 2553 { 2554 enum isPromSafe = !(isSigned!N || isSigned!M) || 2555 (isSigned!P && (wop == "%"? (isSigned!R || !isSigned!N) : isSigned!R)); 2556 2557 const div0 = (right == 0); 2558 static if (isSigned!N && isSigned!M) 2559 const posOver = (left == trueMin!R) && (right == -1); 2560 else 2561 enum posOver = false; 2562 2563 R ret = void; 2564 if (div0 || posOver) 2565 { 2566 (posOver? IntFlag.posOver : IntFlag.div0).raise!policy(); 2567 ret = 0; // Prevent unrecoverable FPE 2568 } 2569 else 2570 ret = cast(R)mixin("left " ~ wop ~ " right"); 2571 2572 return ret; 2573 } 2574 else static if (wop.among!("<<", ">>", ">>>")) 2575 { 2576 enum isPromSafe = !isSigned!N || isSigned!R || (op == ">>>"); 2577 2578 enum invalidSh = ~cast(M)(8 * P.sizeof - 1); 2579 if (right & invalidSh) 2580 IntFlag.undef.raise!policy(); 2581 2582 return cast(R) mixin("cast(P)left " ~ wop ~ " right"); 2583 } 2584 else static if (wop.among!("&", "|", "^")) 2585 { 2586 enum isPromSafe = true; 2587 2588 return cast(R)mixin("left " ~ wop ~ " right"); 2589 } 2590 else 2591 static assert(false); 2592 2593 static assert(isPromSafe, 2594 "The standard " ~ N.stringof ~ " " ~ op ~ " " ~ M.stringof ~ 2595 " operation is unsafe, due to a signed/unsigned mismatch. " ~ 2596 "Use an explicit cast, or switch to smartOp/SmartInt." 2597 ); 2598 } 2599 2600 /** 2601 Perform the binary (two-argument) integer operation specified by `op`. 2602 $(UL 2603 $(LI Unsafe signed/unsigned operations will generate a compile-time error.) 2604 $(LI `+`, `-`, `*`, `/`, and `%` are checked for overflow at runtime.) 2605 $(LI `/` and `%` are also checked for divide-by-zero.) 2606 $(LI `<<`, `>>`, and `>>>` are checked to verify that `right >= 0` and `right < (8 * typeof(left).sizeof)`. 2607 Otherwise, `IntFlag.undef` is raised.) 2608 ) $(BR) 2609 Note also: 2610 $(UL 2611 $(LI The shift operators are $(B not) checked for overflow and should not be used for multiplication, 2612 division, or exponentiation. Instead, use `mulPow2()` and `divPow2()`, which internally use the bitshifts for speed, 2613 but check for overflow and correctly handle negative values.) 2614 $(LI Likewise, `modPow2()` should be used for remainders instead of `&`.) 2615 $(LI `^^` and `^^=` will remain disabled in favour of `pow` until DMD issues 15288 and 15412 are fixed.) 2616 ) $(BR) 2617 Like the standard equiavlents, the assignment operators (`+=`, `-=`, `*=`, etc.) take `left` by `ref` and will overwrite 2618 it with the result of the operation. 2619 **/ 2620 OpType!(N, op, M) binary(string op, N, M)(const N left, const M right) @safe 2621 if (isFixedPoint!N && isFixedPoint!M && 2622 op.among!("+", "-", "*", "/", "%", "^^", "<<", ">>", ">>>", "&", "|", "^")) 2623 { 2624 static assert(op != "^^", 2625 "pow() should be used instead of operator ^^ because of issue 15288."); 2626 2627 return binaryImpl!op(left, right); 2628 } 2629 /// ditto 2630 /+ref+/ N binary(string op, N, M)(/+return+/ ref N left, const M right) @safe 2631 if (isIntegral!N && isFixedPoint!M && (op[$ - 1] == '=')) 2632 { 2633 static assert(op != "^^=", 2634 "pow() should be used instead of operator ^^= because of issue 15412."); 2635 2636 left = binaryImpl!op(left, right); 2637 return left; 2638 } 2639 /// 2640 unittest 2641 { 2642 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2643 2644 assert(safeOp.binary!"+"(17, -5) == 12); 2645 static assert(!__traits(compiles, safeOp.binary!"+"(-1, 1u))); 2646 2647 ulong a = 18_446_744_073_709_551_615uL; 2648 safeOp.binary!"+="(a, 1u); 2649 assert(IntFlags.local.clear() == IntFlag.posOver); 2650 2651 assert(safeOp.binary!"-"(17u, 5u) == 12u); 2652 safeOp.binary!"-"(5u, 17u); 2653 assert(IntFlags.local.clear() == IntFlag.negOver); 2654 2655 ulong b = 123_456_789_987_654_321uL; 2656 static assert(!__traits(compiles, safeOp.binary!"-="(b, 987_654_321))); 2657 assert(safeOp.binary!"-="(b, 987_654_321u) == 123_456_789_000_000_000uL); 2658 assert(b == 123_456_789_000_000_000uL); 2659 2660 assert(safeOp.binary!"*"(-1 << 30, 2) == int.min); 2661 safeOp.binary!"*"(1 << 30, 2); 2662 assert(IntFlags.local.clear() == IntFlag.negOver); 2663 2664 uint c = 1u << 18; 2665 assert(safeOp.binary!"*="(c, 1u << 4) == 1u << 22); 2666 assert(c == 1u << 22); 2667 2668 assert(safeOp.binary!"/"(22, 11) == 2); 2669 assert(!__traits(compiles, safeOp.binary!"/"(-22, 11u))); 2670 safeOp.binary!"/"(0, 0); 2671 assert(IntFlags.local.clear() == IntFlag.div0); 2672 2673 long j = long.min; 2674 safeOp.binary!"/="(j, -1); 2675 assert(IntFlags.local.clear() == IntFlag.posOver); 2676 2677 assert(safeOp.binary!"%"(20u, 7u) == 6u); 2678 static assert(!__traits(compiles, safeOp.binary!"%"(20u, -7))); 2679 safeOp.binary!"%"(20u, 0u); 2680 assert(IntFlags.local.clear() == IntFlag.div0); 2681 2682 short n = 75; 2683 assert(safeOp.binary!"%="(n, -10) == 5); 2684 assert(n == 5); 2685 } 2686 /// 2687 unittest 2688 { 2689 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2690 2691 assert(safeOp.binary!"<<"(-0x80, 2) == -0x200); 2692 safeOp.binary!"<<"(-0x80, -2); 2693 assert(IntFlags.local.clear() == IntFlag.undef); 2694 2695 ubyte a = 0x3u; 2696 safeOp.binary!"<<="(a, 7); 2697 assert(a == 0x80u); 2698 2699 assert(safeOp.binary!">>"(-0xC, 5u) == -0x1); 2700 safeOp.binary!">>"(-0xC, long.max); 2701 assert(IntFlags.local.clear() == IntFlag.undef); 2702 2703 short b = 0x700; 2704 assert(safeOp.binary!">>="(b, 8) == 0x7); 2705 assert(b == 0x7); 2706 2707 assert(safeOp.binary!">>>"(-0x80, 2u) == 0x3FFF_FFE0); 2708 safeOp.binary!">>>"(-0x80, 32); 2709 assert(IntFlags.local.clear() == IntFlag.undef); 2710 2711 int c = 0xFE_DCBA; 2712 assert(safeOp.binary!">>>="(c, 12) == 0xFED); 2713 assert(c == 0xFED); 2714 2715 assert(safeOp.binary!"&"(0x6Fu, 0x4076) == 0x66u); 2716 2717 ubyte d = 0x6Fu; 2718 assert(safeOp.binary!"&="(d, 0x4076) == 0x66u); 2719 assert(d == 0x66u); 2720 2721 assert(safeOp.binary!"|"(0x6F, 0x4076u) == 0x407Fu); 2722 2723 byte e = 0x6F; 2724 assert(safeOp.binary!"|="(e, 0x4076u) == 0x7F); 2725 assert(e == 0x7F); 2726 2727 assert(safeOp.binary!"^"(0x6F, 0x4076) == 0x4019); 2728 2729 int f = 0x6F; 2730 assert(safeOp.binary!"^="(f, 0x4076) == 0x4019); 2731 assert(f == 0x4019); 2732 2733 assert(!IntFlags.local); 2734 } 2735 2736 /** 2737 Equivalent to `left * pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to 2738 `left << exp` that is still very fast. 2739 2740 Note that (conceptually) rounding occurs $(I after) the `*`, meaning that `mulPow2(left, -exp)` is equivalent to 2741 `divPow2(left, exp)`. 2742 **/ 2743 auto mulPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 2744 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 2745 { 2746 return byPow2Impl!("*", NumFromScal!N, NumFromScal!M)(left, exp); 2747 } 2748 /// ditto 2749 auto mulPow2(N, M)(const N left, const M exp) @safe 2750 if (isFixedPoint!N && isFixedPoint!M) 2751 { 2752 return byPow2Impl!("*", policy, NumFromScal!N, NumFromScal!M)(left, exp); 2753 } 2754 /// 2755 unittest 2756 { 2757 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2758 2759 assert(safeOp.mulPow2(-23, 5) == -736); 2760 safeOp.mulPow2(10_000_000, 10); 2761 assert(IntFlags.local.clear() == IntFlag.posOver); 2762 2763 assert(safeOp.mulPow2(65536, -8) == 256); 2764 assert(safeOp.mulPow2(-100, -100) == 0); 2765 } 2766 2767 /** 2768 Equivalent to `left / pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to 2769 `left >> exp` that is still very fast. 2770 2771 Note that (conceptually) rounding occurs $(I after) the `/`, meaning that `divPow2(left, -exp)` is equivalent to 2772 `mulPow2(left, exp)`. 2773 **/ 2774 auto divPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 2775 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 2776 { 2777 return byPow2Impl!("/", NumFromScal!N, NumFromScal!M)(left, exp); 2778 } 2779 /// ditto 2780 auto divPow2(N, M)(const N left, const M exp) @safe 2781 if (isFixedPoint!N && isFixedPoint!M) 2782 { 2783 return byPow2Impl!("/", policy, NumFromScal!N, NumFromScal!M)(left, exp); 2784 } 2785 /// 2786 unittest 2787 { 2788 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2789 2790 assert(safeOp.divPow2(65536, 8) == 256); 2791 assert(safeOp.divPow2(-100, 100) == 0); 2792 assert(safeOp.divPow2(-23, -5) == -736); 2793 2794 safeOp.divPow2(10_000_000, -10); 2795 assert(IntFlags.local.clear() == IntFlag.posOver); 2796 } 2797 2798 /** 2799 Equivalent to `left % pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to 2800 `left & ((1 << exp) - 1)` that is still very fast. 2801 **/ 2802 auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 2803 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 2804 { 2805 return byPow2Impl!("%", NumFromScal!N, NumFromScal!M)(left, exp); 2806 } 2807 /// ditto 2808 auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc 2809 if (isFixedPoint!N && isFixedPoint!M) 2810 { 2811 return byPow2Impl!("%", IntFlagPolicy.noex, NumFromScal!N, NumFromScal!M)(left, exp); 2812 } 2813 /// 2814 unittest 2815 { 2816 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2817 2818 assert(safeOp.modPow2( 101, 1) == 1); 2819 assert(safeOp.modPow2( 101, 3) == 5); 2820 assert(safeOp.modPow2(-101, 3) == -5); 2821 2822 assert(safeOp.modPow2(101, -2) == 0); 2823 assert(safeOp.modPow2(101, 1_000) == 101); 2824 } 2825 2826 /** 2827 Raise `base` to the `exp` power. 2828 2829 Errors that may be signalled if neither input is floating-point: 2830 $(UL 2831 $(LI `IntFlag.posOver` or `IntFlag.negOver` if the absolute value of the result is too large to 2832 represent with the return type.) 2833 $(LI `exp < 0`, `IntFlag.undef` is raised because `std.math.pow` would trigger an FPE given the same input.) 2834 ) 2835 **/ 2836 CallType!(std.math.pow, N, M) pow(N, M)(const N base, const M exp) @safe 2837 if (isFixedPoint!N && isFixedPoint!M) 2838 { 2839 alias R = typeof(return); 2840 static assert(!isSigned!N || isSigned!R, 2841 "std.math.pow(" ~ N.stringof ~ ", " ~ M.stringof ~ 2842 ") is unsafe, due to a signed/unsigned mismatch. Use an explicit cast, or switch to smartOp/SmartInt." 2843 ); 2844 2845 auto po = powImpl!(R, Select!(isSigned!M, long, ulong))(base, exp); 2846 static assert(is(typeof(po.num) == R)); 2847 if (exp < 0) 2848 po.flag = IntFlag.undef; 2849 2850 if (!po.flag.isNull) 2851 po.flag.raise!policy(); 2852 return po.num; 2853 } 2854 /// 2855 unittest 2856 { 2857 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2858 2859 assert(safeOp.pow(-10, 3) == -1_000); 2860 static assert(!__traits(compiles, safeOp.pow(16, 4uL))); 2861 safeOp.pow(2, -1); 2862 assert(IntFlags.local.clear() == IntFlag.undef); 2863 2864 safeOp.pow(-3, 27); 2865 assert(IntFlags.local.clear() == IntFlag.negOver); 2866 safeOp.pow(0, -5); 2867 assert(IntFlags.local.clear() == IntFlag.undef); 2868 } 2869 } 2870 private alias safeOp(bool throws) = safeOp!(cast(Flag!"throws")throws); 2871 2872 // conv ///////////////////////////////////////////////// 2873 /** 2874 A wrapper for `std.conv.to()` which uses `checkedint.flags` for error signaling when converting between any combination 2875 of basic scalar types and `checkedint` types. With an appropriate `policy`, this allows 2876 $(LINK2 ./package.html#to, `checkedint.to()`) to be used for numeric conversions in `pure nothrow` code, unlike 2877 `std.conv.to()`. 2878 2879 Conversions involving any other type are simply forwarded to `std.conv.to()`, with no runtime overhead. 2880 **/ 2881 template to(T, IntFlagPolicy policy) 2882 { 2883 private enum useFlags(S) = (isCheckedInt!T || isScalarType!T) && (isCheckedInt!S || isScalarType!S); 2884 private enum reqAttrs = 2885 ((policy == IntFlagPolicy.noex || policy == IntFlagPolicy.asserts)? " nothrow" : "") ~ 2886 ((policy == IntFlagPolicy.asserts || policy == IntFlagPolicy.throws)? " pure" : ""); 2887 2888 T to(S)(const S value) @safe 2889 if (useFlags!S) 2890 { 2891 static if (isCheckedInt!T || isCheckedInt!S) 2892 return T(checkedint.to!(BasicScalar!T, policy)(value.bscal)); 2893 else 2894 { 2895 static if (policy != IntFlagPolicy.none && !isFloatingPoint!T) 2896 { 2897 static if (isFloatingPoint!S) 2898 { 2899 if (value >= trueMin!T) 2900 { 2901 if (value > trueMax!T) 2902 IntFlag.posOver.raise!policy(); 2903 } 2904 else 2905 (std.math.isNaN(value)? IntFlag.undef : IntFlag.negOver).raise!policy(); 2906 } 2907 else 2908 { 2909 static if (cast(long)trueMin!S < cast(long)trueMin!T) 2910 { 2911 if (value < cast(S)trueMin!T) 2912 IntFlag.negOver.raise!policy(); 2913 } 2914 static if (cast(ulong)trueMax!S > cast(ulong)trueMax!T) 2915 { 2916 if (value > cast(S)trueMax!T) 2917 IntFlag.posOver.raise!policy(); 2918 } 2919 } 2920 } 2921 return cast(T)value; 2922 } 2923 } 2924 2925 mixin(` 2926 T to(S)(S value)` ~ reqAttrs ~ ` 2927 if (!useFlags!S) 2928 { 2929 import std.conv : impl = to; 2930 return impl!T(value); 2931 }`); 2932 } 2933 /// 2934 unittest 2935 { 2936 // Conversions involving only basic scalars or checkedint types use IntFlags for error signalling. 2937 import checkedint.noex : smartInt, SmartInt, smartOp, to; // use IntFlagPolicy.noex 2938 2939 assert(to!int(smartInt(-421751L)) == -421751); 2940 assert(to!(SmartInt!ubyte)(100) == 100u); 2941 2942 assert(is(typeof(to!int(50u)) == int)); 2943 assert(to!int(50u) == 50); 2944 assert(!IntFlags.local); 2945 2946 // If IntFlagPolicy.noex is set, failed conversions return garbage, but... 2947 assert(smartOp.cmp!"!="(to!int(uint.max), uint.max)); 2948 // ...IntFlags.local can be checked to see if anything went wrong. 2949 assert(IntFlags.local.clear() == IntFlag.posOver); 2950 } 2951 /// 2952 unittest 2953 { 2954 // Everything else forwards to std.conv.to(). 2955 assert(to!(string, IntFlagPolicy.throws)(55) == "55"); 2956 assert(to!(real, IntFlagPolicy.throws)("3.141519e0") == 3.141519L); 2957 2958 // Setting IntFlagPolicy.noex or .asserts will block std.conv.to(), unless the instantiation is nothrow. 2959 // Setting IntFlagPolicy.asserts or .throws will block std.conv.to(), unless the instantiation is pure. 2960 static assert(!__traits(compiles, to!(real, IntFlagPolicy.noex)("3.141519e0"))); 2961 } 2962 2963 @property { 2964 /** 2965 Get a view or copy of `num` as a basic scalar. 2966 2967 Useful in generic code that handles both basic types, and `checkedint` types. 2968 **/ 2969 /+ref inout(N) bscal(N)(return ref inout(N) num) @safe 2970 if (isScalarType!N) 2971 { 2972 return num; 2973 } 2974 /// ditto 2975 ref inout(N) bscal(N)(return ref inout(N) num) @safe 2976 if (isCheckedInt!N) 2977 { 2978 return num.bscal; 2979 } 2980 /// ditto +/ 2981 N bscal(N)(const N num) @safe 2982 if (isScalarType!N) 2983 { 2984 return num; 2985 } 2986 /// ditto 2987 BasicScalar!N bscal(N)(const N num) @safe 2988 if (isCheckedInt!N) 2989 { 2990 return num.bscal; 2991 } 2992 /// 2993 unittest 2994 { 2995 import checkedint.throws : smartInt, SmartInt; // use IntFlagPolicy.throws 2996 2997 assert(is(typeof(bscal(2u)) == uint)); 2998 assert(is(typeof(bscal(SmartInt!int(2))) == int)); 2999 3000 assert(bscal(-3153) == -3153); 3001 assert(bscal(smartInt(75_000)) == 75_000); 3002 } 3003 3004 /** 3005 Get a view or copy of `num` that supports bitwise operations. 3006 3007 Useful in generic code that handles both basic types and `checkedint` types. 3008 **/ 3009 /+ref inout(N) bits(N)(return ref inout(N) num) @safe 3010 if (isFixedPoint!N) 3011 { 3012 return num; 3013 } 3014 /// ditto 3015 ref inout(N) bits(N)(return ref inout(N) num) @safe 3016 if (isCheckedInt!N) 3017 { 3018 return num.bits; 3019 } 3020 /// ditto +/ 3021 N bits(N)(const N num) @safe 3022 if (isFixedPoint!N) 3023 { 3024 return num; 3025 } 3026 /// ditto 3027 SmartInt!(BasicScalar!N, N.policy, Yes.bitOps) bits(N)(const N num) @safe 3028 if (isSmartInt!N) 3029 { 3030 return num.bits; 3031 } 3032 /// ditto 3033 SafeInt!(BasicScalar!N, N.policy, Yes.bitOps) bits(N)(const N num) @safe 3034 if (isSafeInt!N) 3035 { 3036 return num.bits; 3037 } 3038 /// 3039 unittest 3040 { 3041 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 3042 3043 assert(is(typeof(bits(5)) == int)); 3044 3045 SmartInt!(int, No.bitOps) noBits = 5; 3046 assert(is(typeof(bits(noBits)) == SmartInt!(int, Yes.bitOps))); 3047 3048 static assert(!__traits(compiles, noBits << 2)); 3049 assert((bits(noBits) << 2) == 20); 3050 } 3051 3052 /** 3053 Cast `num` to a basic type suitable for indexing an array. 3054 3055 For signed types, `ptrdiff_t` is returned. For unsigned types, `size_t` is returned. 3056 **/ 3057 Select!(isSigned!N, ptrdiff_t, size_t) idx(IntFlagPolicy policy, N)(const N num) @safe 3058 if (isScalarType!N || isCheckedInt!N) 3059 { 3060 return to!(typeof(return), policy)(num.bscal); 3061 } 3062 /// ditto 3063 Select!(isSigned!(BasicScalar!N), ptrdiff_t, size_t) idx(N)(const N num) @safe 3064 if (isCheckedInt!N) 3065 { 3066 return num.idx; 3067 } 3068 /// 3069 unittest 3070 { 3071 import checkedint.noex : idx, SmartInt, safeInt; // use IntFlagPolicy.noex 3072 3073 assert(is(typeof(idx(cast(long)1)) == ptrdiff_t)); 3074 assert(is(typeof(idx(cast(ubyte)1)) == size_t)); 3075 assert(is(typeof(idx(SmartInt!ulong(1))) == size_t)); 3076 3077 assert(idx(17uL) == 17); 3078 assert(idx(-3) == -3); 3079 assert(idx(safeInt(cast(byte)100)) == 100); 3080 3081 static if (size_t.sizeof == 4) 3082 { 3083 idx(ulong.max); 3084 assert(IntFlags.local.clear() == IntFlag.posOver); 3085 3086 idx(long.min); 3087 assert(IntFlags.local.clear() == IntFlag.negOver); 3088 } 3089 } 3090 } 3091 /+}+/ 3092 3093 // traits ///////////////////////////////////////////////// 3094 3095 /// Evaluates to `true` if `T` is an instance of $(LINK2 ./package.html#SafeInt, `SafeInt`). 3096 enum isSafeInt(T) = isInstanceOf!(SafeInt, T); 3097 /// 3098 unittest 3099 { 3100 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3101 3102 assert( isSafeInt!(SafeInt!int)); 3103 3104 assert(!isSafeInt!int); 3105 assert(!isSafeInt!(SmartInt!int)); 3106 } 3107 3108 /// Evaluates to `true` if `T` is an instance of $(LINK2 ./package.html#SmartInt, `SmartInt`). 3109 enum isSmartInt(T) = isInstanceOf!(SmartInt, T); 3110 /// 3111 unittest 3112 { 3113 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3114 3115 assert( isSmartInt!(SmartInt!int)); 3116 3117 assert(!isSmartInt!int); 3118 assert(!isSmartInt!(SafeInt!int)); 3119 } 3120 3121 /** 3122 Evaluates to `true` if `T` is an instance of $(LINK2 ./package.html#SafeInt, `SafeInt`) 3123 or $(LINK2 ./package.html#SmartInt, `SmartInt`). 3124 **/ 3125 enum isCheckedInt(T) = isSafeInt!T || isSmartInt!T; 3126 /// 3127 unittest 3128 { 3129 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3130 3131 assert( isCheckedInt!(SafeInt!int)); 3132 assert( isCheckedInt!(SmartInt!int)); 3133 3134 assert(!isCheckedInt!int); 3135 } 3136 3137 /** 3138 Evaluates to `true` if either: 3139 $(UL 3140 $(LI `isScalarType!T`, or) 3141 $(LI `isCheckedInt!T`) 3142 ) 3143 $(B And) bitwise operators such as `<<` and `~` are available for `T`. 3144 **/ 3145 template hasBitOps(T) 3146 { 3147 static if (isCheckedInt!T) 3148 enum hasBitOps = TemplateArgsOf!T[2]; 3149 else 3150 enum hasBitOps = isFixedPoint!T; 3151 } 3152 /// 3153 unittest 3154 { 3155 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3156 3157 assert( hasBitOps!(SafeInt!(int, Yes.bitOps))); 3158 assert( hasBitOps!(SmartInt!(int, Yes.bitOps))); 3159 assert( hasBitOps!int); 3160 assert( hasBitOps!bool); 3161 assert( hasBitOps!dchar); 3162 3163 assert(!hasBitOps!(SafeInt!(int, No.bitOps))); 3164 assert(!hasBitOps!(SmartInt!(int, No.bitOps))); 3165 assert(!hasBitOps!float); 3166 } 3167 3168 /** 3169 Aliases to the basic scalar type associated with `T`, assuming either: 3170 $(UL 3171 $(LI `isScalarType!T`, or) 3172 $(LI `isCheckedInt!T`) 3173 ) 3174 Otherwise, `BasicScalar` aliases to `void`. 3175 **/ 3176 template BasicScalar(T) 3177 { 3178 static if (isScalarType!T) 3179 alias BasicScalar = Unqual!T; 3180 else static if (isCheckedInt!T) 3181 alias BasicScalar = TemplateArgsOf!T[0]; 3182 else 3183 alias BasicScalar = void; 3184 } 3185 /// 3186 unittest 3187 { 3188 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3189 3190 assert(is(BasicScalar!(SafeInt!int) == int)); 3191 assert(is(BasicScalar!(SmartInt!ushort) == ushort)); 3192 3193 assert(is(BasicScalar!int == int)); 3194 assert(is(BasicScalar!(const shared real) == real)); 3195 } 3196 3197 // internal ///////////////////////////////////////////////// 3198 private 3199 { 3200 enum N trueMin(N) = mostNegative!N; 3201 template trueMax(N) 3202 if (isScalarType!N) 3203 { 3204 static if (is(Unqual!N == dchar)) 3205 enum N trueMax = ~cast(N)0; 3206 else 3207 enum N trueMax = N.max; 3208 } 3209 3210 template NumFromScal(N) 3211 if (isScalarType!N) 3212 { 3213 static if (isNumeric!N) 3214 alias NumFromScal = N; 3215 else static if (isSomeChar!N) 3216 alias NumFromScal = IntFromChar!N; 3217 else //if (isBoolean!N) 3218 alias NumFromScal = ubyte; 3219 } 3220 3221 /+pragma(inline, true) 3222 {+/ 3223 int bsfImpl(IntFlagPolicy policy, N)(const N num) @safe 3224 if (isFixedPoint!N) 3225 { 3226 static if (isSigned!N) 3227 return bsfImpl!(policy, Unsigned!N)(num); 3228 else 3229 { 3230 static assert(N.sizeof <= ulong.sizeof); 3231 3232 int ret = void; 3233 if (num == 0) 3234 { 3235 IntFlag.undef.raise!policy(); 3236 ret = int.min; 3237 } 3238 else 3239 ret = bsf(num); 3240 3241 return ret; 3242 } 3243 } 3244 int bsrImpl(IntFlagPolicy policy, N)(const N num) @safe 3245 if (isFixedPoint!N) 3246 { 3247 static if (isSigned!N) 3248 return bsrImpl!(policy, Unsigned!N)(num); 3249 else 3250 { 3251 static assert(N.sizeof <= ulong.sizeof); 3252 3253 int ret = void; 3254 if (num == 0) 3255 { 3256 IntFlag.undef.raise!policy(); 3257 ret = int.min; 3258 } 3259 else 3260 ret = bsr(num); 3261 3262 return ret; 3263 } 3264 } 3265 3266 auto byPow2Impl(string op, N, M)(const N left, const M exp) pure @safe nothrow @nogc 3267 if (op.among!("*", "/", "%") && ((isFloatingPoint!N && isNumeric!M) || (isNumeric!N && isFloatingPoint!M))) 3268 { 3269 import std.math : exp2, isFinite, frexp, ldexp; 3270 3271 enum wantPrec = max(precision!N, precision!M); 3272 alias R = 3273 Select!(wantPrec <= precision!float, float, 3274 Select!(wantPrec <= precision!double, double, real)); 3275 3276 static if (isFloatingPoint!M) 3277 { 3278 R ret = void; 3279 3280 static if (op.among!("*", "/")) 3281 { 3282 if (left == 0 && exp.isFinite) 3283 ret = 0; 3284 else 3285 { 3286 R wexp = cast(R)exp; 3287 static if (op == "/") 3288 wexp = -wexp; 3289 3290 ret = cast(R)left * exp2(wexp); 3291 } 3292 } 3293 else 3294 { 3295 const p2 = exp2(cast(R)exp); 3296 ret = 3297 p2.isFinite? cast(R)left % p2 : 3298 (p2 > 0)? cast(R)left : 3299 (p2 < 0)? cast(R)0 : 3300 R.nan; 3301 } 3302 3303 return ret; 3304 } 3305 else 3306 { 3307 static if (op.among!("*", "/")) 3308 { 3309 int wexp = 3310 (exp > int.max)? int.max : 3311 (cast(long)exp < -int.max)? -int.max : cast(int)exp; 3312 static if (op == "/") 3313 wexp = -wexp; 3314 3315 return ldexp(cast(R)left, wexp); 3316 } 3317 else 3318 { 3319 int expL; 3320 real mantL = frexp(left, expL); 3321 3322 static if (!isSigned!M) 3323 const retL = expL <= exp; 3324 else 3325 const retL = (expL < 0) || (expL <= exp); 3326 3327 R ret = void; 3328 if (retL) 3329 ret = left; 3330 else 3331 { 3332 const expDiff = expL - exp; 3333 ret = (expDiff > N.mant_dig)? 3334 cast(R)0 : 3335 left - ldexp(floor(ldexp(mantissa, expDiff)), expL - expDiff); 3336 } 3337 3338 return ret; 3339 } 3340 } 3341 } 3342 auto byPow2Impl(string op, IntFlagPolicy policy, N, M)(const N left, const M exp) @safe 3343 if (op.among!("*", "/", "%") && isIntegral!N && isIntegral!M) 3344 { 3345 alias R = Select!(op.among!("*", "/") != 0, Promoted!N, N); 3346 enum Unsigned!M maxSh = 8 * N.sizeof - 1; 3347 3348 R ret = void; 3349 static if (op.among!("*", "/")) 3350 { 3351 const rc = cast(R)left; 3352 const negE = exp < 0; 3353 const absE = cast(Unsigned!M)(negE? 3354 -exp : 3355 exp); 3356 const bigSh = (absE > maxSh); 3357 3358 R back = void; 3359 if ((op == "*")? negE : !negE) 3360 { 3361 if (bigSh) 3362 ret = 0; 3363 else 3364 { 3365 // ">>" rounds as floor(), but we want trunc() like "/" 3366 ret = (rc < 0)? 3367 -(-rc >>> absE) : 3368 rc >>> absE; 3369 } 3370 } 3371 else 3372 { 3373 if (bigSh) 3374 { 3375 ret = 0; 3376 back = 0; 3377 } 3378 else 3379 { 3380 ret = rc << absE; 3381 back = ret >> absE; 3382 } 3383 3384 if (back != rc) 3385 IntFlag.over.raise!policy(); 3386 } 3387 } 3388 else 3389 { 3390 if (exp & ~maxSh) 3391 ret = (exp < 0)? 0 : left; 3392 else 3393 { 3394 const mask = ~(~cast(N)0 << exp); 3395 ret = cast(R)(left < 0? 3396 -(-left & mask) : 3397 left & mask); 3398 } 3399 } 3400 3401 return ret; 3402 } 3403 /+}+/ 3404 3405 struct PowOut(B) 3406 { 3407 B num; 3408 IntFlag flag; 3409 } 3410 3411 // Minimize template bloat by using a common pow() implementation 3412 PowOut!B powImpl(B, E)(const B base, const E exp) @safe 3413 if ((is(B == int) || is(B == uint) || is(B == long) || is(B == ulong)) && 3414 (is(E == long) || is(E == ulong))) 3415 { 3416 static if (__VERSION__ >= 2068) pragma(inline, false); 3417 PowOut!B ret; 3418 3419 static if (isSigned!B) 3420 { 3421 alias cmul = muls; 3422 const smallB = (1 >= base && base >= -1); 3423 } 3424 else 3425 { 3426 alias cmul = mulu; 3427 const smallB = (base <= 1); 3428 } 3429 3430 if (smallB) 3431 { 3432 if (base == 0) 3433 { 3434 static if (isSigned!E) 3435 { 3436 if (exp < 0) 3437 ret.flag = IntFlag.div0; 3438 } 3439 3440 ret.num = (exp == 0); 3441 } 3442 else 3443 ret.num = (exp & 0x1)? base : 1; 3444 3445 return ret; 3446 } 3447 if (exp <= 0) 3448 { 3449 ret.num = (exp == 0); 3450 return ret; 3451 } 3452 3453 ret.num = 1; 3454 if (exp <= precision!B) 3455 { 3456 B b = base; 3457 int e = cast(int)exp; 3458 if (e & 0x1) 3459 ret.num = b; 3460 e >>>= 1; 3461 3462 bool over = false; 3463 while (e != 0) 3464 { 3465 b = cmul(b, b, over); 3466 if (e & 0x1) 3467 ret.num = cmul(ret.num, b, over); 3468 3469 e >>>= 1; 3470 } 3471 3472 if (!over) 3473 return ret; 3474 } 3475 3476 ret.flag = (base < 0 && (exp & 0x1))? 3477 IntFlag.negOver : 3478 IntFlag.posOver; 3479 return ret; 3480 } 3481 }