| @safe unittest |
| { |
| import std.checkedint; |
| |
| int[] concatAndAdd(int[] a, int[] b, int offset) |
| { |
| // Aborts on overflow on size computation |
| auto r = new int[(checked(a.length) + b.length).get]; |
| // Aborts on overflow on element computation |
| foreach (i; 0 .. a.length) |
| r[i] = (a[i] + checked(offset)).get; |
| foreach (i; 0 .. b.length) |
| r[i + a.length] = (b[i] + checked(offset)).get; |
| return r; |
| } |
| assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = (cast(byte) 127).checked!Saturate; |
| assert(x == 127); |
| x++; |
| assert(x == 127); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = 100.checked!WithNaN; |
| assert(x == 100); |
| x /= 0; |
| assert(x.isNaN); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| uint x = 1; |
| auto y = x.checked!ProperCompare; |
| assert(x < -1); // built-in comparison |
| assert(y > -1); // ProperCompare |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| import std.exception : assertThrown; |
| auto x = -1.checked!Throw; |
| assertThrown(x / 0); |
| assertThrown(x + int.min); |
| assertThrown(x == uint.max); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = checked(ubyte(42)); |
| static assert(is(typeof(x.get()) == ubyte)); |
| assert(x.get == 42); |
| const y = checked(ubyte(42)); |
| static assert(is(typeof(y.get()) == const ubyte)); |
| assert(y.get == 42); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| assert(Checked!short.min == -32768); |
| assert(Checked!(short, WithNaN).min == -32767); |
| assert(Checked!(uint, WithNaN).max == uint.max - 1); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto a = checked(42L); |
| assert(a == 42); |
| auto b = Checked!long(4242); // convert 4242 to long |
| assert(b == 4242); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| Checked!long a; |
| a = 42L; |
| assert(a == 42); |
| a = 4242; |
| assert(a == 4242); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| Checked!long a, b; |
| a = b = 3; |
| assert(a == 3 && b == 3); |
| |
| } |
| |
| @system unittest |
| { |
| import std.checkedint; |
| |
| import std.conv : to; |
| |
| const a = to!long("1234"); |
| const b = to!(Checked!long)("1234"); |
| assert(a == b); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| assert(cast(uint) checked(42) == 42); |
| assert(cast(uint) checked!WithNaN(-42) == uint.max); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| import std.traits : isUnsigned; |
| |
| static struct MyHook |
| { |
| static bool thereWereErrors; |
| static bool hookOpEquals(L, R)(L lhs, R rhs) |
| { |
| if (lhs != rhs) return false; |
| static if (isUnsigned!L && !isUnsigned!R) |
| { |
| if (lhs > 0 && rhs < 0) thereWereErrors = true; |
| } |
| else static if (isUnsigned!R && !isUnsigned!L) |
| if (lhs < 0 && rhs > 0) thereWereErrors = true; |
| // Preserve built-in behavior. |
| return true; |
| } |
| } |
| auto a = checked!MyHook(-42); |
| assert(a == uint(-42)); |
| assert(MyHook.thereWereErrors); |
| MyHook.thereWereErrors = false; |
| assert(checked!MyHook(uint(-42)) == -42); |
| assert(MyHook.thereWereErrors); |
| static struct MyHook2 |
| { |
| static bool hookOpEquals(L, R)(L lhs, R rhs) |
| { |
| return lhs == rhs; |
| } |
| } |
| MyHook.thereWereErrors = false; |
| assert(checked!MyHook2(uint(-42)) == a); |
| // Hook on left hand side takes precedence, so no errors |
| assert(!MyHook.thereWereErrors); |
| |
| } |
| |
| @system unittest |
| { |
| import std.checkedint; |
| |
| import std.format; |
| |
| assert(format("%04d", checked(15)) == "0015"); |
| assert(format("0x%02x", checked(15)) == "0x0f"); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| import std.traits : isUnsigned; |
| |
| static struct MyHook |
| { |
| static bool thereWereErrors; |
| static int hookOpCmp(L, R)(L lhs, R rhs) |
| { |
| static if (isUnsigned!L && !isUnsigned!R) |
| { |
| if (rhs < 0 && rhs >= lhs) |
| thereWereErrors = true; |
| } |
| else static if (isUnsigned!R && !isUnsigned!L) |
| { |
| if (lhs < 0 && lhs >= rhs) |
| thereWereErrors = true; |
| } |
| // Preserve built-in behavior. |
| return lhs < rhs ? -1 : lhs > rhs; |
| } |
| } |
| auto a = checked!MyHook(-42); |
| assert(a > uint(42)); |
| assert(MyHook.thereWereErrors); |
| static struct MyHook2 |
| { |
| static int hookOpCmp(L, R)(L lhs, R rhs) |
| { |
| // Default behavior |
| return lhs < rhs ? -1 : lhs > rhs; |
| } |
| } |
| MyHook.thereWereErrors = false; |
| assert(Checked!(uint, MyHook2)(uint(-42)) <= a); |
| //assert(Checked!(uint, MyHook2)(uint(-42)) >= a); |
| // Hook on left hand side takes precedence, so no errors |
| assert(!MyHook.thereWereErrors); |
| assert(a <= Checked!(uint, MyHook2)(uint(-42))); |
| assert(MyHook.thereWereErrors); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| static struct MyHook |
| { |
| static bool thereWereErrors; |
| static L hookOpUnary(string x, L)(L lhs) |
| { |
| if (x == "-" && lhs == -lhs) thereWereErrors = true; |
| return -lhs; |
| } |
| } |
| auto a = checked!MyHook(long.min); |
| assert(a == -a); |
| assert(MyHook.thereWereErrors); |
| auto b = checked!void(42); |
| assert(++b == 43); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| static struct MyHook |
| { |
| static bool thereWereErrors; |
| static T onLowerBound(Rhs, T)(Rhs rhs, T bound) |
| { |
| thereWereErrors = true; |
| return bound; |
| } |
| static T onUpperBound(Rhs, T)(Rhs rhs, T bound) |
| { |
| thereWereErrors = true; |
| return bound; |
| } |
| } |
| auto x = checked!MyHook(byte.min); |
| x -= 1; |
| assert(MyHook.thereWereErrors); |
| MyHook.thereWereErrors = false; |
| x = byte.max; |
| x += 1; |
| assert(MyHook.thereWereErrors); |
| |
| } |
| |
| @safe @nogc pure nothrow unittest |
| { |
| import std.checkedint; |
| |
| // Hook that ignores all problems. |
| static struct Ignore |
| { |
| @nogc nothrow pure @safe static: |
| Dst onBadCast(Dst, Src)(Src src) { return cast(Dst) src; } |
| Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; } |
| T onUpperBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; } |
| bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return lhs == rhs; } |
| int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return (lhs > rhs) - (lhs < rhs); } |
| typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) { return mixin(x ~ "lhs"); } |
| typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) |
| { |
| static if (x == "/") |
| return typeof(lhs / rhs).min; |
| else |
| return mixin("lhs" ~ x ~ "rhs"); |
| } |
| } |
| |
| auto x = Checked!(int, Ignore)(5) + 7; |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| static assert(is(typeof(checked(42)) == Checked!int)); |
| assert(checked(42) == Checked!int(42)); |
| static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN))); |
| assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42)); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| void test(T)() |
| { |
| Checked!(int, Abort) x; |
| x = 42; |
| auto x1 = cast(T) x; |
| assert(x1 == 42); |
| //x1 += long(int.max); |
| } |
| test!short; |
| test!(const short); |
| test!(immutable short); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| void test(T)() |
| { |
| Checked!(int, Throw) x; |
| x = 42; |
| auto x1 = cast(T) x; |
| assert(x1 == 42); |
| x = T.max + 1; |
| import std.exception : assertThrown, assertNotThrown; |
| assertThrown(cast(T) x); |
| x = x.max; |
| assertThrown(x += 42); |
| assertThrown(x += 42L); |
| x = x.min; |
| assertThrown(-x); |
| assertThrown(x -= 42); |
| assertThrown(x -= 42L); |
| x = -1; |
| assertNotThrown(x == -1); |
| assertThrown(x == uint(-1)); |
| assertNotThrown(x <= -1); |
| assertThrown(x <= uint(-1)); |
| } |
| test!short; |
| test!(const short); |
| test!(immutable short); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = checked!Warn(-42); |
| // Passes |
| assert(x == -42); |
| // Passes but prints a warning |
| // assert(x == uint(-42)); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = checked!Warn(-42); |
| // Passes |
| assert(x <= -42); |
| // Passes but prints a warning |
| // assert(x <= uint(-42)); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = checked!Warn(42); |
| short x1 = cast(short) x; |
| //x += long(int.max); |
| auto y = checked!Warn(cast(const int) 42); |
| short y1 = cast(const byte) y; |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| alias opEqualsProper = ProperCompare.hookOpEquals; |
| assert(opEqualsProper(42, 42)); |
| assert(opEqualsProper(42.0, 42.0)); |
| assert(opEqualsProper(42u, 42)); |
| assert(opEqualsProper(42, 42u)); |
| assert(-1 == 4294967295u); |
| assert(!opEqualsProper(-1, 4294967295u)); |
| assert(!opEqualsProper(const uint(-1), -1)); |
| assert(!opEqualsProper(uint(-1), -1.0)); |
| assert(3_000_000_000U == -1_294_967_296); |
| assert(!opEqualsProper(3_000_000_000U, -1_294_967_296)); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = checked!WithNaN(422); |
| assert((cast(ubyte) x) == 255); |
| x = checked!WithNaN(-422); |
| assert((cast(byte) x) == -128); |
| assert(cast(short) x == -422); |
| assert(cast(bool) x); |
| x = x.init; // set back to NaN |
| assert(x != true); |
| assert(x != false); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| Checked!(int, WithNaN) x; |
| assert(!(x < 0) && !(x > 0) && !(x == 0)); |
| x = 1; |
| assert(x > 0 && !(x < 0) && !(x == 0)); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| Checked!(int, WithNaN) x; |
| ++x; |
| assert(x.isNaN); |
| x = 1; |
| assert(!x.isNaN); |
| x = -x; |
| ++x; |
| assert(!x.isNaN); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| Checked!(int, WithNaN) x; |
| assert((x + 1).isNaN); |
| x = 100; |
| assert(!(x + 1).isNaN); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| Checked!(int, WithNaN) x; |
| assert((1 + x).isNaN); |
| x = 100; |
| assert(!(1 + x).isNaN); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| Checked!(int, WithNaN) x; |
| x += 4; |
| assert(x.isNaN); |
| x = 0; |
| x += 4; |
| assert(!x.isNaN); |
| x += int.max; |
| assert(x.isNaN); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x1 = Checked!(int, WithNaN)(); |
| assert(x1.isNaN); |
| assert(x1.get == int.min); |
| assert(x1 != x1); |
| assert(!(x1 < x1)); |
| assert(!(x1 > x1)); |
| assert(!(x1 == x1)); |
| ++x1; |
| assert(x1.isNaN); |
| assert(x1.get == int.min); |
| --x1; |
| assert(x1.isNaN); |
| assert(x1.get == int.min); |
| x1 = 42; |
| assert(!x1.isNaN); |
| assert(x1 == x1); |
| assert(x1 <= x1); |
| assert(x1 >= x1); |
| static assert(x1.min == int.min + 1); |
| x1 += long(int.max); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x1 = Checked!(int, WithNaN)(); |
| assert(x1.isNaN); |
| x1 = 1; |
| assert(!x1.isNaN); |
| x1 = x1.init; |
| assert(x1.isNaN); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = checked!Saturate(short(100)); |
| x += 33000; |
| assert(x == short.max); |
| x -= 70000; |
| assert(x == short.min); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| assert(checked!Saturate(int.max) + 1 == int.max); |
| assert(checked!Saturate(100) ^^ 10 == int.max); |
| assert(checked!Saturate(-100) ^^ 10 == int.max); |
| assert(checked!Saturate(100) / 0 == int.max); |
| assert(checked!Saturate(100) << -1 == 0); |
| assert(checked!Saturate(100) << 33 == int.max); |
| assert(checked!Saturate(100) >> -1 == int.max); |
| assert(checked!Saturate(100) >> 33 == 0); |
| |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| auto x = checked!Saturate(int.max); |
| ++x; |
| assert(x == int.max); |
| --x; |
| assert(x == int.max - 1); |
| x = int.min; |
| assert(-x == int.max); |
| x -= 42; |
| assert(x == int.min); |
| assert(x * -2 == int.max); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| bool overflow; |
| assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow); |
| assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow); |
| assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow); |
| assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow); |
| assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| bool overflow; |
| assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow); |
| assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow); |
| assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow); |
| assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow); |
| } |
| |
| @safe unittest |
| { |
| import std.checkedint; |
| |
| struct MyHook |
| { |
| static size_t hookToHash(T)(const T payload) nothrow @trusted |
| { |
| return .hashOf(payload); |
| } |
| } |
| |
| int[Checked!(int, MyHook)] aa; |
| Checked!(int, MyHook) var = 42; |
| aa[var] = 100; |
| |
| assert(aa[var] == 100); |
| |
| int[Checked!(int, Abort)] bb; |
| Checked!(int, Abort) var2 = 42; |
| bb[var2] = 100; |
| |
| assert(bb[var2] == 100); |
| } |
| |