| @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); | |
| } | |