common/math: add safe division, modulo, and utility functions

Add new integer utility functions to common/math package:

- SafeDiv: safe division that returns error flag on division by zero
- SafeMod: safe modulo that returns error flag on modulo by zero
- Min: returns minimum of two uint64 values
- Max: returns maximum of two uint64 values
- Clamp: constrains value to a range [min, max]
- AbsDiff: returns absolute difference between two uint64 values

These utilities provide commonly needed operations with proper edge
case handling, particularly useful for gas calculations and other
EVM-related integer operations where division/modulo by zero must
be handled gracefully.

Includes comprehensive tests for all new functions.
This commit is contained in:
SmartFlow Developer 2026-01-08 23:10:08 +01:00
parent f51870e40e
commit 594a5b3098
2 changed files with 240 additions and 0 deletions

View file

@ -0,0 +1,76 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package math
// SafeDiv returns x/y and checks for division by zero.
// Returns (0, true) if y is zero (indicating an error condition).
// Returns (x/y, false) if the division is valid.
func SafeDiv(x, y uint64) (uint64, bool) {
if y == 0 {
return 0, true
}
return x / y, false
}
// SafeMod returns x%y and checks for division by zero.
// Returns (0, true) if y is zero (indicating an error condition).
// Returns (x%y, false) if the operation is valid.
func SafeMod(x, y uint64) (uint64, bool) {
if y == 0 {
return 0, true
}
return x % y, false
}
// Min returns the smaller of x or y.
func Min(x, y uint64) uint64 {
if x < y {
return x
}
return y
}
// Max returns the larger of x or y.
func Max(x, y uint64) uint64 {
if x > y {
return x
}
return y
}
// Clamp returns x clamped to the inclusive range [min, max].
// If x is less than min, min is returned.
// If x is greater than max, max is returned.
// Otherwise, x is returned unchanged.
func Clamp(x, min, max uint64) uint64 {
if x < min {
return min
}
if x > max {
return max
}
return x
}
// AbsDiff returns the absolute difference between x and y.
// This is useful when you need |x - y| without worrying about underflow.
func AbsDiff(x, y uint64) uint64 {
if x > y {
return x - y
}
return y - x
}

View file

@ -0,0 +1,164 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package math
import (
"math"
"testing"
)
func TestSafeDiv(t *testing.T) {
tests := []struct {
x, y uint64
expected uint64
isError bool
}{
{10, 2, 5, false},
{10, 3, 3, false},
{0, 5, 0, false},
{100, 1, 100, false},
{math.MaxUint64, 2, math.MaxUint64 / 2, false},
// Division by zero cases
{10, 0, 0, true},
{0, 0, 0, true},
{math.MaxUint64, 0, 0, true},
}
for i, test := range tests {
result, isError := SafeDiv(test.x, test.y)
if isError != test.isError {
t.Errorf("test %d: SafeDiv(%d, %d) error = %v, want %v", i, test.x, test.y, isError, test.isError)
}
if !isError && result != test.expected {
t.Errorf("test %d: SafeDiv(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
}
}
}
func TestSafeMod(t *testing.T) {
tests := []struct {
x, y uint64
expected uint64
isError bool
}{
{10, 3, 1, false},
{10, 2, 0, false},
{0, 5, 0, false},
{7, 7, 0, false},
{math.MaxUint64, 2, 1, false},
// Modulo by zero cases
{10, 0, 0, true},
{0, 0, 0, true},
}
for i, test := range tests {
result, isError := SafeMod(test.x, test.y)
if isError != test.isError {
t.Errorf("test %d: SafeMod(%d, %d) error = %v, want %v", i, test.x, test.y, isError, test.isError)
}
if !isError && result != test.expected {
t.Errorf("test %d: SafeMod(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
}
}
}
func TestMin(t *testing.T) {
tests := []struct {
x, y uint64
expected uint64
}{
{1, 2, 1},
{2, 1, 1},
{5, 5, 5},
{0, 100, 0},
{math.MaxUint64, 0, 0},
{math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64 - 1},
}
for i, test := range tests {
result := Min(test.x, test.y)
if result != test.expected {
t.Errorf("test %d: Min(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
}
}
}
func TestMax(t *testing.T) {
tests := []struct {
x, y uint64
expected uint64
}{
{1, 2, 2},
{2, 1, 2},
{5, 5, 5},
{0, 100, 100},
{math.MaxUint64, 0, math.MaxUint64},
{math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64},
}
for i, test := range tests {
result := Max(test.x, test.y)
if result != test.expected {
t.Errorf("test %d: Max(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
}
}
}
func TestClamp(t *testing.T) {
tests := []struct {
x, min, max uint64
expected uint64
}{
{5, 0, 10, 5}, // within range
{0, 5, 10, 5}, // below min
{15, 5, 10, 10}, // above max
{5, 5, 10, 5}, // at min
{10, 5, 10, 10}, // at max
{5, 5, 5, 5}, // min == max == x
{0, 0, 0, 0}, // all zero
{100, 0, 50, 50}, // clamped to max
}
for i, test := range tests {
result := Clamp(test.x, test.min, test.max)
if result != test.expected {
t.Errorf("test %d: Clamp(%d, %d, %d) = %d, want %d", i, test.x, test.min, test.max, result, test.expected)
}
}
}
func TestAbsDiff(t *testing.T) {
tests := []struct {
x, y uint64
expected uint64
}{
{10, 3, 7},
{3, 10, 7},
{5, 5, 0},
{0, 0, 0},
{math.MaxUint64, 0, math.MaxUint64},
{0, math.MaxUint64, math.MaxUint64},
{100, 99, 1},
}
for i, test := range tests {
result := AbsDiff(test.x, test.y)
if result != test.expected {
t.Errorf("test %d: AbsDiff(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
}
}
}