use crate::types::{AsnType, Constraints, Extensible, Tag, constraints};
use alloc::boxed::Box;
use core::hash::Hash;
use num_bigint::{BigInt, BigUint, ToBigInt};
use num_traits::{CheckedAdd, CheckedSub};
use num_traits::{Signed, ToBytes, ToPrimitive, identities::Zero};
#[derive(Debug, Clone, Ord, PartialOrd)]
#[allow(missing_docs)]
pub struct Integer(IntegerKind);
macro_rules! op_or_promote {
($rhs:ident . $op:ident ($($args:tt)*), $promote:expr) => {
$rhs.$op($($args)*).map(IntegerKind::Primitive).unwrap_or_else(|| IntegerKind::Variable($promote))
}
}
#[derive(Debug, Clone, Ord, PartialOrd)]
#[allow(missing_docs)]
pub enum IntegerKind {
Primitive(isize),
Variable(Box<BigInt>),
}
impl Integer {
pub const ZERO: Self = Self(IntegerKind::Primitive(0));
pub const ONE: Self = Self(IntegerKind::Primitive(1));
}
impl Default for Integer {
fn default() -> Self {
Self::ZERO
}
}
impl core::fmt::Display for Integer {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match &self.0 {
IntegerKind::Primitive(value) => write!(f, "{value}"),
IntegerKind::Variable(value) => write!(f, "{value}"),
}
}
}
impl PartialEq for Integer {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl PartialEq for IntegerKind {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(IntegerKind::Primitive(lhs), IntegerKind::Primitive(rhs)) => lhs == rhs,
(IntegerKind::Variable(lhs), IntegerKind::Variable(rhs)) => lhs == rhs,
(IntegerKind::Primitive(lhs), IntegerKind::Variable(rhs)) => {
lhs.to_bigint().unwrap_or_default() == **rhs
}
(IntegerKind::Variable(lhs), IntegerKind::Primitive(rhs)) => {
**lhs == rhs.to_bigint().unwrap_or_default()
}
}
}
}
impl Eq for Integer {}
impl Eq for IntegerKind {}
impl Hash for Integer {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl Hash for IntegerKind {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
match self {
IntegerKind::Primitive(value) => value.hash(state),
IntegerKind::Variable(value) => value.hash(state),
}
}
}
impl num_traits::CheckedAdd for Integer {
fn checked_add(&self, other: &Self) -> Option<Self> {
Some(Self(match (&self.0, &other.0) {
(IntegerKind::Primitive(lhs), IntegerKind::Primitive(rhs)) => {
op_or_promote!(lhs.checked_add(rhs), Box::new(BigInt::from(*lhs) + *rhs))
}
(IntegerKind::Primitive(lhs), IntegerKind::Variable(rhs)) => {
IntegerKind::Variable(Box::new(&**rhs + lhs))
}
(IntegerKind::Variable(lhs), IntegerKind::Primitive(rhs)) => {
IntegerKind::Variable(Box::new(&**lhs + *rhs))
}
(IntegerKind::Variable(lhs), IntegerKind::Variable(rhs)) => {
IntegerKind::Variable(Box::new(&**lhs + &**rhs))
}
}))
}
}
impl core::ops::Add for Integer {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
<Self as CheckedAdd>::checked_add(&self, &rhs).unwrap_or_default()
}
}
macro_rules! impl_ops_integer {
($($t:ty),*) => {
$(
impl core::ops::Add<$t> for Integer {
type Output = Self;
fn add(self, rhs: $t) -> Self::Output {
Self(match self.0 {
IntegerKind::Primitive(lhs) => op_or_promote!(lhs.checked_add(rhs as isize), Box::new(BigInt::from(lhs) + rhs)),
IntegerKind::Variable(lhs) => IntegerKind::Variable(Box::new(*lhs + rhs)),
})
}
}
impl core::ops::Sub<$t> for Integer {
type Output = Self;
fn sub(self, rhs: $t) -> Self::Output {
Self(match self.0 {
IntegerKind::Primitive(lhs) => op_or_promote!(lhs.checked_sub(rhs as isize), Box::new(BigInt::from(lhs) - rhs)),
IntegerKind::Variable(lhs) => IntegerKind::Variable(Box::new(*lhs - rhs)),
})
}
}
)*
};
}
macro_rules! impl_ops_integer_big {
($($t:ty),*) => {
$(
impl core::ops::Add<$t> for Integer {
type Output = Self;
fn add(self, rhs: $t) -> Self::Output {
Self(match self.0 {
IntegerKind::Primitive(lhs) => {
let value = isize::try_from(rhs).ok();
op_or_promote!(value.and_then(|rhs| lhs.checked_add(rhs)), Box::new(BigInt::from(lhs) + rhs))
}
IntegerKind::Variable(lhs) => {
IntegerKind::Variable(Box::new(*lhs + rhs))
}
})
}
}
impl core::ops::Sub<$t> for Integer {
type Output = Self;
fn sub(self, rhs: $t) -> Self::Output {
Self(match self.0 {
IntegerKind::Primitive(lhs) => {
let value = isize::try_from(rhs).ok();
op_or_promote!(value.and_then(|rhs| lhs.checked_sub(rhs)), Box::new(BigInt::from(lhs) - rhs))
}
IntegerKind::Variable(lhs) => {
IntegerKind::Variable(Box::new(*lhs - rhs))
}
})
}
}
)*
};
}
#[cfg(target_pointer_width = "32")]
impl_ops_integer!(u8, u16, i8, i16, i32, isize);
#[cfg(target_pointer_width = "32")]
impl_ops_integer_big!(u32, i64);
#[cfg(target_pointer_width = "64")]
impl_ops_integer!(u8, u16, u32, i8, i16, i32, i64, isize);
impl_ops_integer_big!(u64, u128, usize, i128);
impl num_traits::CheckedSub for Integer {
fn checked_sub(&self, other: &Self) -> Option<Self> {
Some(Self(match (&self.0, &other.0) {
(IntegerKind::Primitive(lhs), IntegerKind::Primitive(rhs)) => {
op_or_promote!(lhs.checked_sub(rhs), Box::new(BigInt::from(*lhs) - *rhs))
}
(IntegerKind::Primitive(lhs), IntegerKind::Variable(rhs)) => {
IntegerKind::Variable(Box::new(BigInt::from(*lhs) - &**rhs))
}
(IntegerKind::Variable(lhs), IntegerKind::Primitive(rhs)) => {
IntegerKind::Variable(Box::new(&**lhs - *rhs))
}
(IntegerKind::Variable(lhs), IntegerKind::Variable(rhs)) => {
IntegerKind::Variable(Box::new(&**lhs - &**rhs))
}
}))
}
}
impl core::ops::Sub for Integer {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
<Self as CheckedSub>::checked_sub(&self, &rhs).unwrap_or_default()
}
}
impl ToPrimitive for Integer {
fn to_i64(&self) -> Option<i64> {
match &self.0 {
IntegerKind::Primitive(value) => Some(*value as i64),
IntegerKind::Variable(value) => value.to_i64(),
}
}
fn to_u64(&self) -> Option<u64> {
match &self.0 {
#[allow(clippy::cast_sign_loss)] IntegerKind::Primitive(value) => (*value >= 0).then_some(*value as u64),
IntegerKind::Variable(value) => value.to_u64(),
}
}
fn to_i128(&self) -> Option<i128> {
match &self.0 {
IntegerKind::Primitive(value) => Some(*value as i128),
IntegerKind::Variable(value) => value.to_i128(),
}
}
}
macro_rules! impl_from_integer_as_prim {
($($t:ty),*) => {
$(
impl From<$t> for Integer {
fn from(value: $t) -> Self {
#[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] Self(IntegerKind::Primitive(value as isize))
}
}
)*
};
}
macro_rules! impl_from_integer_as_big {
($($t:ty),*) => {
$(
impl From<$t> for Integer {
fn from(value: $t) -> Self {
Self(op_or_promote!(value.to_isize(), Box::new(BigInt::from(value))))
}
}
)*
};
}
#[cfg(target_pointer_width = "32")]
impl_from_integer_as_prim!(u8, u16, i8, i16, i32, isize);
#[cfg(target_pointer_width = "32")]
impl_from_integer_as_big!(u32, i64);
#[cfg(target_pointer_width = "64")]
impl_from_integer_as_prim!(u8, u16, u32, i8, i16, i32, i64, isize);
impl_from_integer_as_big!(u64, u128, i128, usize, BigInt);
impl From<Integer> for BigInt {
fn from(value: Integer) -> Self {
match value.0 {
IntegerKind::Primitive(value) => value.to_bigint().unwrap_or_default(),
IntegerKind::Variable(value) => *value,
}
}
}
impl ToBigInt for Integer {
fn to_bigint(&self) -> Option<BigInt> {
match &self.0 {
IntegerKind::Primitive(value) => Some(BigInt::from(*value)),
IntegerKind::Variable(value) => Some(*value.clone()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TryFromIntegerError {
original: BigInt,
}
impl TryFromIntegerError {
fn new(original: BigInt) -> Self {
TryFromIntegerError { original }
}
fn __description(&self) -> &str {
"out of range conversion regarding integer conversion attempted"
}
pub fn into_original(self) -> BigInt {
self.original
}
}
impl alloc::fmt::Display for TryFromIntegerError {
fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
self.__description().fmt(f)
}
}
macro_rules! impl_try_from_integer {
($($t:ty),*) => {
$(
impl core::convert::TryFrom<Integer> for $t {
type Error = TryFromIntegerError;
fn try_from(value: Integer) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl core::convert::TryFrom<&Integer> for $t {
type Error = TryFromIntegerError;
fn try_from(value: &Integer) -> Result<Self, Self::Error> {
match &value.0 {
IntegerKind::Primitive(value) => (*value).try_into().map_err(|_| TryFromIntegerError::new(value.to_bigint().unwrap_or_default())),
IntegerKind::Variable(value) => (**value).clone().try_into().map_err(|_| TryFromIntegerError::new(*value.clone())),
}
}
}
)*
};
}
impl_try_from_integer!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ConstrainedInteger<const START: i128, const END: i128>(pub(crate) Integer);
impl<const START: i128, const END: i128> ConstrainedInteger<START, END> {
#[cfg(test)]
pub(crate) fn new<T: Into<Integer>>(value: T) -> Self {
Self(value.into())
}
}
impl<const START: i128, const END: i128> AsnType for ConstrainedInteger<START, END> {
const TAG: Tag = Tag::INTEGER;
const CONSTRAINTS: Constraints =
Constraints::new(&[constraints::Constraint::Value(Extensible::new(
constraints::Value::new(constraints::Bounded::const_new(START, END)),
))]);
}
impl<const START: i128, const END: i128> core::ops::Deref for ConstrainedInteger<START, END> {
type Target = Integer;
fn deref(&self) -> &Self::Target {
&self.0
}
}
macro_rules! impl_try_from_integer_constrained {
($($t:ty),*) => {
$(
impl<const START: i128, const END: i128> TryFrom<$t> for ConstrainedInteger<START, END> {
type Error = TryFromIntegerError;
fn try_from(value: $t) -> Result<Self, Self::Error> {
if value as i128 >= START && value as i128 <= END {
Ok(Self(value.into()))
} else {
Err(TryFromIntegerError::new(value.into()))
}
}
}
)*
}
}
impl_try_from_integer_constrained!(isize, i32, i64, i128);
pub trait IntegerType:
Sized
+ Clone
+ core::fmt::Debug
+ core::fmt::Display
+ Default
+ TryInto<i64>
+ TryInto<i128>
+ TryInto<isize>
+ TryFrom<i64>
+ TryFrom<i128>
+ TryFrom<isize>
+ TryFrom<BigInt>
+ Into<BigInt>
+ ToBigInt
+ num_traits::CheckedAdd
+ num_traits::CheckedSub
+ core::cmp::PartialOrd
+ core::cmp::PartialEq
+ num_traits::ToPrimitive
{
const WIDTH: u32;
const BYTE_WIDTH: usize = Self::WIDTH as usize / 8;
const ZERO: Self;
type UnsignedPair: IntegerType;
type SignedPair: IntegerType;
fn try_from_bytes(input: &[u8], codec: crate::Codec)
-> Result<Self, crate::error::DecodeError>;
fn try_from_signed_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError>;
fn try_from_unsigned_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError>;
fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize);
fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize);
fn wrapping_unsigned_add(self, other: Self::UnsignedPair) -> Self;
fn is_negative(&self) -> bool;
fn is_signed(&self) -> bool;
fn to_integer(self) -> Integer;
}
trait MinFixedSizeIntegerBytes: IntegerType + ToBytes {
#[inline(always)]
fn needed_as_be_bytes<const N: usize>(&self, signed: bool) -> ([u8; N], usize) {
let bytes: [u8; N] = self.to_le_bytes().as_ref().try_into().unwrap_or([0; N]);
let needed = if signed {
self.signed_bytes_needed()
} else {
self.unsigned_bytes_needed()
};
let mut slice_reversed: [u8; N] = [0; N];
for i in 0..needed {
slice_reversed[i] = bytes[needed - 1 - i];
}
(slice_reversed, needed)
}
fn unsigned_bytes_needed(&self) -> usize;
fn signed_bytes_needed(&self) -> usize;
}
macro_rules! integer_type_impl {
((signed $t1:ty, $t2:ty), $($ts:tt)*) => {
impl IntegerType for $t1 {
const WIDTH: u32 = <$t1>::BITS;
const ZERO: $t1 = 0 as $t1;
type UnsignedPair = $t2;
type SignedPair = $t1;
#[inline(always)]
fn try_from_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
Self::try_from_signed_bytes(input, codec)
}
#[inline(always)]
fn try_from_signed_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
const BYTE_SIZE: usize = (<$t1>::BITS / 8) as usize;
if input.is_empty() {
return Err(crate::error::DecodeError::unexpected_empty_input(codec));
}
if input.len() > BYTE_SIZE {
return Err(crate::error::DecodeError::integer_overflow(<$t1>::BITS, codec));
}
let mut result = (input[0] as $t1) << (BYTE_SIZE - 1) * 8;
result >>= (BYTE_SIZE - input.len()) * 8;
for (i, &byte) in input.iter().skip(1).enumerate() {
result |= (byte as $t1) << (8 * (input.len() - 2 - i));
}
Ok(result)
}
#[inline(always)]
fn try_from_unsigned_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
<$t1>::try_from(<$t2>::try_from_bytes(input, codec)?)
.map_err(|_| {
crate::error::DecodeError::integer_type_conversion_failed(alloc::format!("Failed to create signed integer from unsigned bytes, target bit-size {}, with bytes {:?}", <$t1>::BITS, input).into(), codec)
})
}
#[inline(always)]
fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
const N: usize = core::mem::size_of::<$t1>();
self.needed_as_be_bytes::<N>( true)
}
#[inline(always)]
fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
const N: usize = core::mem::size_of::<$t2>();
(*self as $t2).needed_as_be_bytes::<N>( false)
}
fn wrapping_unsigned_add(self, other: $t2) -> Self {
self.wrapping_add(other as $t1)
}
fn is_negative(&self) -> bool {
<Self as Signed>::is_negative(self)
}
fn is_signed(&self) -> bool {
true
}
fn to_integer(self) -> Integer {
Integer(op_or_promote!(self.to_isize(), Box::new(self.to_bigint().unwrap_or_default())))
}
}
impl MinFixedSizeIntegerBytes for $t1 {
#[inline(always)]
fn unsigned_bytes_needed(&self) -> usize {
(*self as $t2).unsigned_bytes_needed()
}
#[inline(always)]
fn signed_bytes_needed(&self) -> usize {
let leading_bits = if Signed::is_negative(self) {
self.leading_ones() as usize
} else {
self.leading_zeros() as usize
};
let full_bytes = Self::BYTE_WIDTH - leading_bits / 8;
let extra_byte = (leading_bits % 8 == 0) as usize;
full_bytes + extra_byte
}
}
integer_type_impl!($($ts)*);
};
((unsigned $t1:ty, $t2:ty), $($ts:tt)*) => {
impl IntegerType for $t1 {
const WIDTH: u32 = <$t1>::BITS;
const ZERO: $t1 = 0 as $t1;
type UnsignedPair = $t1;
type SignedPair = $t2;
#[inline(always)]
fn try_from_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
Self::try_from_unsigned_bytes(input, codec)
}
#[inline(always)]
fn try_from_signed_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
<$t1>::try_from(<$t2>::try_from_bytes(input, codec)?)
.map_err(|_| {
crate::error::DecodeError::integer_type_conversion_failed(alloc::format!("Failed to create unsigned integer from signed bytes, target bit-size {}, with bytes: {:?}", <$t1>::BITS, input).into(), codec)
})
}
#[inline(always)]
fn try_from_unsigned_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
const BYTE_SIZE: usize = (<$t1>::BITS / 8) as usize;
if input.is_empty() {
return Err(crate::error::DecodeError::unexpected_empty_input(codec));
}
if input.len() > BYTE_SIZE {
return Err(crate::error::DecodeError::integer_overflow(<$t1>::BITS, codec));
}
let mut result: $t1 = 0;
let start_shift = (input.len() - 1) * 8;
for (i, &byte) in input.iter().enumerate() {
let shift = start_shift - (i * 8);
result |= (byte as $t1) << shift;
}
Ok(result)
}
#[inline(always)]
fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
const N: usize = core::mem::size_of::<$t2>();
(*self as $t2).needed_as_be_bytes::<N>(true)
}
#[inline(always)]
fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
const N: usize = core::mem::size_of::<$t1>();
self.needed_as_be_bytes::<N>(false)
}
fn wrapping_unsigned_add(self, other: $t1) -> Self {
self.wrapping_add(other)
}
fn is_negative(&self) -> bool {
false
}
fn is_signed(&self) -> bool {
false
}
fn to_integer(self) -> Integer {
Integer(op_or_promote!(self.to_isize(), Box::new(self.to_bigint().unwrap_or_default())))
}
}
impl MinFixedSizeIntegerBytes for $t1 {
#[inline(always)]
fn unsigned_bytes_needed(&self) -> usize {
if self.is_zero() {
1
} else {
let significant_bits = Self::WIDTH as usize - self.leading_zeros() as usize;
significant_bits.div_ceil(8)
}
}
#[inline(always)]
fn signed_bytes_needed(&self) -> usize {
(*self as $t2).signed_bytes_needed()
}
}
integer_type_impl!($($ts)*);
};
(,) => {};
() => {};
}
integer_type_impl!(
(unsigned u8, i16),
(signed i8, u8),
(unsigned u16, i32),
(signed i16, u16),
(unsigned u32, i64),
(signed i32, u32),
(unsigned u64, i128),
(signed i64, u64),
(unsigned u128, i128),
(signed i128, u128),
(unsigned usize, i128),
(signed isize, usize),
);
impl IntegerType for BigInt {
const WIDTH: u32 = u32::MAX;
const ZERO: BigInt = BigInt::ZERO;
type UnsignedPair = Self;
type SignedPair = Self;
#[inline(always)]
fn try_from_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
if input.is_empty() {
return Err(crate::error::DecodeError::unexpected_empty_input(codec));
}
Ok(BigInt::from_signed_bytes_be(input))
}
#[inline(always)]
fn try_from_signed_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
Self::try_from_bytes(input, codec)
}
#[inline(always)]
fn try_from_unsigned_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
if input.is_empty() {
return Err(crate::error::DecodeError::unexpected_empty_input(codec));
}
Ok(BigUint::from_bytes_be(input).into())
}
#[inline(always)]
fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
let bytes = self.to_signed_bytes_be();
let len = bytes.len();
(bytes, len)
}
#[inline(always)]
fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
let bytes = self.to_biguint().unwrap_or_default().to_bytes_be();
let len = bytes.len();
(bytes, len)
}
fn wrapping_unsigned_add(self, other: Self) -> Self {
self + other
}
fn is_negative(&self) -> bool {
<Self as Signed>::is_negative(self)
}
fn is_signed(&self) -> bool {
true
}
fn to_integer(self) -> Integer {
Integer(IntegerKind::Variable(Box::new(self)))
}
}
enum IntegerBytesRef<T: AsRef<[u8]>> {
Stack([u8; core::mem::size_of::<isize>()]),
Heap(T),
}
impl<T: AsRef<[u8]>> AsRef<[u8]> for IntegerBytesRef<T> {
fn as_ref(&self) -> &[u8] {
match self {
IntegerBytesRef::Stack(arr) => arr,
IntegerBytesRef::Heap(slice) => slice.as_ref(),
}
}
}
impl IntegerType for Integer {
const WIDTH: u32 = u32::MAX;
const ZERO: Integer = Integer::ZERO;
type UnsignedPair = Self;
type SignedPair = Self;
#[inline(always)]
fn try_from_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
if input.is_empty() {
return Err(crate::error::DecodeError::unexpected_empty_input(codec));
}
isize::try_from_bytes(input, codec)
.map(IntegerKind::Primitive)
.or_else(|_| {
BigInt::try_from_bytes(input, codec)
.map(Box::new)
.map(IntegerKind::Variable)
})
.map(Self)
}
#[inline(always)]
fn try_from_signed_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
Self::try_from_bytes(input, codec)
}
#[inline(always)]
fn try_from_unsigned_bytes(
input: &[u8],
codec: crate::Codec,
) -> Result<Self, crate::error::DecodeError> {
if input.is_empty() {
return Err(crate::error::DecodeError::unexpected_empty_input(codec));
}
isize::try_from_unsigned_bytes(input, codec)
.map(IntegerKind::Primitive)
.or_else(|_| {
BigInt::try_from_unsigned_bytes(input, codec)
.map(Box::new)
.map(IntegerKind::Variable)
})
.map(Self)
}
#[inline(always)]
fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
match &self.0 {
IntegerKind::Primitive(value) => {
let (bytes, len) = <isize as IntegerType>::to_signed_bytes_be(value);
(
IntegerBytesRef::Stack(bytes.as_ref().try_into().unwrap_or_default()),
len,
)
}
IntegerKind::Variable(value) => {
let (bytes, len) = <BigInt as IntegerType>::to_signed_bytes_be(value);
(IntegerBytesRef::Heap(bytes), len)
}
}
}
#[inline(always)]
fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
match &self.0 {
IntegerKind::Primitive(value) => {
let (bytes, len) = <isize as IntegerType>::to_unsigned_bytes_be(value);
(
IntegerBytesRef::Stack(bytes.as_ref().try_into().unwrap_or_default()),
len,
)
}
IntegerKind::Variable(value) => {
let (bytes, len) = <BigInt as IntegerType>::to_signed_bytes_be(value);
(IntegerBytesRef::Heap(bytes), len)
}
}
}
fn wrapping_unsigned_add(self, other: Self) -> Self {
self + other
}
fn is_negative(&self) -> bool {
match &self.0 {
IntegerKind::Primitive(value) => <isize as IntegerType>::is_negative(value),
IntegerKind::Variable(value) => <BigInt as IntegerType>::is_negative(value),
}
}
fn is_signed(&self) -> bool {
true
}
fn to_integer(self) -> Integer {
self
}
}
#[cfg(test)]
macro_rules! assert_integer_round_trip {
($t:ty, $value:expr, $expected_unsigned:expr, $expected_signed:expr) => {{
let value = <$t>::try_from($value).unwrap();
let (unsigned_bytes, unsigned_needed) = value.to_unsigned_bytes_be();
assert_eq!(
&unsigned_bytes.as_ref()[..unsigned_needed],
$expected_unsigned,
"Unsigned bytes mismatch for {}",
stringify!($value)
);
#[allow(unused_comparisons)]
if $value >= 0 || stringify!($t).starts_with('u') {
assert_eq!(
<$t>::try_from_unsigned_bytes(
&unsigned_bytes.as_ref()[..unsigned_needed],
crate::Codec::Oer
)
.ok(),
Some(value),
"Round-trip failed for unsigned bytes of {}",
stringify!($value)
);
}
let (signed_bytes, signed_needed) = value.to_signed_bytes_be();
assert_eq!(
&signed_bytes.as_ref()[..signed_needed],
$expected_signed,
"Signed bytes mismatch for {}",
stringify!($value)
);
assert_eq!(
<$t>::try_from_signed_bytes(&signed_bytes.as_ref()[..signed_needed], crate::Codec::Oer)
.ok(),
Some(value),
"Round-trip failed for signed bytes of {}",
stringify!($value)
);
let integer = Integer::from($value);
let (bytes, needed) = integer.to_signed_bytes_be();
assert_eq!(
Integer::try_from_signed_bytes(&bytes.as_ref()[..needed], crate::Codec::Oer).ok(),
Some($value.into()),
"Round-trip failed for Integer({})",
stringify!($value)
);
assert_eq!(
&bytes.as_ref()[..needed],
$expected_signed,
"Signed bytes mismatch for Integer({})",
stringify!($value)
);
}};
}
macro_rules! test_integer_conversions_and_operations {
($($t:ident),*) => {
#[cfg(test)]
mod tests {
use crate::prelude::*;
use super::*;
$(
#[test]
fn $t() {
let min = <$t>::MIN as i128;
let max = <$t>::MAX as u128;
if min >= isize::MIN as i128 {
assert!(matches!(min.into(), Integer(IntegerKind::Primitive(_))));
} else {
assert!(matches!(min.into(), Integer(IntegerKind::Variable(_))));
}
if max <= isize::MAX as u128 {
assert!(matches!(max.into(), Integer(IntegerKind::Primitive(_))));
} else {
assert!(matches!(max.into(), Integer(IntegerKind::Variable(_))));
}
assert_integer_round_trip!($t, 1, &[1], &[1]);
if <$t>::MAX as u128 >= i8::MAX as u128 {
assert_integer_round_trip!($t, i8::MAX as $t, &[127], &[127]);
}
if <$t>::MAX as u128 >= i16::MAX as u128 {
assert_integer_round_trip!($t, i16::MAX as $t, &[127, 255], &[127, 255]);
assert_integer_round_trip!($t, 128, &[128], &[0, 128]);
}
if <$t>::MAX as u128 >= i32::MAX as u128 {
assert_integer_round_trip!($t, i32::MAX as $t, &[127, 255, 255, 255], &[127, 255, 255, 255]);
assert_integer_round_trip!($t, (i16::MAX as $t + 1), &[128, 0], &[0, 128, 0]);
}
if <$t>::MAX as u128 >= i64::MAX as u128 {
assert_integer_round_trip!($t, i64::MAX as $t, &[127, 255, 255, 255, 255, 255, 255, 255], &[127, 255, 255, 255, 255, 255, 255, 255]);
}
match stringify!($t) {
"i8" => {
assert_integer_round_trip!($t, -1, &[255], &[255]);
},
"i16" => {
assert_integer_round_trip!($t, -1, &[255, 255], &[255]);
},
"i32" => {
assert_integer_round_trip!($t, -1, &[255, 255, 255, 255], &[255]);
},
"i64" => {
assert_integer_round_trip!($t, -1, &[255, 255, 255, 255, 255, 255, 255, 255], &[255]);
},
_ => {},
}
}
)*
#[test]
fn test_overflow_addition() {
let max = Integer(IntegerKind::Primitive(isize::MAX));
let one = Integer(IntegerKind::Primitive(1));
let result = max + one;
assert!(matches!(result, Integer(IntegerKind::Variable(_))));
}
#[test]
fn test_overflow_subtraction() {
let min = Integer(IntegerKind::Primitive(isize::MIN));
let one = Integer(IntegerKind::Primitive(1));
let result = min - one;
assert!(matches!(result, Integer(IntegerKind::Variable(_))));
}
#[test]
fn test_primitive_to_i64() {
let zero = Integer(IntegerKind::Primitive(0));
let positive = Integer(IntegerKind::Primitive(42));
let negative = Integer(IntegerKind::Primitive(-42));
let max = Integer(IntegerKind::Primitive(isize::MAX));
let min = Integer(IntegerKind::Primitive(isize::MIN));
assert_eq!(zero.to_i64(), Some(0i64));
assert_eq!(positive.to_i64(), Some(42i64));
assert_eq!(negative.to_i64(), Some(-42i64));
assert_eq!(max.to_i64(), Some(isize::MAX as i64));
assert_eq!(min.to_i64(), Some(isize::MIN as i64));
}
#[test]
fn test_primitive_to_u64() {
let zero = Integer(IntegerKind::Primitive(0));
let positive = Integer(IntegerKind::Primitive(42));
let negative = Integer(IntegerKind::Primitive(-42));
let max = Integer(IntegerKind::Primitive(isize::MAX));
assert_eq!(zero.to_u64(), Some(0u64));
assert_eq!(positive.to_u64(), Some(42u64));
assert_eq!(negative.to_u64(), None); assert_eq!(max.to_u64(), Some(isize::MAX as u64));
}
#[test]
fn test_primitive_to_i128() {
let zero = Integer(IntegerKind::Primitive(0));
let positive = Integer(IntegerKind::Primitive(42));
let negative = Integer(IntegerKind::Primitive(-42));
let max = Integer(IntegerKind::Primitive(isize::MAX));
let min = Integer(IntegerKind::Primitive(isize::MIN));
assert_eq!(zero.to_i128(), Some(0i128));
assert_eq!(positive.to_i128(), Some(42i128));
assert_eq!(negative.to_i128(), Some(-42i128));
assert_eq!(max.to_i128(), Some(isize::MAX as i128));
assert_eq!(min.to_i128(), Some(isize::MIN as i128));
}
#[test]
fn test_variable_to_i64() {
let zero = Integer(IntegerKind::Variable(Box::new(BigInt::from(0))));
let positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(100))));
let negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(-100))));
let large_positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(i64::MAX) + 1)));
let large_negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(i64::MIN) - 1)));
assert_eq!(zero.to_i64(), Some(0i64));
assert_eq!(positive.to_i64(), Some(100i64));
assert_eq!(negative.to_i64(), Some(-100i64));
assert_eq!(large_positive.to_i64(), None); assert_eq!(large_negative.to_i64(), None); }
#[test]
fn test_variable_to_u64() {
let zero = Integer(IntegerKind::Variable(Box::new(BigInt::from(0))));
let positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(100))));
let negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(-100))));
let large_positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(u64::MAX) + 1)));
assert_eq!(zero.to_u64(), Some(0u64));
assert_eq!(positive.to_u64(), Some(100u64));
assert_eq!(negative.to_u64(), None); assert_eq!(large_positive.to_u64(), None); }
#[test]
fn test_variable_to_i128() {
let zero = Integer(IntegerKind::Variable(Box::new(BigInt::from(0))));
let positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(1000))));
let negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(-1000))));
let very_large_positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(i128::MAX) + BigInt::from(1))));
let very_large_negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(i128::MIN) - BigInt::from(1))));
assert_eq!(zero.to_i128(), Some(0i128));
assert_eq!(positive.to_i128(), Some(1000i128));
assert_eq!(negative.to_i128(), Some(-1000i128));
assert_eq!(very_large_positive.to_i128(), None);
assert_eq!(very_large_negative.to_i128(), None);
}
}
};
}
test_integer_conversions_and_operations!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);