Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

safe_math.gno

6.42 Kb · 253 lines
  1package gnsmath
  2
  3import (
  4	"math"
  5
  6	ufmt "gno.land/p/nt/ufmt/v0"
  7
  8	i256 "gno.land/p/gnoswap/int256/v1"
  9	u256 "gno.land/p/gnoswap/uint256/v1"
 10)
 11
 12// Range bounds used by the safe conversion helpers.
 13const (
 14	maxInt64Decimal  = "9223372036854775807"                     // 2^63 - 1
 15	maxInt128Decimal = "170141183460469231731687303715884105727" // 2^127 - 1
 16)
 17
 18// MaxInt128 returns the largest positive value representable by a signed 128-bit integer.
 19//
 20// Returns:
 21//   - maxInt128: A fresh *i256.Int containing 2^127 - 1, used as the upper bound
 22//     for conversions that must fit in the signed int128 range.
 23func MaxInt128() *i256.Int {
 24	return i256.MustFromDecimal(maxInt128Decimal)
 25}
 26
 27// SafeAddInt64 returns the exact sum of two signed 64-bit integers.
 28//
 29// Parameters:
 30//   - a: First signed int64 operand.
 31//   - b: Second signed int64 operand.
 32//
 33// Returns:
 34//   - sum: a + b when the mathematical result is within [math.MinInt64, math.MaxInt64].
 35//
 36// Panics if the signed int64 sum overflows or underflows.
 37func SafeAddInt64(a, b int64) int64 {
 38	if a > 0 && b > math.MaxInt64-a {
 39		panic("int64 addition overflow")
 40	}
 41	if a < 0 && b < math.MinInt64-a {
 42		panic("int64 addition underflow")
 43	}
 44	return a + b
 45}
 46
 47// SafeSubInt64 returns the exact difference of two signed 64-bit integers.
 48//
 49// Parameters:
 50//   - a: Signed int64 minuend.
 51//   - b: Signed int64 subtrahend.
 52//
 53// Returns:
 54//   - difference: a - b when the mathematical result is within [math.MinInt64, math.MaxInt64].
 55//
 56// Panics if the signed int64 difference overflows or underflows.
 57func SafeSubInt64(a, b int64) int64 {
 58	if b > 0 && a < math.MinInt64+b {
 59		panic("int64 subtraction underflow")
 60	}
 61	if b < 0 && a > math.MaxInt64+b {
 62		panic("int64 subtraction overflow")
 63	}
 64	return a - b
 65}
 66
 67// SafeMulInt64 returns the exact product of two signed 64-bit integers.
 68//
 69// Parameters:
 70//   - a: First signed int64 factor.
 71//   - b: Second signed int64 factor.
 72//
 73// Returns:
 74//   - product: a * b when the mathematical result is within [math.MinInt64, math.MaxInt64].
 75//
 76// Panics if the signed int64 product overflows or underflows.
 77func SafeMulInt64(a, b int64) int64 {
 78	if a == 0 || b == 0 {
 79		return 0
 80	}
 81
 82	if a > 0 && b > 0 {
 83		if a > math.MaxInt64/b {
 84			panic("int64 multiplication overflow")
 85		}
 86	} else if a < 0 && b < 0 {
 87		if a < math.MaxInt64/b {
 88			panic("int64 multiplication overflow")
 89		}
 90	} else if a > 0 && b < 0 {
 91		if b < math.MinInt64/a {
 92			panic("int64 multiplication underflow")
 93		}
 94	} else { // a < 0 && b > 0
 95		if a < math.MinInt64/b {
 96			panic("int64 multiplication underflow")
 97		}
 98	}
 99
100	return a * b
101}
102
103// SafeMulDivInt64 returns the truncated quotient (a * b) / c.
104//
105// The product is formed in signed 256-bit arithmetic before division, so an
106// intermediate product may exceed int64 while the final quotient must still fit.
107//
108// Parameters:
109//   - a: First signed int64 factor.
110//   - b: Second signed int64 factor.
111//   - c: Non-zero signed int64 divisor.
112//
113// Returns:
114//   - quotient: The signed integer quotient after dividing a * b by c.
115//
116// Panics if the 256-bit product overflows, c is zero, or the quotient is outside
117// the representable int64 range.
118func SafeMulDivInt64(a, b, c int64) int64 {
119	if a == 0 || b == 0 {
120		return 0
121	}
122
123	result, overflow := i256.Zero().MulOverflow(i256.NewInt(a), i256.NewInt(b))
124	if overflow {
125		panic(errSafeMathOverflow)
126	}
127
128	result = i256.Zero().Div(result, i256.NewInt(c))
129	if !result.IsInt64() {
130		panic(errSafeMathOverflow)
131	}
132
133	return result.Int64()
134}
135
136// SafeAbsInt64 returns the non-negative absolute value of a.
137//
138// Parameters:
139//   - a: Signed int64 value whose magnitude is requested.
140//
141// Returns:
142//   - magnitude: |a| as int64.
143//
144// Panics when a is math.MinInt64 because its positive magnitude cannot be
145// represented by int64.
146func SafeAbsInt64(a int64) int64 {
147	if a == math.MinInt64 {
148		panic(errSafeMathOverflow)
149	}
150	if a < 0 {
151		return -a
152	}
153	return a
154}
155
156// SafeAddUint64 returns the exact sum of two unsigned 64-bit integers.
157//
158// Parameters:
159//   - a: First uint64 operand.
160//   - b: Second uint64 operand.
161//
162// Returns:
163//   - sum: a + b when the mathematical result is at most math.MaxUint64.
164//
165// Panics if the uint64 sum overflows.
166func SafeAddUint64(a, b uint64) uint64 {
167	if a > math.MaxUint64-b {
168		panic("uint64 addition overflow")
169	}
170	return a + b
171}
172
173// SafeSubUint64 returns the exact difference of two unsigned 64-bit integers.
174//
175// Parameters:
176//   - a: Unsigned uint64 minuend.
177//   - b: Unsigned uint64 subtrahend; it must not exceed a.
178//
179// Returns:
180//   - difference: a - b.
181//
182// Panics if b is greater than a and the subtraction would underflow uint64.
183func SafeSubUint64(a, b uint64) uint64 {
184	if a < b {
185		panic("uint64 subtraction underflow")
186	}
187	return a - b
188}
189
190// SafeUint64ToInt64 converts a uint64 to a signed int64 without changing its value.
191//
192// Parameters:
193//   - value: Unsigned value to convert; it must be no greater than 2^63 - 1.
194//
195// Returns:
196//   - converted: value represented as int64.
197//
198// Panics when value exceeds math.MaxInt64.
199func SafeUint64ToInt64(value uint64) int64 {
200	if value > uint64(math.MaxInt64) {
201		panic(ufmt.Sprintf(
202			"amount(%d) overflows int64 range (max: %s)",
203			value, maxInt64Decimal,
204		))
205	}
206	return int64(value)
207}
208
209// SafeConvertToInt64 converts a non-negative 256-bit integer to int64.
210//
211// Parameters:
212//   - value: Unsigned 256-bit value to convert; nil is invalid.
213//
214// Returns:
215//   - converted: value represented as int64 when it is at most math.MaxInt64.
216//
217// Panics when value is nil or outside the int64 range.
218func SafeConvertToInt64(value *u256.Uint) int64 {
219	if value == nil {
220		panic("SafeConvertToInt64: value is nil")
221	}
222	res, overflow := value.Uint64WithOverflow()
223	if overflow || res > uint64(math.MaxInt64) {
224		panic(ufmt.Sprintf(
225			"amount(%s) overflows int64 range (max: %s)",
226			value.ToString(), maxInt64Decimal,
227		))
228	}
229	return int64(res)
230}
231
232// SafeConvertToInt128 converts a non-negative 256-bit integer to signed int128.
233//
234// Parameters:
235//   - value: Unsigned 256-bit value to convert; nil is invalid.
236//
237// Returns:
238//   - converted: A new *i256.Int representing value when it is at most 2^127 - 1.
239//
240// Panics when value is nil or exceeds the largest positive signed int128 value.
241func SafeConvertToInt128(value *u256.Uint) *i256.Int {
242	if value == nil {
243		panic("SafeConvertToInt128: value is nil")
244	}
245	converted := i256.FromUint256(value)
246	if converted.Gt(MaxInt128()) {
247		panic(ufmt.Sprintf(
248			"amount(%s) overflows int128 range",
249			value.ToString(),
250		))
251	}
252	return converted
253}