use crate::constants::MAX_MONEY;
use crate::error::{ConsensusError, Result};
#[cold]
fn make_arithmetic_overflow_error() -> ConsensusError {
ConsensusError::TransactionValidation("Arithmetic overflow".into())
}
#[allow(dead_code)] const MAX_SAFE_VALUE: i64 = MAX_MONEY / 2;
#[inline(always)]
#[cfg(feature = "production")]
pub fn safe_add(a: i64, b: i64) -> Result<i64> {
if a >= 0 && b >= 0 {
if a > i64::MAX - b {
return Err(make_arithmetic_overflow_error());
}
Ok(a + b)
} else if a < 0 && b < 0 {
if a < i64::MIN - b {
return Err(ConsensusError::TransactionValidation(
"Arithmetic underflow".into(),
));
}
Ok(a + b)
} else {
a.checked_add(b).ok_or_else(make_arithmetic_overflow_error)
}
}
#[cfg(not(feature = "production"))]
#[inline]
pub fn safe_add(a: i64, b: i64) -> Result<i64> {
a.checked_add(b).ok_or_else(make_arithmetic_overflow_error)
}
#[inline(always)]
#[cfg(feature = "production")]
pub fn safe_sub(a: i64, b: i64) -> Result<i64> {
if a >= 0 && b >= 0 {
Ok(a - b)
} else if a < 0 && b < 0 {
Ok(a - b)
} else {
a.checked_sub(b)
.ok_or_else(|| ConsensusError::TransactionValidation("Arithmetic underflow".into()))
}
}
#[cfg(not(feature = "production"))]
#[inline]
pub fn safe_sub(a: i64, b: i64) -> Result<i64> {
a.checked_sub(b)
.ok_or_else(|| ConsensusError::TransactionValidation("Arithmetic underflow".into()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_safe_add_positive() {
assert_eq!(safe_add(100, 200).unwrap(), 300);
assert_eq!(safe_add(MAX_SAFE_VALUE, 0).unwrap(), MAX_SAFE_VALUE);
}
#[test]
fn test_safe_add_overflow() {
assert!(safe_add(i64::MAX, 1).is_err());
}
#[test]
fn test_safe_sub_positive() {
assert_eq!(safe_sub(300, 200).unwrap(), 100);
assert_eq!(safe_sub(MAX_SAFE_VALUE, 0).unwrap(), MAX_SAFE_VALUE);
}
#[test]
fn test_safe_sub_underflow() {
assert!(safe_sub(i64::MIN, 1).is_err());
}
}