commit c9fb75f584944c9e4a258fa1c2cffe2f3991681d
parent 6e12e7dcc81f80a405fa5018b7ff572141cc4c61
Author: Sebastian <sebastian@sebsite.pw>
Date: Tue, 18 Jul 2023 18:56:08 -0400
math::checked: initial commit
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
4 files changed, 419 insertions(+), 0 deletions(-)
diff --git a/math/checked/README b/math/checked/README
@@ -0,0 +1,2 @@
+math::checked provides safe integer arithmetic functions that check for
+overflow.
diff --git a/math/checked/checked.ha b/math/checked/checked.ha
@@ -0,0 +1,377 @@
+use math;
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addi8(a: i8, b: i8) (i8, bool) = {
+ const res = a + b;
+ const overflow = a < 0 == b < 0 && a < 0 != res < 0;
+ return (res, overflow);
+};
+
+@test fn addi8() void = {
+ const (res, overflow) = addi8(100, 20);
+ assert(res == 120);
+ assert(!overflow);
+ const (res, overflow) = addi8(100, 50);
+ assert(res == -106);
+ assert(overflow);
+};
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addi16(a: i16, b: i16) (i16, bool) = {
+ const res = a + b;
+ const overflow = a < 0 == b < 0 && a < 0 != res < 0;
+ return (res, overflow);
+};
+
+@test fn addi16() void = {
+ const (res, overflow) = addi16(32700, 60);
+ assert(res == 32760);
+ assert(!overflow);
+ const (res, overflow) = addi16(32700, 100);
+ assert(res == -32736);
+ assert(overflow);
+};
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addi32(a: i32, b: i32) (i32, bool) = {
+ const res = a + b;
+ const overflow = a < 0 == b < 0 && a < 0 != res < 0;
+ return (res, overflow);
+};
+
+@test fn addi32() void = {
+ const (res, overflow) = addi32(2147483600, 40);
+ assert(res == 2147483640);
+ assert(!overflow);
+ const (res, overflow) = addi32(2147483600, 100);
+ assert(res == -2147483596);
+ assert(overflow);
+};
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addi64(a: i64, b: i64) (i64, bool) = {
+ const res = a + b;
+ const overflow = a < 0 == b < 0 && a < 0 != res < 0;
+ return (res, overflow);
+};
+
+@test fn addi64() void = {
+ const (res, overflow) = addi64(9223372036854775800, 5);
+ assert(res == 9223372036854775805);
+ assert(!overflow);
+ const (res, overflow) = addi64(9223372036854775800, 10);
+ assert(res == -9223372036854775806);
+ assert(overflow);
+};
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addu8(a: u8, b: u8) (u8, bool) = {
+ const res = a + b;
+ const overflow = res < a;
+ return (res, overflow);
+};
+
+@test fn addu8() void = {
+ const (res, overflow) = addu8(200, 50);
+ assert(res == 250);
+ assert(!overflow);
+ const (res, overflow) = addu8(200, 100);
+ assert(res == 44);
+ assert(overflow);
+};
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addu16(a: u16, b: u16) (u16, bool) = {
+ const res = a + b;
+ const overflow = res < a;
+ return (res, overflow);
+};
+
+@test fn addu16() void = {
+ const (res, overflow) = addu16(65500, 30);
+ assert(res == 65530);
+ assert(!overflow);
+ const (res, overflow) = addu16(65500, 50);
+ assert(res == 14);
+ assert(overflow);
+};
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addu32(a: u32, b: u32) (u32, bool) = {
+ const res = a + b;
+ const overflow = res < a;
+ return (res, overflow);
+};
+
+@test fn addu32() void = {
+ const (res, overflow) = addu32(4294967200, 90);
+ assert(res == 4294967290);
+ assert(!overflow);
+ const (res, overflow) = addu32(4294967200, 100);
+ assert(res == 4);
+ assert(overflow);
+};
+
+// Adds 'a' and 'b', returning the result and whether overflow occurred.
+export fn addu64(a: u64, b: u64) (u64, bool) = {
+ const res = a + b;
+ const overflow = res < a;
+ return (res, overflow);
+};
+
+@test fn addu64() void = {
+ const (res, overflow) = addu64(18446744073709551600, 10);
+ assert(res == 18446744073709551610);
+ assert(!overflow);
+ const (res, overflow) = addu64(18446744073709551610, 50);
+ assert(res == 44);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subi8(a: i8, b: i8) (i8, bool) = addi8(a, -b);
+
+@test fn subi8() void = {
+ const (res, overflow) = subi8(-100, 20);
+ assert(res == -120);
+ assert(!overflow);
+ const (res, overflow) = subi8(-100, 50);
+ assert(res == 106);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subi16(a: i16, b: i16) (i16, bool) = addi16(a, -b);
+
+@test fn subi16() void = {
+ const (res, overflow) = subi16(-32700, 60);
+ assert(res == -32760);
+ assert(!overflow);
+ const (res, overflow) = subi16(-32700, 100);
+ assert(res == 32736);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subi32(a: i32, b: i32) (i32, bool) = addi32(a, -b);
+
+@test fn subi32() void = {
+ const (res, overflow) = subi32(-2147483600, 40);
+ assert(res == -2147483640);
+ assert(!overflow);
+ const (res, overflow) = subi32(-2147483600, 100);
+ assert(res == 2147483596);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subi64(a: i64, b: i64) (i64, bool) = addi64(a, -b);
+
+@test fn subi64() void = {
+ const (res, overflow) = subi64(-9223372036854775800, 5);
+ assert(res == -9223372036854775805);
+ assert(!overflow);
+ const (res, overflow) = subi64(-9223372036854775800, 10);
+ assert(res == 9223372036854775806);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subu8(a: u8, b: u8) (u8, bool) = {
+ const res = a - b;
+ const overflow = res > a;
+ return (res, overflow);
+};
+
+@test fn subu8() void = {
+ const (res, overflow) = subu8(250, 50);
+ assert(res == 200);
+ assert(!overflow);
+ const (res, overflow) = subu8(44, 100);
+ assert(res == 200);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subu16(a: u16, b: u16) (u16, bool) = {
+ const res = a - b;
+ const overflow = res > a;
+ return (res, overflow);
+};
+
+@test fn subu16() void = {
+ const (res, overflow) = subu16(65530, 30);
+ assert(res == 65500);
+ assert(!overflow);
+ const (res, overflow) = subu16(14, 50);
+ assert(res == 65500);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subu32(a: u32, b: u32) (u32, bool) = {
+ const res = a - b;
+ const overflow = res > a;
+ return (res, overflow);
+};
+
+@test fn subu32() void = {
+ const (res, overflow) = subu32(4294967290, 90);
+ assert(res == 4294967200);
+ assert(!overflow);
+ const (res, overflow) = subu32(4, 100);
+ assert(res == 4294967200);
+ assert(overflow);
+};
+
+// Subtracts 'b' from 'a', returning the result and whether overflow occurred.
+export fn subu64(a: u64, b: u64) (u64, bool) = {
+ const res = a - b;
+ const overflow = res > a;
+ return (res, overflow);
+};
+
+@test fn subu64() void = {
+ const (res, overflow) = subu64(18446744073709551610, 10);
+ assert(res == 18446744073709551600);
+ assert(!overflow);
+ const (res, overflow) = subu64(44, 50);
+ assert(res == 18446744073709551610);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn muli8(a: i8, b: i8) (i8, bool) = {
+ const fullres = a: i16 * b: i16;
+ const res = fullres: i8;
+ const overflow = res != fullres;
+ return (res, overflow);
+};
+
+@test fn muli8() void = {
+ const (res, overflow) = muli8(11, 11);
+ assert(res == 121);
+ assert(!overflow);
+ const (res, overflow) = muli8(12, 12);
+ assert(res == -112);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn muli16(a: i16, b: i16) (i16, bool) = {
+ const fullres = a: i32 * b: i32;
+ const res = fullres: i16;
+ const overflow = res != fullres;
+ return (res, overflow);
+};
+
+@test fn muli16() void = {
+ const (res, overflow) = muli16(181, 181);
+ assert(res == 32761);
+ assert(!overflow);
+ const (res, overflow) = muli16(182, 182);
+ assert(res == -32412);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn muli32(a: i32, b: i32) (i32, bool) = {
+ const fullres = a: i64 * b: i64;
+ const res = fullres: i32;
+ const overflow = res != fullres;
+ return (res, overflow);
+};
+
+@test fn muli32() void = {
+ const (res, overflow) = muli32(46340, 46340);
+ assert(res == 2147395600);
+ assert(!overflow);
+ const (res, overflow) = muli32(46341, 46341);
+ assert(res == -2147479015);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn muli64(a: i64, b: i64) (i64, bool) = {
+ const (hi, lo) = math::mulu64(math::absi64(a), math::absi64(b));
+ const res = a * b;
+ const overflow = hi != 0 || lo & (1 << 63) != 0;
+ return (res, overflow);
+};
+
+@test fn muli64() void = {
+ const (res, overflow) = muli64(3037000499, 3037000499);
+ assert(res == 9223372030926249001);
+ assert(!overflow);
+ const (res, overflow) = muli64(3037000500, 3037000500);
+ assert(res == -9223372036709301616);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn mulu8(a: u8, b: u8) (u8, bool) = {
+ const fullres = a: u16 * b: u16;
+ const res = fullres: u8;
+ const overflow = res != fullres;
+ return (res, overflow);
+};
+
+@test fn mulu8() void = {
+ const (res, overflow) = mulu8(15, 15);
+ assert(res == 225);
+ assert(!overflow);
+ const (res, overflow) = mulu8(16, 16);
+ assert(res == 0);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn mulu16(a: u16, b: u16) (u16, bool) = {
+ const fullres = a: u32 * b: u32;
+ const res = fullres: u16;
+ const overflow = res != fullres;
+ return (res, overflow);
+};
+
+@test fn mulu16() void = {
+ const (res, overflow) = mulu16(255, 255);
+ assert(res == 65025);
+ assert(!overflow);
+ const (res, overflow) = mulu16(256, 256);
+ assert(res == 0);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn mulu32(a: u32, b: u32) (u32, bool) = {
+ const fullres = a: u64 * b: u64;
+ const res = fullres: u32;
+ const overflow = res != fullres;
+ return (res, overflow);
+};
+
+@test fn mulu32() void = {
+ const (res, overflow) = mulu32(65535, 65535);
+ assert(res == 4294836225);
+ assert(!overflow);
+ const (res, overflow) = mulu32(65536, 65536);
+ assert(res == 0);
+ assert(overflow);
+};
+
+// Multiplies 'a' and 'b' returning the result and whether overflow occurred.
+export fn mulu64(a: u64, b: u64) (u64, bool) = {
+ const (hi, lo) = math::mulu64(a, b);
+ const res = lo;
+ const overflow = hi != 0;
+ return (res, overflow);
+};
+
+@test fn mulu64() void = {
+ const (res, overflow) = mulu64(4294967295, 4294967295);
+ assert(res == 18446744065119617025);
+ assert(!overflow);
+ const (res, overflow) = mulu64(4294967296, 4294967296);
+ assert(res == 0);
+ assert(overflow);
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -981,6 +981,11 @@ math() {
gen_ssa math types rt
}
+math_checked() {
+ gen_srcs math::checked checked.ha
+ gen_ssa math::checked math
+}
+
mime() {
# This module is not built by default because gen-stdlib does not do a good
# job of resolving @init dependency ordering issues
@@ -1633,6 +1638,7 @@ linux::timerfd linux
linux::vdso linux
log linux freebsd
math
+math::checked
math::complex
math::random
net linux freebsd
diff --git a/stdlib.mk b/stdlib.mk
@@ -589,6 +589,13 @@ stdlib_deps_any += $(stdlib_math_any)
stdlib_math_linux = $(stdlib_math_any)
stdlib_math_freebsd = $(stdlib_math_any)
+# gen_lib math::checked (any)
+stdlib_math_checked_any = $(HARECACHE)/math/checked/math_checked-any.o
+stdlib_env += HARE_TD_math::checked=$(HARECACHE)/math/checked/math_checked.td
+stdlib_deps_any += $(stdlib_math_checked_any)
+stdlib_math_checked_linux = $(stdlib_math_checked_any)
+stdlib_math_checked_freebsd = $(stdlib_math_checked_any)
+
# gen_lib math::complex (any)
stdlib_math_complex_any = $(HARECACHE)/math/complex/math_complex-any.o
stdlib_env += HARE_TD_math::complex=$(HARECACHE)/math/complex/math_complex.td
@@ -1702,6 +1709,16 @@ $(HARECACHE)/math/math-any.ssa: $(stdlib_math_any_srcs) $(stdlib_rt) $(stdlib_ty
@$(stdlib_env) $(HAREC) $(HAREFLAGS) -o $@ -Nmath \
-t$(HARECACHE)/math/math.td $(stdlib_math_any_srcs)
+# math::checked (+any)
+stdlib_math_checked_any_srcs = \
+ $(STDLIB)/math/checked/checked.ha
+
+$(HARECACHE)/math/checked/math_checked-any.ssa: $(stdlib_math_checked_any_srcs) $(stdlib_rt) $(stdlib_math_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/math/checked
+ @$(stdlib_env) $(HAREC) $(HAREFLAGS) -o $@ -Nmath::checked \
+ -t$(HARECACHE)/math/checked/math_checked.td $(stdlib_math_checked_any_srcs)
+
# math::complex (+any)
stdlib_math_complex_any_srcs = \
$(STDLIB)/math/complex/complex.ha
@@ -2997,6 +3014,13 @@ testlib_deps_any += $(testlib_math_any)
testlib_math_linux = $(testlib_math_any)
testlib_math_freebsd = $(testlib_math_any)
+# gen_lib math::checked (any)
+testlib_math_checked_any = $(TESTCACHE)/math/checked/math_checked-any.o
+testlib_env += HARE_TD_math::checked=$(TESTCACHE)/math/checked/math_checked.td
+testlib_deps_any += $(testlib_math_checked_any)
+testlib_math_checked_linux = $(testlib_math_checked_any)
+testlib_math_checked_freebsd = $(testlib_math_checked_any)
+
# gen_lib math::complex (any)
testlib_math_complex_any = $(TESTCACHE)/math/complex/math_complex-any.o
testlib_env += HARE_TD_math::complex=$(TESTCACHE)/math/complex/math_complex.td
@@ -4163,6 +4187,16 @@ $(TESTCACHE)/math/math-any.ssa: $(testlib_math_any_srcs) $(testlib_rt) $(testlib
@$(testlib_env) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nmath \
-t$(TESTCACHE)/math/math.td $(testlib_math_any_srcs)
+# math::checked (+any)
+testlib_math_checked_any_srcs = \
+ $(STDLIB)/math/checked/checked.ha
+
+$(TESTCACHE)/math/checked/math_checked-any.ssa: $(testlib_math_checked_any_srcs) $(testlib_rt) $(testlib_math_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/math/checked
+ @$(testlib_env) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nmath::checked \
+ -t$(TESTCACHE)/math/checked/math_checked.td $(testlib_math_checked_any_srcs)
+
# math::complex (+any)
testlib_math_complex_any_srcs = \
$(STDLIB)/math/complex/complex.ha \