radiance/pkg/safemath/checked.go

173 lines
4.2 KiB
Go

// 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
}