Add safemath pkg for checked and saturated math

This commit is contained in:
smcio 2023-06-27 13:15:00 +00:00
parent 479b7393cf
commit becec44c5c
3 changed files with 893 additions and 0 deletions

172
pkg/safemath/checked.go Normal file
View File

@ -0,0 +1,172 @@
// The safemath package implements helper functions for safe integer arithmetic.
//
// This file implements 'checked' integer arithmetic for guarding against integer
// overflows and division-by-zero errors.
package safemath
import (
"errors"
"math/bits"
)
var (
ErrOverflowAdd = errors.New("integer overflow in addition")
ErrOverflowMul = errors.New("integer overflow in multiplication")
ErrOverflowSub = errors.New("integer overflow in subtraction")
ErrDivByZero = errors.New("divide by zero")
)
// CheckedAddU8 adds two uint8's together, returning an error
// in the event of an overflow.
func CheckedAddU8(a, b uint8) (uint8, error) {
result := a + b
if result < a {
return 0, ErrOverflowAdd
}
return result, nil
}
// CheckedMulU8 multiplies two uint8's together, returning an error
// in the event of an overflow.
func CheckedMulU8(a, b uint8) (uint8, error) {
result := a * b
if result < a {
return 0, ErrOverflowMul
}
return result, nil
}
// CheckedSubU8 computes `a - b` for two uint8's returning an error in the event
// that a is smaller than b
func CheckedSubU8(a, b uint8) (uint8, error) {
if a < b {
return 0, ErrOverflowSub
}
return a - b, nil
}
// CheckedDivU8 computes `a / b` for two uint8's, returning an error in the event
// that b is 0
func CheckedDivU8(a, b uint8) (uint8, error) {
if b == 0 {
return 0, ErrDivByZero
}
return a / b, nil
}
// CheckedAddU16 adds two uint16's together, returning an error in the event
// of an overflow.
func CheckedAddU16(a, b uint16) (uint16, error) {
result := a + b
if result < a {
return 0, ErrOverflowAdd
}
return result, nil
}
// CheckedMulU16 multiplies two uint16's together, returning an error in the event
// of an overflow.
func CheckedMulU16(a, b uint16) (uint16, error) {
result := a * b
if result < a {
return 0, ErrOverflowMul
}
return result, nil
}
// CheckedSubU16 computes `a - b` for two uint16's, returning an error in the event
// that a is smaller than b
func CheckedSubU16(a, b uint16) (uint16, error) {
if a < b {
return 0, ErrOverflowSub
}
return a - b, nil
}
// CheckedDivU16 computes `a / b` for two uint16's, returning an error in the event
// that b is 0
func CheckedDivU16(a, b uint16) (uint16, error) {
if b == 0 {
return 0, ErrDivByZero
}
return a / b, nil
}
// CheckedAddU32 adds two uint32's together, returning an error in the event of an overflow.
func CheckedAddU32(a, b uint32) (uint32, error) {
sum, carryOut := bits.Add32(a, b, 0)
if carryOut == 1 {
return 0, ErrOverflowAdd
}
return sum, nil
}
// CheckedMulU32 multiplies two uint32's together, returning an error in the event
// of an overflow.
func CheckedMulU32(a, b uint32) (uint32, error) {
hi, lo := bits.Mul32(a, b)
if hi > 0 {
return 0, ErrOverflowMul
}
return lo, nil
}
// CheckedSubU32 computes `a - b` for two uint32's, returning an error in the event that
// a is smaller than b
func CheckedSubU32(a, b uint32) (uint32, error) {
result, borrow := bits.Sub32(a, b, 0)
if borrow == 1 {
return 0, ErrOverflowSub
}
return result, nil
}
// CheckedDivU32 computes `a / b` for two uint32's, returning an error in the event
// that b is 0
func CheckedDivU32(a, b uint32) (uint32, error) {
if b == 0 {
return 0, ErrDivByZero
}
result, _ := bits.Div32(0, a, b)
return result, nil
}
// CheckedAddU64 adds two uint64's together, returning an error in the event of an overflow.
func CheckedAddU64(a, b uint64) (uint64, error) {
sum, carryOut := bits.Add64(a, b, 0)
if carryOut == 1 {
return 0, ErrOverflowAdd
}
return sum, nil
}
// CheckedMulU64 multiplies two uint64's together, returning an error in the event
// of an overflow.
func CheckedMulU64(a, b uint64) (uint64, error) {
hi, lo := bits.Mul64(a, b)
if hi > 0 {
return 0, ErrOverflowMul
}
return lo, nil
}
// CheckedSubU64 computes `a - b` for two uint64's, returning an error in the event
// that a is smaller than b
func CheckedSubU64(a, b uint64) (uint64, error) {
result, borrow := bits.Sub64(a, b, 0)
if borrow == 1 {
return 0, ErrOverflowSub
}
return result, nil
}
// CheckedDivU64 computes `a / b` for two uint64's, returning an error in the event
// that b is 0
func CheckedDivU64(a, b uint64) (uint64, error) {
if b == 0 {
return 0, ErrDivByZero
}
result, _ := bits.Div64(0, a, b)
return result, nil
}

View File

@ -0,0 +1,592 @@
package safemath
import (
"math"
"math/rand"
"testing"
)
func TestCheckedAddU8_Overflow(t *testing.T) {
var a uint8 = 0xff
var b uint8 = 2
_, err := CheckedAddU8(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d + %d", a, b)
}
}
func TestCheckedAddU8_NoOverflow(t *testing.T) {
var a uint8 = 0xf0
var b uint8 = 2
result, err := CheckedAddU8(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d + %d", a, b)
}
if result != (a + b) {
t.Errorf("wrong result for %d + %d", a, b)
}
}
func TestCheckedAddU16_Overflow(t *testing.T) {
var a uint16 = math.MaxUint16
var b uint16 = 2
_, err := CheckedAddU16(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d + %d", a, b)
}
}
func TestCheckedAddU16_NoOverflow(t *testing.T) {
var a uint16 = 100
var b uint16 = 2
result, err := CheckedAddU16(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d + %d", a, b)
}
if result != (a + b) {
t.Errorf("wrong result for %d + %d", a, b)
}
}
func TestCheckedAddU32_Overflow(t *testing.T) {
var a uint32 = math.MaxUint32
var b uint32 = 2
_, err := CheckedAddU32(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d + %d", a, b)
}
}
func TestCheckedAddU32_NoOverflow(t *testing.T) {
var a uint32 = 100
var b uint32 = 2
result, err := CheckedAddU32(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d + %d", a, b)
}
if result != (a + b) {
t.Errorf("wrong result for %d + %d", a, b)
}
}
func TestCheckedAddU64_Overflow(t *testing.T) {
var a uint64 = math.MaxUint64
var b uint64 = 2
_, err := CheckedAddU64(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d + %d", a, b)
}
}
func TestCheckedAddU64_NoOverflow(t *testing.T) {
var a uint64 = 100
var b uint64 = 2
result, err := CheckedAddU64(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d + %d", a, b)
}
if result != (a + b) {
t.Errorf("wrong result for %d + %d", a, b)
}
}
func TestCheckedMulU8_Overflow(t *testing.T) {
var a uint8 = 0xff / 2
var b uint8 = 3
_, err := CheckedMulU8(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d * %d", a, b)
}
}
func TestCheckedMulU8_NoOverflow(t *testing.T) {
var a uint8 = 22
var b uint8 = 2
result, err := CheckedMulU8(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d * %d", a, b)
}
if result != (a * b) {
t.Errorf("wrong result for %d * %d", a, b)
}
}
func TestCheckedMulU16_Overflow(t *testing.T) {
var a uint16 = math.MaxUint16 / 2
var b uint16 = 3
_, err := CheckedMulU16(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d * %d", a, b)
}
}
func TestCheckedMulU16_NoOverflow(t *testing.T) {
var a uint16 = 22
var b uint16 = 2
result, err := CheckedMulU16(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d * %d", a, b)
}
if result != (a * b) {
t.Errorf("wrong result for %d * %d", a, b)
}
}
func TestCheckedMulU32_Overflow(t *testing.T) {
var a uint32 = math.MaxUint32 / 2
var b uint32 = 3
_, err := CheckedMulU32(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d * %d", a, b)
}
}
func TestCheckedMulU32_NoOverflow(t *testing.T) {
var a uint32 = 22
var b uint32 = 2
result, err := CheckedMulU32(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d * %d", a, b)
}
if result != (a * b) {
t.Errorf("wrong result for %d * %d", a, b)
}
}
func TestCheckedMulU64_Overflow(t *testing.T) {
var a uint64 = math.MaxUint64 / 2
var b uint64 = 3
_, err := CheckedMulU64(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d * %d", a, b)
}
}
func TestCheckedMulU64_NoOverflow(t *testing.T) {
var a uint64 = 22
var b uint64 = 2
result, err := CheckedMulU64(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d * %d", a, b)
}
if result != (a * b) {
t.Errorf("wrong result for %d * %d", a, b)
}
}
func TestCheckedSubU8_Underflow(t *testing.T) {
var a uint8 = 0x10
var b uint8 = 0xff
_, err := CheckedSubU8(a, b)
if err == nil {
t.Errorf("should've detected underflow in calculating %d - %d", a, b)
}
}
func TestCheckedSubU8_NoUnderflow(t *testing.T) {
var a uint8 = 22
var b uint8 = 2
result, err := CheckedSubU8(a, b)
if err != nil {
t.Errorf("should NOT have detected underflow in calculating %d - %d", a, b)
}
if result != (a - b) {
t.Errorf("wrong result for %d - %d", a, b)
}
}
func TestCheckedSubU16_Underflow(t *testing.T) {
var a uint16 = 0x10
var b uint16 = 0xff
_, err := CheckedSubU16(a, b)
if err == nil {
t.Errorf("should've detected underflow in calculating %d - %d", a, b)
}
}
func TestCheckedSubU16_NoUnderflow(t *testing.T) {
var a uint16 = 22
var b uint16 = 2
result, err := CheckedSubU16(a, b)
if err != nil {
t.Errorf("should NOT have detected underflow in calculating %d - %d", a, b)
}
if result != (a - b) {
t.Errorf("wrong result for %d - %d", a, b)
}
}
func TestCheckedSubU32_Underflow(t *testing.T) {
var a uint32 = 0x10
var b uint32 = 0xff
_, err := CheckedSubU32(a, b)
if err == nil {
t.Errorf("should've detected underflow in calculating %d - %d", a, b)
}
}
func TestCheckedSubU32_NoUnderflow(t *testing.T) {
var a uint32 = 20
var b uint32 = 2
result, err := CheckedSubU32(a, b)
if err != nil {
t.Errorf("should NOT have detected underflow in calculating %d - %d", a, b)
}
if result != (a - b) {
t.Errorf("wrong result for %d - %d", a, b)
}
}
func TestCheckedSubU64_Underflow(t *testing.T) {
var a uint64 = 0x10
var b uint64 = 0xff
_, err := CheckedSubU64(a, b)
if err == nil {
t.Errorf("should've detected overflow in calculating %d - %d", a, b)
}
}
func TestCheckedSubU64_NoUnderflow(t *testing.T) {
var a uint64 = 22
var b uint64 = 2
result, err := CheckedSubU64(a, b)
if err != nil {
t.Errorf("should NOT have detected overflow in calculating %d * %d", a, b)
}
if result != (a - b) {
t.Errorf("wrong result for %d - %d", a, b)
}
}
func TestCheckedDivU16_DivByZero(t *testing.T) {
var a uint16 = 0x10
var b uint16 = 0
_, err := CheckedDivU16(a, b)
if err == nil {
t.Errorf("should've detected division-by-zero in calculating %d / %d", a, b)
}
}
func TestCheckedDivU16_NoDivByZero(t *testing.T) {
var a uint16 = 22
var b uint16 = 2
result, err := CheckedDivU16(a, b)
if err != nil {
t.Errorf("should NOT have detected division-by-zero in calculating %d / %d", a, b)
}
if result != (a / b) {
t.Errorf("wrong result for %d / %d", a, b)
}
}
func TestCheckedDivU32_DivByZero(t *testing.T) {
var a uint32 = 0x10
var b uint32 = 0
_, err := CheckedDivU32(a, b)
if err == nil {
t.Errorf("should've detected division-by-zero in calculating %d / %d", a, b)
}
}
func TestCheckedDivU32_NoDivByZero(t *testing.T) {
var a uint32 = 20
var b uint32 = 2
result, err := CheckedDivU32(a, b)
if err != nil {
t.Errorf("should NOT have detected division-by-zero in calculating %d / %d", a, b)
}
if result != (a / b) {
t.Errorf("wrong result for %d / %d", a, b)
}
}
func TestCheckedDivU64_DivByZero(t *testing.T) {
var a uint64 = 0x10
var b uint64 = 0
_, err := CheckedDivU64(a, b)
if err == nil {
t.Errorf("should've detected division-by-zero in calculating %d / %d", a, b)
}
}
func TestCheckedDivU64_NoDivByZero(t *testing.T) {
var a uint64 = 22
var b uint64 = 2
result, err := CheckedDivU64(a, b)
if err != nil {
t.Errorf("should NOT have detected division-by-zero in calculating %d / %d", a, b)
}
if result != (a / b) {
t.Errorf("wrong result for %d / %d", a, b)
}
}
func TestSaturatingAddU8_ShouldSaturate(t *testing.T) {
var a uint8 = 0xfe
var b uint8 = 6
result := SaturatingAddU8(a, b)
if result != math.MaxUint8 {
t.Errorf("result should've saturated in calculating %d + %d", a, b)
}
}
func TestSaturatingAddU8_ShouldNotSaturate(t *testing.T) {
var a uint8 = 10
var b uint8 = 20
result := SaturatingAddU8(a, b)
if result != (a + b) {
t.Errorf("wrong result in calculating %d + %d (got %d)", a, b, result)
}
}
func TestSaturatingMulU8_ShouldSaturate(t *testing.T) {
var a uint8 = 0xff / 2
var b uint8 = 3
result := SaturatingMulU8(a, b)
if result != math.MaxUint8 {
t.Errorf("result should've saturated in calculating %d * %d", a, b)
}
}
func TestSaturatingMulU8_ShouldNotSaturate(t *testing.T) {
var a uint8 = uint8(rand.Intn(5))
var b uint8 = uint8(rand.Intn(5))
result := SaturatingMulU8(a, b)
if result != (a * b) {
t.Errorf("wrong result in calculating %d * %d (got %d)", a, b, result)
}
}
func TestSaturatingSubU8_ShouldSaturate(t *testing.T) {
var a uint8 = 10
var b uint8 = 0x20
result := SaturatingSubU8(a, b)
if result != 0 {
t.Errorf("result should've saturated in calculating %d - %d", a, b)
}
}
func TestSaturatingSubU8_ShouldNotSaturate(t *testing.T) {
var a uint8 = uint8(rand.Intn(50) + 10)
var b uint8 = uint8(rand.Intn(10))
result := SaturatingSubU8(a, b)
if result != (a - b) {
t.Errorf("wrong result in calculating %d - %d (got %d)", a, b, result)
}
}
func TestSaturatingAddU16_ShouldSaturate(t *testing.T) {
var a uint16 = math.MaxUint16
var b uint16 = 6
result := SaturatingAddU16(a, b)
if result != math.MaxUint16 {
t.Errorf("result should've saturated in calculating %d + %d", a, b)
}
}
func TestSaturatingAddU16_ShouldNotSaturate(t *testing.T) {
var a uint16 = uint16(rand.Intn(200))
var b uint16 = uint16(rand.Intn(200))
result := SaturatingAddU16(a, b)
if result != (a + b) {
t.Errorf("wrong result in calculating %d + %d (got %d)", a, b, result)
}
}
func TestSaturatingMulU16_ShouldSaturate(t *testing.T) {
var a uint16 = math.MaxUint16 / 2
var b uint16 = 3
result := SaturatingMulU16(a, b)
if result != math.MaxUint16 {
t.Errorf("result should've saturated in calculating %d * %d", a, b)
}
}
func TestSaturatingMulU16_ShouldNotSaturate(t *testing.T) {
var a uint16 = uint16(rand.Intn(20))
var b uint16 = uint16(rand.Intn(20))
result := SaturatingMulU16(a, b)
if result != (a * b) {
t.Errorf("wrong result in calculating %d * %d (got %d)", a, b, result)
}
}
func TestSaturatingSubU16_ShouldSaturate(t *testing.T) {
var a uint16 = 10
var b uint16 = 0x20
result := SaturatingSubU16(a, b)
if result != 0 {
t.Errorf("result should've saturated in calculating %d - %d", a, b)
}
}
func TestSaturatingSubU16_ShouldNotSaturate(t *testing.T) {
var a uint16 = uint16(rand.Intn(100) + 50)
var b uint16 = uint16(rand.Intn(50))
result := SaturatingSubU16(a, b)
if result != (a - b) {
t.Errorf("wrong result in calculating %d - %d (got %d)", a, b, result)
}
}
func TestSaturatingAddU32_ShouldSaturate(t *testing.T) {
var a uint32 = math.MaxUint32
var b uint32 = 6
result := SaturatingAddU32(a, b)
if result != math.MaxUint32 {
t.Errorf("result should've saturated in calculating %d + %d", a, b)
}
}
func TestSaturatingAddU32_ShouldNotSaturate(t *testing.T) {
var a uint32 = uint32(rand.Intn(1000))
var b uint32 = uint32(rand.Intn(1000))
result := SaturatingAddU32(a, b)
if result != (a + b) {
t.Errorf("wrong result in calculating %d + %d (got %d)", a, b, result)
}
}
func TestSaturatingMulU32_ShouldSaturate(t *testing.T) {
var a uint32 = math.MaxUint32 / 2
var b uint32 = 3
result := SaturatingMulU32(a, b)
if result != math.MaxUint32 {
t.Errorf("result should've saturated in calculating %d * %d", a, b)
}
}
func TestSaturatingMulU32_ShouldNotSaturate(t *testing.T) {
var a uint32 = uint32(rand.Intn(200))
var b uint32 = uint32(rand.Intn(1000))
result := SaturatingMulU32(a, b)
if result != (a * b) {
t.Errorf("wrong result in calculating %d * %d (got %d)", a, b, result)
}
}
func TestSaturatingSubU32_ShouldSaturate(t *testing.T) {
var a uint32 = 10
var b uint32 = 0x20
result := SaturatingSubU32(a, b)
if result != 0 {
t.Errorf("result should've saturated in calculating %d - %d", a, b)
}
}
func TestSaturatingSubU32_ShouldNotSaturate(t *testing.T) {
var a uint32 = uint32(rand.Intn(1000) + 500)
var b uint32 = uint32(rand.Intn(500))
result := SaturatingSubU32(a, b)
if result != (a - b) {
t.Errorf("wrong result in calculating %d - %d (got %d)", a, b, result)
}
}
func TestSaturatingAddU64_ShouldSaturate(t *testing.T) {
var a uint64 = math.MaxUint64
var b uint64 = 6
result := SaturatingAddU64(a, b)
if result != math.MaxUint64 {
t.Errorf("result should've saturated in calculating %d + %d", a, b)
}
}
func TestSaturatingAddU64_ShouldNotSaturate(t *testing.T) {
var a uint64 = uint64(rand.Intn(20000))
var b uint64 = uint64(rand.Intn(20000))
result := SaturatingAddU64(a, b)
if result != (a + b) {
t.Errorf("wrong result in calculating %d + %d (got %d)", a, b, result)
}
}
func TestSaturatingMulU64_ShouldSaturate(t *testing.T) {
var a uint64 = math.MaxUint64 / 2
var b uint64 = 3
result := SaturatingMulU64(a, b)
if result != math.MaxUint64 {
t.Errorf("result should've saturated in calculating %d * %d", a, b)
}
}
func TestSaturatingMulU64_ShouldNotSaturate(t *testing.T) {
var a uint64 = uint64(rand.Intn(50000))
var b uint64 = uint64(rand.Intn(1000))
result := SaturatingMulU64(a, b)
if result != (a * b) {
t.Errorf("wrong result in calculating %d * %d (got %d)", a, b, result)
}
}
func TestSaturatingSubU64_ShouldSaturate(t *testing.T) {
var a uint64 = 10
var b uint64 = 0x20
result := SaturatingSubU64(a, b)
if result != 0 {
t.Errorf("result should've saturated in calculating %d - %d", a, b)
}
}
func TestSaturatingSubU64_ShouldNotSaturate(t *testing.T) {
var a uint64 = uint64(rand.Intn(50000) + 1000)
var b uint64 = uint64(rand.Intn(1000))
result := SaturatingSubU64(a, b)
if result != (a - b) {
t.Errorf("wrong result in calculating %d - %d (got %d)", a, b, result)
}
}

129
pkg/safemath/saturating.go Normal file
View File

@ -0,0 +1,129 @@
// The safemath package implements helper functions for safe handling of integers.
//
// This file implements integer operations that 'saturate' at the upper and lower bounds
// of the relevant type instead of overflowing.
package safemath
import (
"math"
"math/bits"
)
// SaturatingAddU8 adds two uint8's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingAddU8(a, b uint8) uint8 {
result := a + b
if result < a {
return math.MaxUint8
}
return result
}
// SaturatingMulU8 multiplies two uint8's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingMulU8(a, b uint8) uint8 {
if a == 0 || b == 0 {
return 0
}
result := a * b
if result < a {
return math.MaxUint8
}
return result
}
// SaturatingSubU8 computes `a - b` for two uint8's and saturates at the numerical boundary
// if an underflow would have occurred.
func SaturatingSubU8(a, b uint8) uint8 {
if a < b {
return 0
}
return a - b
}
// SaturatingAddU16 adds two uint16's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingAddU16(a, b uint16) uint16 {
result := a + b
if result < a {
return math.MaxUint16
}
return result
}
// SaturatingMulU16 multiplies two uint16's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingMulU16(a, b uint16) uint16 {
if a == 0 || b == 0 {
return 0
}
result := a * b
if result < a {
return math.MaxUint16
}
return result
}
// SaturatingSubU16 computes `a - b` for two uint16's and saturates at the numerical
// boundary (zero) if an underflow would have occurred.
func SaturatingSubU16(a, b uint16) uint16 {
if a < b {
return 0
}
return a - b
}
// SaturatingAddU32 adds two uint32's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingAddU32(a, b uint32) uint32 {
result, carry := bits.Add32(a, b, 0)
return result | uint32(-int32(carry))
}
// SaturatingMulU32 multiplies two uint32's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingMulU32(a, b uint32) uint32 {
overflow, result := bits.Mul32(a, b)
if overflow > 0 {
return math.MaxUint32
}
return result
}
// SaturatingSubU32 computes `a - b` for two uint32's and saturates at the numerical
// boundary (zero) if an underflow would have occurred.
func SaturatingSubU32(a, b uint32) uint32 {
result, borrow := bits.Sub32(a, b, 0)
if borrow == 1 {
return 0
}
return result
}
// SaturatingAddU64 adds two uint64's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingAddU64(a, b uint64) uint64 {
result, carry := bits.Add64(a, b, 0)
return result | uint64(-int64(carry))
}
// SaturatingMulU64 multiplies two uint64's together and saturates at the numerical boundary
// if an overflow would have occurred.
func SaturatingMulU64(a, b uint64) uint64 {
hi, lo := bits.Mul64(a, b)
if hi > 0 {
return math.MaxUint64
}
return lo
}
// SaturatingSubU64 computes `a - b` for two uint64's and saturates at the numerical
// boundary (zero) if an underflow would have occurred.
func SaturatingSubU64(a, b uint64) uint64 {
result, borrow := bits.Sub64(a, b, 0)
if borrow == 1 {
return 0
}
return result
}