safeint

package module
v0.0.0-...-3a2168e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 23, 2026 License: MIT Imports: 6 Imported by: 0

README

safeint

Overflow-checked integer arithmetic for Go, built on generics.

Go's native integer operations silently wrap on overflow. safeint makes overflow detection explicit — you decide whether to check, panic, or wrap.

Install

go get github.com/howardsun-tw/safeint

Requires Go 1.18+ (generics). Tested on Go 1.18 through 1.26.

API Overview

The package provides three API styles for different use cases:

1. Checked Functions — (result, ok)

Functions return the result and a boolean. ok is false when overflow (or division by zero) occurs.

sum, ok := safeint.Add(a, b)
if !ok {
    // handle overflow
}

product, ok := safeint.Mul(a, b)
diff, ok := safeint.Sub(a, b)
quotient, ok := safeint.Div(a, b)
quotient, remainder, ok := safeint.DivMod(a, b)
remainder, ok := safeint.Mod(a, b)
negated, ok := safeint.Neg(a)
absolute, ok := safeint.Abs(a)
power, ok := safeint.Pow(base, exp)
shifted, ok := safeint.Lsh(a, n)
converted, ok := safeint.Convert[int64, int32](a)

All functions are generic over any Go integer type (int, int8...int64, uint, uint8...uint64, and named types based on them).

2. Must Functions — panic on overflow

Convenience wrappers that panic instead of returning a bool. Useful for cases where overflow is a programming error.

sum := safeint.MustAdd(a, b)       // panics: "safeint: Add overflow"
product := safeint.MustMul(a, b)
quotient := safeint.MustDiv(a, b)
negated := safeint.MustNeg(a)
converted := safeint.MustConvert[int64, int32](a)

Warning: Do not use Must* functions with untrusted input in server contexts — a panic will crash the goroutine.

3. Int[T] Wrapper Type — method-based API

A value-type wrapper providing three method families:

a := safeint.New[int64](100)
b := safeint.New[int64](200)

// Checked — returns (Int[T], bool)
sum, ok := a.AddOverflow(b)

// Wrapping — silently wraps, like native Go
sum := a.Add(b)

// Must — panics on overflow
sum := a.MustAdd(b)

Comparison methods:

a.Eq(b)       // ==
a.Lt(b)       // <
a.Gt(b)       // >
a.Lte(b)      // <=
a.Gte(b)      // >=
a.Cmp(b)      // -1, 0, +1
a.IsZero()

Value access:

a.Val()        // returns the underlying T
a.String()     // implements fmt.Stringer

Cross-type conversion:

// Go methods can't have extra type params, so this is a standalone function:
narrow, ok := safeint.ConvertInt[int64, int32](wideInt)

Full-Precision MulDiv / MulMod

Compute (a * b) / c or (a * b) % c without intermediate overflow. The product a * b is computed at double width internally (128-bit for 64-bit types via math/bits).

// (price * quantity) / divisor — no intermediate overflow
result, ok := safeint.MulDiv[int64](price, quantity, divisor)

// (a * b) % modulus
remainder, ok := safeint.MulMod[uint64](a, b, modulus)

Returns (0, false) when the divisor/modulus is zero or the final result overflows T.

Supported Operations

Operation Checked Must Int[T] Checked Int[T] Wrapping Int[T] Must
Add Add MustAdd AddOverflow Add MustAdd
Subtract Sub MustSub SubOverflow Sub MustSub
Multiply Mul MustMul MulOverflow Mul MustMul
Divide Div MustDiv DivOverflow MustDiv
DivMod DivMod DivModOverflow
Modulo Mod ModOverflow
Negate Neg MustNeg NegOverflow
Abs Abs AbsOverflow
Power Pow PowOverflow
Left Shift Lsh LshOverflow
Convert Convert MustConvert ConvertInt
MulDiv MulDiv MustMulDiv MulDivOverflow
MulMod MulMod MulModOverflow

Overflow Semantics

Some edge cases worth noting:

  • Division by zero — returns (0, false), never panics.
  • Signed MinInt / -1 — detected as overflow (Go would silently wrap).
  • MinInt % -1 — returns (0, true) (correct in Go, no overflow).
  • Neg on unsigned — overflow for any non-zero value.
  • Abs on signed MinInt — overflow (|MinInt| > MaxInt).
  • 0^0 — returns (1, true) by convention.
  • Lsh — returns (0, false) if shift >= bit width or bits are lost.

Type Constraints

type Integer interface { Signed | Unsigned }

type Signed interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}

type Unsigned interface {
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

Named types are supported via the ~ constraint:

type UserID int64
id, ok := safeint.Add(UserID(1), UserID(2)) // id is UserID

Testing

Tests include exhaustive verification of all 8-bit type ranges (int8, uint8) against reference implementations using math/big, plus targeted edge-case tests for 16/32/64-bit types.

go test ./...
Go Version Compatibility

CI tests against every Go release from 1.18 to 1.26. The minimum version is Go 1.18 (the release that introduced generics).

Security Note

All algorithms use data-dependent branches and are not constant-time. Do not use in cryptographic contexts where timing side-channels matter.

Acknowledgements

This project was inspired by and references ideas from:

  • g-utils/overflow — checked arithmetic functions for Go
  • holiman/uint256 — high-performance 256-bit integer library with dual API design (wrapping + checked methods)

License

MIT

Documentation

Overview

Package safeint provides overflow-checked arithmetic for Go's native integer types.

It offers three API styles:

  • Checked functions: Add, Sub, Mul, etc. return (result, ok) where ok is false on overflow.
  • Must functions: MustAdd, MustSub, etc. panic on overflow. Do not use with untrusted input.
  • Int[T] wrapper type: provides method-based API with checked, wrapping, and must variants.

All algorithms use data-dependent branches and are NOT constant-time. Do not use in cryptographic contexts where timing side-channels matter.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Abs

func Abs[T Integer](a T) (T, bool)

Abs returns |a| and true if the result does not overflow. Overflows only for signed MinInt (whose absolute value exceeds MaxInt). For unsigned types, always returns (a, true).

func Add

func Add[T Integer](a, b T) (T, bool)

Add returns a + b and true if the result does not overflow.

func Convert

func Convert[T Integer, U Integer](a T) (U, bool)

Convert converts value a of type T to type U, returning true if the value is preserved exactly (no truncation or sign change).

func Div

func Div[T Integer](a, b T) (T, bool)

Div returns a / b and true if the result does not overflow. Returns (0, false) on division by zero or signed MinInt / -1 overflow.

func DivMod

func DivMod[T Integer](a, b T) (T, T, bool)

DivMod returns (a/b, a%b) and true if the quotient does not overflow. Returns (0, 0, false) on division by zero or signed MinInt / -1 overflow.

func Lsh

func Lsh[T Integer](a T, n uint) (T, bool)

Lsh returns a << n and true if no bits are lost. Always returns (0, true) when a == 0.

func Mod

func Mod[T Integer](a, b T) (T, bool)

Mod returns a % b and true. Returns (0, false) only on division by zero. The remainder never overflows; even MinInt % -1 == 0 is correct in Go.

func Mul

func Mul[T Integer](a, b T) (T, bool)

Mul returns a * b and true if the result does not overflow. Both a sign check and a division-roundtrip check are required: the sign check catches MinInt * -1; the roundtrip catches magnitude overflow.

func MulDiv

func MulDiv[T Integer](a, b, c T) (T, bool)

MulDiv returns (a*b)/c computed with full intermediate precision (no intermediate overflow). Returns (0, false) when c == 0 or the quotient overflows T.

func MulMod

func MulMod[T Integer](a, b, c T) (T, bool)

MulMod returns (a*b)%c computed with full intermediate precision. Returns (0, false) when c == 0.

func MustAdd

func MustAdd[T Integer](a, b T) T

MustAdd returns a + b, panicking on overflow. WARNING: Do not use with untrusted input in server contexts.

func MustConvert

func MustConvert[T Integer, U Integer](a T) U

MustConvert converts a from T to U, panicking if the value cannot be represented.

func MustDiv

func MustDiv[T Integer](a, b T) T

MustDiv returns a / b, panicking on overflow or division by zero.

func MustMul

func MustMul[T Integer](a, b T) T

MustMul returns a * b, panicking on overflow.

func MustMulDiv

func MustMulDiv[T Integer](a, b, c T) T

MustMulDiv returns (a*b)/c, panicking on overflow or division by zero.

func MustNeg

func MustNeg[T Integer](a T) T

MustNeg returns -a, panicking on overflow.

func MustSub

func MustSub[T Integer](a, b T) T

MustSub returns a - b, panicking on overflow.

func Neg

func Neg[T Integer](a T) (T, bool)

Neg returns -a and true if the result does not overflow. Overflows for signed MinInt and all non-zero unsigned values.

func Pow

func Pow[T Integer](base T, exp uint) (T, bool)

Pow returns base^exp and true if no intermediate or final overflow occurs. Uses binary exponentiation. 0^0 returns (1, true) by convention.

func Sub

func Sub[T Integer](a, b T) (T, bool)

Sub returns a - b and true if the result does not overflow.

Types

type Int

type Int[T Integer] struct {
	// contains filtered or unexported fields
}

Int is a generic wrapper around Go's native integer types, providing method-based arithmetic with overflow detection.

It uses value semantics — all methods return new values, never mutate. Inspired by holiman/uint256's dual API: wrapping methods (Add, Sub, Mul) that silently wrap on overflow, and checked methods (*Overflow) that report overflow via a bool return.

func ConvertInt

func ConvertInt[T Integer, U Integer](a Int[T]) (Int[U], bool)

ConvertInt converts an Int[T] to Int[U], returning true if the value is preserved exactly.

func New

func New[T Integer](v T) Int[T]

New creates an Int[T] from a raw value.

func Zero

func Zero[T Integer]() Int[T]

Zero returns the zero value of Int[T].

func (Int[T]) AbsOverflow

func (a Int[T]) AbsOverflow() (Int[T], bool)

AbsOverflow returns |a| and true if the result does not overflow.

func (Int[T]) Add

func (a Int[T]) Add(b Int[T]) Int[T]

Add returns a + b, wrapping on overflow.

func (Int[T]) AddOverflow

func (a Int[T]) AddOverflow(b Int[T]) (Int[T], bool)

AddOverflow returns a + b and true if the result does not overflow.

func (Int[T]) Cmp

func (a Int[T]) Cmp(b Int[T]) int

Cmp compares a and b, returning -1, 0, or +1.

func (Int[T]) DivModOverflow

func (a Int[T]) DivModOverflow(b Int[T]) (Int[T], Int[T], bool)

DivModOverflow returns (a/b, a%b) and true if the quotient does not overflow.

func (Int[T]) DivOverflow

func (a Int[T]) DivOverflow(b Int[T]) (Int[T], bool)

DivOverflow returns a / b and true if the result does not overflow.

func (Int[T]) Eq

func (a Int[T]) Eq(b Int[T]) bool

Eq returns true if a == b.

func (Int[T]) Gt

func (a Int[T]) Gt(b Int[T]) bool

Gt returns true if a > b.

func (Int[T]) Gte

func (a Int[T]) Gte(b Int[T]) bool

Gte returns true if a >= b.

func (Int[T]) IsZero

func (a Int[T]) IsZero() bool

IsZero returns true if a == 0.

func (Int[T]) LshOverflow

func (a Int[T]) LshOverflow(n uint) (Int[T], bool)

LshOverflow returns a << n and true if no bits are lost.

func (Int[T]) Lt

func (a Int[T]) Lt(b Int[T]) bool

Lt returns true if a < b.

func (Int[T]) Lte

func (a Int[T]) Lte(b Int[T]) bool

Lte returns true if a <= b.

func (Int[T]) MarshalJSON

func (a Int[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (Int[T]) ModOverflow

func (a Int[T]) ModOverflow(b Int[T]) (Int[T], bool)

ModOverflow returns a % b and true. Returns (0, false) on division by zero.

func (Int[T]) Mul

func (a Int[T]) Mul(b Int[T]) Int[T]

Mul returns a * b, wrapping on overflow.

func (Int[T]) MulDivOverflow

func (a Int[T]) MulDivOverflow(b, c Int[T]) (Int[T], bool)

MulDivOverflow returns (a*b)/c with full intermediate precision.

func (Int[T]) MulModOverflow

func (a Int[T]) MulModOverflow(b, c Int[T]) (Int[T], bool)

MulModOverflow returns (a*b)%c with full intermediate precision.

func (Int[T]) MulOverflow

func (a Int[T]) MulOverflow(b Int[T]) (Int[T], bool)

MulOverflow returns a * b and true if the result does not overflow.

func (Int[T]) MustAdd

func (a Int[T]) MustAdd(b Int[T]) Int[T]

MustAdd returns a + b, panicking on overflow.

func (Int[T]) MustDiv

func (a Int[T]) MustDiv(b Int[T]) Int[T]

MustDiv returns a / b, panicking on overflow or division by zero.

func (Int[T]) MustMul

func (a Int[T]) MustMul(b Int[T]) Int[T]

MustMul returns a * b, panicking on overflow.

func (Int[T]) MustSub

func (a Int[T]) MustSub(b Int[T]) Int[T]

MustSub returns a - b, panicking on overflow.

func (Int[T]) NegOverflow

func (a Int[T]) NegOverflow() (Int[T], bool)

NegOverflow returns -a and true if the result does not overflow.

func (Int[T]) PowOverflow

func (a Int[T]) PowOverflow(exp uint) (Int[T], bool)

PowOverflow returns a^exp and true if no overflow occurs.

func (*Int[T]) Scan

func (a *Int[T]) Scan(src interface{}) error

Scan implements sql.Scanner. Accepts int64, float64 (whole numbers only), []byte, string, or nil.

func (Int[T]) String

func (a Int[T]) String() string

String implements fmt.Stringer.

func (Int[T]) Sub

func (a Int[T]) Sub(b Int[T]) Int[T]

Sub returns a - b, wrapping on overflow.

func (Int[T]) SubOverflow

func (a Int[T]) SubOverflow(b Int[T]) (Int[T], bool)

SubOverflow returns a - b and true if the result does not overflow.

func (*Int[T]) UnmarshalJSON

func (a *Int[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (Int[T]) Val

func (a Int[T]) Val() T

Val returns the underlying raw value.

func (Int[T]) Value

func (a Int[T]) Value() (driver.Value, error)

Value implements driver.Valuer. Returns the underlying value as int64, or as a decimal string if it overflows int64 (e.g. uint64 > MaxInt64).

type Integer

type Integer interface {
	Signed | Unsigned
}

Integer is a constraint that permits any Go built-in integer type (including named types).

type Signed

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

Signed is a constraint that permits any signed integer type.

type Unsigned

type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

Unsigned is a constraint that permits any unsigned integer type.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL