1use crate::types::{constraints, AsnType, Constraints, Extensible, Tag};
2use alloc::boxed::Box;
3use core::hash::Hash;
4use num_bigint::{BigInt, BigUint, ToBigInt};
5use num_traits::{identities::Zero, Signed, ToBytes, ToPrimitive};
6use num_traits::{CheckedAdd, CheckedSub};
7
8#[derive(Debug, Clone, Ord, PartialOrd)]
14#[allow(missing_docs)]
15pub struct Integer(IntegerKind);
16
17macro_rules! op_or_promote {
18    ($rhs:ident . $op:ident ($($args:tt)*), $promote:expr) => {
19        $rhs.$op($($args)*).map(IntegerKind::Primitive).unwrap_or_else(|| IntegerKind::Variable($promote))
20    }
21}
22
23#[derive(Debug, Clone, Ord, PartialOrd)]
25#[allow(missing_docs)]
26pub enum IntegerKind {
27    Primitive(isize),
28    Variable(Box<BigInt>),
29}
30
31impl Integer {
32    pub const ZERO: Self = Self(IntegerKind::Primitive(0));
34    pub const ONE: Self = Self(IntegerKind::Primitive(1));
36}
37
38impl Default for Integer {
39    fn default() -> Self {
40        Self::ZERO
41    }
42}
43
44impl core::fmt::Display for Integer {
45    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
46        match &self.0 {
47            IntegerKind::Primitive(value) => write!(f, "{value}"),
48            IntegerKind::Variable(value) => write!(f, "{value}"),
49        }
50    }
51}
52
53impl PartialEq for Integer {
54    fn eq(&self, other: &Self) -> bool {
55        self.0 == other.0
56    }
57}
58
59impl PartialEq for IntegerKind {
60    fn eq(&self, other: &Self) -> bool {
61        match (self, other) {
62            (IntegerKind::Primitive(lhs), IntegerKind::Primitive(rhs)) => lhs == rhs,
63            (IntegerKind::Variable(lhs), IntegerKind::Variable(rhs)) => lhs == rhs,
64            (IntegerKind::Primitive(lhs), IntegerKind::Variable(rhs)) => {
65                lhs.to_bigint().unwrap_or_default() == **rhs
66            }
67            (IntegerKind::Variable(lhs), IntegerKind::Primitive(rhs)) => {
68                **lhs == rhs.to_bigint().unwrap_or_default()
69            }
70        }
71    }
72}
73
74impl Eq for Integer {}
75
76impl Eq for IntegerKind {}
77
78impl Hash for Integer {
79    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
80        self.0.hash(state);
81    }
82}
83
84impl Hash for IntegerKind {
85    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
86        match self {
87            IntegerKind::Primitive(value) => value.hash(state),
88            IntegerKind::Variable(value) => value.hash(state),
89        }
90    }
91}
92
93impl num_traits::CheckedAdd for Integer {
94    fn checked_add(&self, other: &Self) -> Option<Self> {
95        Some(Self(match (&self.0, &other.0) {
96            (IntegerKind::Primitive(lhs), IntegerKind::Primitive(rhs)) => {
97                op_or_promote!(lhs.checked_add(rhs), Box::new(BigInt::from(*lhs) + *rhs))
98            }
99            (IntegerKind::Primitive(lhs), IntegerKind::Variable(rhs)) => {
100                IntegerKind::Variable(Box::new(&**rhs + lhs))
101            }
102            (IntegerKind::Variable(lhs), IntegerKind::Primitive(rhs)) => {
103                IntegerKind::Variable(Box::new(&**lhs + *rhs))
104            }
105            (IntegerKind::Variable(lhs), IntegerKind::Variable(rhs)) => {
106                IntegerKind::Variable(Box::new(&**lhs + &**rhs))
107            }
108        }))
109    }
110}
111
112impl core::ops::Add for Integer {
113    type Output = Self;
114    fn add(self, rhs: Self) -> Self::Output {
115        <Self as CheckedAdd>::checked_add(&self, &rhs).unwrap_or_default()
116    }
117}
118
119macro_rules! impl_ops_integer {
120    ($($t:ty),*) => {
121        $(
122            impl core::ops::Add<$t> for Integer {
123                type Output = Self;
124                fn add(self, rhs: $t) -> Self::Output {
125                    Self(match self.0 {
126                        IntegerKind::Primitive(lhs) => op_or_promote!(lhs.checked_add(rhs as isize), Box::new(BigInt::from(lhs) + rhs)),
127                        IntegerKind::Variable(lhs) => IntegerKind::Variable(Box::new(*lhs + rhs)),
128                    })
129                }
130            }
131            impl core::ops::Sub<$t> for Integer {
132                type Output = Self;
133                fn sub(self, rhs: $t) -> Self::Output {
134                    Self(match self.0 {
135                        IntegerKind::Primitive(lhs) => op_or_promote!(lhs.checked_sub(rhs as isize), Box::new(BigInt::from(lhs) - rhs)),
136                        IntegerKind::Variable(lhs) => IntegerKind::Variable(Box::new(*lhs - rhs)),
137                    })
138                }
139            }
140        )*
141    };
142}
143macro_rules! impl_ops_integer_big {
144    ($($t:ty),*) => {
145        $(
146            impl core::ops::Add<$t> for Integer {
147                type Output = Self;
148                fn add(self, rhs: $t) -> Self::Output {
149                    Self(match self.0 {
150                        IntegerKind::Primitive(lhs) => {
151                            let value = isize::try_from(rhs).ok();
152                            op_or_promote!(value.and_then(|rhs| lhs.checked_add(rhs)), Box::new(BigInt::from(lhs) + rhs))
153                        }
154                        IntegerKind::Variable(lhs) => {
155                            IntegerKind::Variable(Box::new(*lhs + rhs))
156                        }
157                    })
158                }
159            }
160            impl core::ops::Sub<$t> for Integer {
161                type Output = Self;
162                fn sub(self, rhs: $t) -> Self::Output {
163                    Self(match self.0 {
164                        IntegerKind::Primitive(lhs) => {
165                            let value = isize::try_from(rhs).ok();
166                            op_or_promote!(value.and_then(|rhs| lhs.checked_sub(rhs)), Box::new(BigInt::from(lhs) - rhs))
167                        }
168                        IntegerKind::Variable(lhs) => {
169                            IntegerKind::Variable(Box::new(*lhs - rhs))
170                        }
171                    })
172                }
173            }
174        )*
175    };
176}
177
178#[cfg(target_pointer_width = "32")]
180impl_ops_integer!(u8, u16, i8, i16, i32, isize);
181#[cfg(target_pointer_width = "32")]
182impl_ops_integer_big!(u32, i64);
183#[cfg(target_pointer_width = "64")]
185impl_ops_integer!(u8, u16, u32, i8, i16, i32, i64, isize);
186
187impl_ops_integer_big!(u64, u128, usize, i128);
189
190impl num_traits::CheckedSub for Integer {
191    fn checked_sub(&self, other: &Self) -> Option<Self> {
192        Some(Self(match (&self.0, &other.0) {
193            (IntegerKind::Primitive(lhs), IntegerKind::Primitive(rhs)) => {
194                op_or_promote!(lhs.checked_sub(rhs), Box::new(BigInt::from(*lhs) - *rhs))
195            }
196            (IntegerKind::Primitive(lhs), IntegerKind::Variable(rhs)) => {
197                IntegerKind::Variable(Box::new(BigInt::from(*lhs) - &**rhs))
198            }
199            (IntegerKind::Variable(lhs), IntegerKind::Primitive(rhs)) => {
200                IntegerKind::Variable(Box::new(&**lhs - *rhs))
201            }
202            (IntegerKind::Variable(lhs), IntegerKind::Variable(rhs)) => {
203                IntegerKind::Variable(Box::new(&**lhs - &**rhs))
204            }
205        }))
206    }
207}
208
209impl core::ops::Sub for Integer {
210    type Output = Self;
211    fn sub(self, rhs: Self) -> Self::Output {
212        <Self as CheckedSub>::checked_sub(&self, &rhs).unwrap_or_default()
213    }
214}
215
216impl ToPrimitive for Integer {
217    fn to_i64(&self) -> Option<i64> {
218        match &self.0 {
219            IntegerKind::Primitive(value) => Some(*value as i64),
220            IntegerKind::Variable(value) => value.to_i64(),
221        }
222    }
223    fn to_u64(&self) -> Option<u64> {
224        match &self.0 {
225            #[allow(clippy::cast_sign_loss)] IntegerKind::Primitive(value) => (*value >= 0).then_some(*value as u64),
227            IntegerKind::Variable(value) => value.to_u64(),
228        }
229    }
230    fn to_i128(&self) -> Option<i128> {
231        match &self.0 {
232            IntegerKind::Primitive(value) => Some(*value as i128),
233            IntegerKind::Variable(value) => value.to_i128(),
234        }
235    }
236}
237
238macro_rules! impl_from_integer_as_prim {
239    ($($t:ty),*) => {
240        $(
241            impl From<$t> for Integer {
242                fn from(value: $t) -> Self {
243                    #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] Self(IntegerKind::Primitive(value as isize))
245                }
246            }
247        )*
248    };
249}
250
251macro_rules! impl_from_integer_as_big {
252    ($($t:ty),*) => {
253        $(
254            impl From<$t> for Integer {
255                fn from(value: $t) -> Self {
256                    Self(op_or_promote!(value.to_isize(), Box::new(BigInt::from(value))))
257                }
258            }
259        )*
260    };
261}
262#[cfg(target_pointer_width = "32")]
263impl_from_integer_as_prim!(u8, u16, i8, i16, i32, isize);
264#[cfg(target_pointer_width = "32")]
265impl_from_integer_as_big!(u32, i64);
266#[cfg(target_pointer_width = "64")]
267impl_from_integer_as_prim!(u8, u16, u32, i8, i16, i32, i64, isize);
268impl_from_integer_as_big!(u64, u128, i128, usize, BigInt);
270
271impl From<Integer> for BigInt {
272    fn from(value: Integer) -> Self {
273        match value.0 {
274            IntegerKind::Primitive(value) => value.to_bigint().unwrap_or_default(),
275            IntegerKind::Variable(value) => *value,
276        }
277    }
278}
279
280impl ToBigInt for Integer {
281    fn to_bigint(&self) -> Option<BigInt> {
282        match &self.0 {
283            IntegerKind::Primitive(value) => Some(BigInt::from(*value)),
284            IntegerKind::Variable(value) => Some(*value.clone()),
285        }
286    }
287}
288
289#[derive(Debug, Clone, PartialEq, Eq)]
290pub struct TryFromIntegerError {
291    original: BigInt,
292}
293
294impl TryFromIntegerError {
295    fn new(original: BigInt) -> Self {
296        TryFromIntegerError { original }
297    }
298    fn __description(&self) -> &str {
299        "out of range conversion regarding integer conversion attempted"
300    }
301    pub fn into_original(self) -> BigInt {
302        self.original
303    }
304}
305
306impl alloc::fmt::Display for TryFromIntegerError {
307    fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
308        self.__description().fmt(f)
309    }
310}
311macro_rules! impl_try_from_integer {
312    ($($t:ty),*) => {
313        $(
314            impl core::convert::TryFrom<Integer> for $t {
315                type Error = TryFromIntegerError;
316                fn try_from(value: Integer) -> Result<Self, Self::Error> {
317                    Self::try_from(&value)
318                }
319            }
320            impl core::convert::TryFrom<&Integer> for $t {
321                type Error = TryFromIntegerError;
322                fn try_from(value: &Integer) -> Result<Self, Self::Error> {
323                    match &value.0 {
324                        IntegerKind::Primitive(value) => (*value).try_into().map_err(|_| TryFromIntegerError::new(value.to_bigint().unwrap_or_default())),
325                        IntegerKind::Variable(value) => (**value).clone().try_into().map_err(|_| TryFromIntegerError::new(*value.clone())),
326                    }
327                }
328            }
329        )*
330    };
331}
332impl_try_from_integer!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
333
334#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
336pub struct ConstrainedInteger<const START: i128, const END: i128>(pub(crate) Integer);
337
338impl<const START: i128, const END: i128> ConstrainedInteger<START, END> {
339    #[cfg(test)]
341    pub(crate) fn new<T: Into<Integer>>(value: T) -> Self {
342        Self(value.into())
343    }
344}
345
346impl<const START: i128, const END: i128> AsnType for ConstrainedInteger<START, END> {
347    const TAG: Tag = Tag::INTEGER;
348    const CONSTRAINTS: Constraints =
349        Constraints::new(&[constraints::Constraint::Value(Extensible::new(
350            constraints::Value::new(constraints::Bounded::const_new(START, END)),
351        ))]);
352}
353
354impl<const START: i128, const END: i128> core::ops::Deref for ConstrainedInteger<START, END> {
355    type Target = Integer;
356
357    fn deref(&self) -> &Self::Target {
358        &self.0
359    }
360}
361
362macro_rules! impl_try_from_integer_constrained {
363    ($($t:ty),*) => {
364        $(
365            impl<const START: i128, const END: i128> TryFrom<$t> for ConstrainedInteger<START, END> {
366                type Error = TryFromIntegerError;
367                fn try_from(value: $t) -> Result<Self, Self::Error> {
368                    if value as i128 >= START && value as i128 <= END {
369                        Ok(Self(value.into()))
370                    } else {
371                        Err(TryFromIntegerError::new(value.into()))
372                    }
373                }
374            }
375        )*
376    }
377}
378impl_try_from_integer_constrained!(isize, i32, i64, i128);
379
380pub trait IntegerType:
383    Sized
384    + Clone
385    + core::fmt::Debug
386    + core::fmt::Display
387    + Default
388    + TryInto<i64>
389    + TryInto<i128>
390    + TryInto<isize>
391    + TryFrom<i64>
392    + TryFrom<i128>
393    + TryFrom<isize>
394    + TryFrom<BigInt>
395    + Into<BigInt>
396    + ToBigInt
397    + num_traits::CheckedAdd
398    + num_traits::CheckedSub
399    + core::cmp::PartialOrd
400    + core::cmp::PartialEq
401    + num_traits::ToPrimitive
402{
403    const WIDTH: u32;
405    const BYTE_WIDTH: usize = Self::WIDTH as usize / 8;
407    const ZERO: Self;
409    type UnsignedPair: IntegerType;
411    type SignedPair: IntegerType;
414
415    fn try_from_bytes(input: &[u8], codec: crate::Codec)
420        -> Result<Self, crate::error::DecodeError>;
421
422    fn try_from_signed_bytes(
427        input: &[u8],
428        codec: crate::Codec,
429    ) -> Result<Self, crate::error::DecodeError>;
430
431    fn try_from_unsigned_bytes(
436        input: &[u8],
437        codec: crate::Codec,
438    ) -> Result<Self, crate::error::DecodeError>;
439
440    fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize);
442
443    fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize);
445
446    fn wrapping_unsigned_add(self, other: Self::UnsignedPair) -> Self;
450    fn is_negative(&self) -> bool;
452    fn is_signed(&self) -> bool;
454    fn to_integer(self) -> Integer;
456}
457
458trait MinFixedSizeIntegerBytes: IntegerType + ToBytes {
459    #[inline(always)]
464    fn needed_as_be_bytes<const N: usize>(&self, signed: bool) -> ([u8; N], usize) {
465        let bytes: [u8; N] = self.to_le_bytes().as_ref().try_into().unwrap_or([0; N]);
466        let needed = if signed {
467            self.signed_bytes_needed()
468        } else {
469            self.unsigned_bytes_needed()
470        };
471        let mut slice_reversed: [u8; N] = [0; N];
472        for i in 0..needed {
474            slice_reversed[i] = bytes[needed - 1 - i];
475        }
476        (slice_reversed, needed)
477    }
478    fn unsigned_bytes_needed(&self) -> usize;
480
481    fn signed_bytes_needed(&self) -> usize;
483}
484
485macro_rules! integer_type_impl {
486    ((signed $t1:ty, $t2:ty), $($ts:tt)*) => {
487        impl IntegerType for $t1 {
488            const WIDTH: u32 = <$t1>::BITS;
489            const ZERO: $t1 = 0 as $t1;
490            type UnsignedPair = $t2;
491            type SignedPair = $t1;
492
493            #[inline(always)]
494            fn try_from_bytes(
495                input: &[u8],
496                codec: crate::Codec,
497            ) -> Result<Self, crate::error::DecodeError> {
498                Self::try_from_signed_bytes(input, codec)
499            }
500
501            #[inline(always)]
502            fn try_from_signed_bytes(
503                input: &[u8],
504                codec: crate::Codec,
505            ) -> Result<Self, crate::error::DecodeError> {
506                const BYTE_SIZE: usize = (<$t1>::BITS / 8) as usize;
507                if input.is_empty() {
508                    return Err(crate::error::DecodeError::unexpected_empty_input(codec));
509                }
510                if input.len() > BYTE_SIZE {
511                    return Err(crate::error::DecodeError::integer_overflow(<$t1>::BITS, codec));
512                }
513
514                let mut result = (input[0] as $t1) << (BYTE_SIZE - 1) * 8;
517                 result >>= (BYTE_SIZE - input.len()) * 8;
518                 for (i, &byte) in input.iter().skip(1).enumerate() {
521                     result |= (byte as $t1) << (8 * (input.len() - 2 - i));
522                 }
523
524                Ok(result)
525
526            }
527
528            #[inline(always)]
529            fn try_from_unsigned_bytes(
530                input: &[u8],
531                codec: crate::Codec,
532            ) -> Result<Self, crate::error::DecodeError> {
533                <$t1>::try_from(<$t2>::try_from_bytes(input, codec)?)
534                    .map_err(|_| {
535                    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)
536                })
537
538            }
539            #[inline(always)]
540            fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
541                const N: usize = core::mem::size_of::<$t1>();
542                self.needed_as_be_bytes::<N>( true)
543            }
544            #[inline(always)]
545            fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
546                const N: usize = core::mem::size_of::<$t2>();
547                (*self as $t2).needed_as_be_bytes::<N>( false)
548            }
549
550            fn wrapping_unsigned_add(self, other: $t2) -> Self {
551                self.wrapping_add(other as $t1)
552            }
553            fn is_negative(&self) -> bool {
554                <Self as Signed>::is_negative(self)
555            }
556            fn is_signed(&self) -> bool {
557                true
558            }
559
560            fn to_integer(self) -> Integer {
561                Integer(op_or_promote!(self.to_isize(), Box::new(self.to_bigint().unwrap_or_default())))
562            }
563        }
564        impl MinFixedSizeIntegerBytes for $t1 {
565
566            #[inline(always)]
567            fn unsigned_bytes_needed(&self) -> usize {
568                (*self as $t2).unsigned_bytes_needed()
569            }
570            #[inline(always)]
571            fn signed_bytes_needed(&self) -> usize {
572                let leading_bits = if Signed::is_negative(self) {
573                    self.leading_ones() as usize
574                } else {
575                    self.leading_zeros() as usize
576                };
577                let full_bytes = Self::BYTE_WIDTH - leading_bits / 8;
578                let extra_byte = (leading_bits % 8 == 0) as usize;
579                full_bytes + extra_byte
580
581            }
582        }
583
584        integer_type_impl!($($ts)*);
585    };
586    ((unsigned $t1:ty, $t2:ty), $($ts:tt)*) => {
587
588
589
590        impl IntegerType for $t1 {
591            const WIDTH: u32 = <$t1>::BITS;
592            const ZERO: $t1 = 0 as $t1;
593            type UnsignedPair = $t1;
594            type SignedPair = $t2;
595
596            #[inline(always)]
597            fn try_from_bytes(
598                input: &[u8],
599                codec: crate::Codec,
600            ) -> Result<Self, crate::error::DecodeError> {
601                Self::try_from_unsigned_bytes(input, codec)
602            }
603
604            #[inline(always)]
605            fn try_from_signed_bytes(
606                input: &[u8],
607                codec: crate::Codec,
608            ) -> Result<Self, crate::error::DecodeError> {
609                <$t1>::try_from(<$t2>::try_from_bytes(input, codec)?)
610                    .map_err(|_| {
611                    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)
612                })
613            }
614
615            #[inline(always)]
616            fn try_from_unsigned_bytes(
617                input: &[u8],
618                codec: crate::Codec,
619            ) -> Result<Self, crate::error::DecodeError> {
620                const BYTE_SIZE: usize = (<$t1>::BITS / 8) as usize;
621                if input.is_empty() {
622                    return Err(crate::error::DecodeError::unexpected_empty_input(codec));
623                }
624                if input.len() > BYTE_SIZE {
625                    return Err(crate::error::DecodeError::integer_overflow(<$t1>::BITS, codec));
626                }
627
628                let mut result: $t1 = 0;
630                let start_shift = (input.len() - 1) * 8;
632                for (i, &byte) in input.iter().enumerate() {
633                    let shift = start_shift - (i * 8);
634                    result |= (byte as $t1) << shift;
635                }
636                Ok(result)
637            }
638
639            #[inline(always)]
643            fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
644                const N: usize = core::mem::size_of::<$t2>();
645                (*self as $t2).needed_as_be_bytes::<N>(true)
646            }
647            #[inline(always)]
648            fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
649                const N: usize = core::mem::size_of::<$t1>();
650                self.needed_as_be_bytes::<N>(false)
651            }
652
653            fn wrapping_unsigned_add(self, other: $t1) -> Self {
654                self.wrapping_add(other)
655            }
656            fn is_negative(&self) -> bool {
657                false
658            }
659            fn is_signed(&self) -> bool {
660                false
661            }
662            fn to_integer(self) -> Integer {
663                Integer(op_or_promote!(self.to_isize(), Box::new(self.to_bigint().unwrap_or_default())))
664            }
665        }
666        impl MinFixedSizeIntegerBytes for $t1 {
667            #[inline(always)]
668            fn unsigned_bytes_needed(&self) -> usize {
669                if self.is_zero() {
670                    1
671                } else {
672                    let significant_bits = Self::WIDTH as usize - self.leading_zeros() as usize;
673                    significant_bits.div_ceil(8)
674                }
675            }
676            #[inline(always)]
677            fn signed_bytes_needed(&self) -> usize {
678                (*self as $t2).signed_bytes_needed()
679            }
680        }
681
682        integer_type_impl!($($ts)*);
683    };
684    (,) => {};
685    () => {};
686}
687
688integer_type_impl!(
689    (unsigned u8, i16),
690    (signed i8, u8),
691    (unsigned u16, i32),
692    (signed i16, u16),
693    (unsigned u32, i64),
694    (signed i32, u32),
695    (unsigned u64, i128),
696    (signed i64, u64),
697    (unsigned u128, i128),
699    (signed i128, u128),
700    (unsigned usize, i128),
701    (signed isize, usize),
702);
703
704impl IntegerType for BigInt {
705    const WIDTH: u32 = u32::MAX;
706    const ZERO: BigInt = BigInt::ZERO;
707    type UnsignedPair = Self;
708    type SignedPair = Self;
709
710    #[inline(always)]
711    fn try_from_bytes(
712        input: &[u8],
713        codec: crate::Codec,
714    ) -> Result<Self, crate::error::DecodeError> {
715        if input.is_empty() {
716            return Err(crate::error::DecodeError::unexpected_empty_input(codec));
717        }
718
719        Ok(BigInt::from_signed_bytes_be(input))
720    }
721
722    #[inline(always)]
723    fn try_from_signed_bytes(
724        input: &[u8],
725        codec: crate::Codec,
726    ) -> Result<Self, crate::error::DecodeError> {
727        Self::try_from_bytes(input, codec)
728    }
729
730    #[inline(always)]
731    fn try_from_unsigned_bytes(
732        input: &[u8],
733        codec: crate::Codec,
734    ) -> Result<Self, crate::error::DecodeError> {
735        if input.is_empty() {
736            return Err(crate::error::DecodeError::unexpected_empty_input(codec));
737        }
738
739        Ok(BigUint::from_bytes_be(input).into())
740    }
741    #[inline(always)]
742    fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
743        let bytes = self.to_signed_bytes_be();
744        let len = bytes.len();
745        (bytes, len)
746    }
747    #[inline(always)]
748    fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
749        let bytes = self.to_biguint().unwrap_or_default().to_bytes_be();
750        let len = bytes.len();
751        (bytes, len)
752    }
753
754    fn wrapping_unsigned_add(self, other: Self) -> Self {
755        self + other
756    }
757    fn is_negative(&self) -> bool {
758        <Self as Signed>::is_negative(self)
759    }
760    fn is_signed(&self) -> bool {
761        true
762    }
763    fn to_integer(self) -> Integer {
764        Integer(IntegerKind::Variable(Box::new(self)))
765    }
766}
767enum IntegerBytesRef<T: AsRef<[u8]>> {
771    Stack([u8; core::mem::size_of::<isize>()]),
772    Heap(T),
773}
774
775impl<T: AsRef<[u8]>> AsRef<[u8]> for IntegerBytesRef<T> {
776    fn as_ref(&self) -> &[u8] {
777        match self {
778            IntegerBytesRef::Stack(arr) => arr,
779            IntegerBytesRef::Heap(slice) => slice.as_ref(),
780        }
781    }
782}
783
784impl IntegerType for Integer {
785    const WIDTH: u32 = u32::MAX;
786    const ZERO: Integer = Integer::ZERO;
787    type UnsignedPair = Self;
788    type SignedPair = Self;
789
790    #[inline(always)]
791    fn try_from_bytes(
792        input: &[u8],
793        codec: crate::Codec,
794    ) -> Result<Self, crate::error::DecodeError> {
795        if input.is_empty() {
796            return Err(crate::error::DecodeError::unexpected_empty_input(codec));
797        }
798        isize::try_from_bytes(input, codec)
799            .map(IntegerKind::Primitive)
800            .or_else(|_| {
801                BigInt::try_from_bytes(input, codec)
802                    .map(Box::new)
803                    .map(IntegerKind::Variable)
804            })
805            .map(Self)
806    }
807
808    #[inline(always)]
809    fn try_from_signed_bytes(
810        input: &[u8],
811        codec: crate::Codec,
812    ) -> Result<Self, crate::error::DecodeError> {
813        Self::try_from_bytes(input, codec)
814    }
815
816    #[inline(always)]
817    fn try_from_unsigned_bytes(
818        input: &[u8],
819        codec: crate::Codec,
820    ) -> Result<Self, crate::error::DecodeError> {
821        if input.is_empty() {
822            return Err(crate::error::DecodeError::unexpected_empty_input(codec));
823        }
824
825        isize::try_from_unsigned_bytes(input, codec)
826            .map(IntegerKind::Primitive)
827            .or_else(|_| {
828                BigInt::try_from_unsigned_bytes(input, codec)
829                    .map(Box::new)
830                    .map(IntegerKind::Variable)
831            })
832            .map(Self)
833    }
834    #[inline(always)]
835    fn to_signed_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
836        match &self.0 {
837            IntegerKind::Primitive(value) => {
838                let (bytes, len) = <isize as IntegerType>::to_signed_bytes_be(value);
839                (
840                    IntegerBytesRef::Stack(bytes.as_ref().try_into().unwrap_or_default()),
841                    len,
842                )
843            }
844            IntegerKind::Variable(value) => {
845                let (bytes, len) = <BigInt as IntegerType>::to_signed_bytes_be(value);
846                (IntegerBytesRef::Heap(bytes), len)
847            }
848        }
849    }
850    #[inline(always)]
851    fn to_unsigned_bytes_be(&self) -> (impl AsRef<[u8]>, usize) {
852        match &self.0 {
853            IntegerKind::Primitive(value) => {
854                let (bytes, len) = <isize as IntegerType>::to_unsigned_bytes_be(value);
855                (
856                    IntegerBytesRef::Stack(bytes.as_ref().try_into().unwrap_or_default()),
857                    len,
858                )
859            }
860            IntegerKind::Variable(value) => {
861                let (bytes, len) = <BigInt as IntegerType>::to_signed_bytes_be(value);
862                (IntegerBytesRef::Heap(bytes), len)
863            }
864        }
865    }
866
867    fn wrapping_unsigned_add(self, other: Self) -> Self {
868        self + other
869    }
870    fn is_negative(&self) -> bool {
871        match &self.0 {
872            IntegerKind::Primitive(value) => <isize as IntegerType>::is_negative(value),
873            IntegerKind::Variable(value) => <BigInt as IntegerType>::is_negative(value),
874        }
875    }
876    fn is_signed(&self) -> bool {
877        true
878    }
879    fn to_integer(self) -> Integer {
880        self
881    }
882}
883
884#[cfg(test)]
885macro_rules! assert_integer_round_trip {
886    ($t:ty, $value:expr, $expected_unsigned:expr, $expected_signed:expr) => {{
887        let value = <$t>::try_from($value).unwrap();
888
889        let (unsigned_bytes, unsigned_needed) = value.to_unsigned_bytes_be();
891        assert_eq!(
892            &unsigned_bytes.as_ref()[..unsigned_needed],
893            $expected_unsigned,
894            "Unsigned bytes mismatch for {}",
895            stringify!($value)
896        );
897
898        #[allow(unused_comparisons)]
900        if $value >= 0 || stringify!($t).starts_with('u') {
901            assert_eq!(
902                <$t>::try_from_unsigned_bytes(
903                    &unsigned_bytes.as_ref()[..unsigned_needed],
904                    crate::Codec::Oer
905                )
906                .ok(),
907                Some(value),
908                "Round-trip failed for unsigned bytes of {}",
909                stringify!($value)
910            );
911        }
912
913        let (signed_bytes, signed_needed) = value.to_signed_bytes_be();
915        assert_eq!(
916            &signed_bytes.as_ref()[..signed_needed],
917            $expected_signed,
918            "Signed bytes mismatch for {}",
919            stringify!($value)
920        );
921
922        assert_eq!(
924            <$t>::try_from_signed_bytes(&signed_bytes.as_ref()[..signed_needed], crate::Codec::Oer)
925                .ok(),
926            Some(value),
927            "Round-trip failed for signed bytes of {}",
928            stringify!($value)
929        );
930        let integer = Integer::from($value);
932        let (bytes, needed) = integer.to_signed_bytes_be();
933        assert_eq!(
934            Integer::try_from_signed_bytes(&bytes.as_ref()[..needed], crate::Codec::Oer).ok(),
935            Some($value.into()),
936            "Round-trip failed for Integer({})",
937            stringify!($value)
938        );
939        assert_eq!(
941            &bytes.as_ref()[..needed],
942            $expected_signed,
943            "Signed bytes mismatch for Integer({})",
944            stringify!($value)
945        );
946    }};
947}
948
949macro_rules! test_integer_conversions_and_operations {
950    ($($t:ident),*) => {
951        #[cfg(test)]
952        mod tests {
953            use crate::prelude::*;
954            use super::*;
955
956            $(
957                #[test]
958                fn $t() {
959                    let min = <$t>::MIN as i128;
960                    let max = <$t>::MAX as u128;
961
962                    if min >= isize::MIN as i128 {
963                        assert!(matches!(min.into(), Integer(IntegerKind::Primitive(_))));
964                    } else {
965                        assert!(matches!(min.into(), Integer(IntegerKind::Variable(_))));
966                    }
967                    if max <= isize::MAX as u128 {
968                        assert!(matches!(max.into(), Integer(IntegerKind::Primitive(_))));
969                    } else {
970                        assert!(matches!(max.into(), Integer(IntegerKind::Variable(_))));
971                    }
972
973                    assert_integer_round_trip!($t, 1, &[1], &[1]);
975
976                    if <$t>::MAX as u128 >= i8::MAX as u128 {
978                        assert_integer_round_trip!($t, i8::MAX as $t, &[127], &[127]);
979                    }
980                    if <$t>::MAX as u128 >= i16::MAX as u128 {
982                        assert_integer_round_trip!($t, i16::MAX as $t, &[127, 255], &[127, 255]);
983                        assert_integer_round_trip!($t, 128, &[128], &[0, 128]);
985                    }
986                    if <$t>::MAX as u128 >= i32::MAX as u128 {
987                        assert_integer_round_trip!($t, i32::MAX as $t, &[127, 255, 255, 255], &[127, 255, 255, 255]);
988                        assert_integer_round_trip!($t, (i16::MAX as $t + 1), &[128, 0], &[0, 128, 0]);
990                    }
991                    if <$t>::MAX as u128 >= i64::MAX as u128 {
992                        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]);
993                    }
994
995
996                    match stringify!($t) {
998                        "i8" => {
999                            assert_integer_round_trip!($t, -1, &[255], &[255]);
1000                        },
1001                        "i16" => {
1002                            assert_integer_round_trip!($t, -1, &[255, 255], &[255]);
1003                        },
1004                        "i32" => {
1005                            assert_integer_round_trip!($t, -1, &[255, 255, 255, 255], &[255]);
1006                        },
1007                        "i64" => {
1008                            assert_integer_round_trip!($t, -1, &[255, 255, 255, 255, 255, 255, 255, 255], &[255]);
1009                        },
1010                        _ => {},
1011                    }
1012                }
1013            )*
1014
1015            #[test]
1016            fn test_overflow_addition() {
1017                let max = Integer(IntegerKind::Primitive(isize::MAX));
1018                let one = Integer(IntegerKind::Primitive(1));
1019                let result = max + one;
1020                assert!(matches!(result, Integer(IntegerKind::Variable(_))));
1021            }
1022
1023            #[test]
1024            fn test_overflow_subtraction() {
1025                let min = Integer(IntegerKind::Primitive(isize::MIN));
1026                let one = Integer(IntegerKind::Primitive(1));
1027                let result = min - one;
1028                assert!(matches!(result, Integer(IntegerKind::Variable(_))));
1029            }
1030
1031            #[test]
1032            fn test_primitive_to_i64() {
1033                let zero = Integer(IntegerKind::Primitive(0));
1034                let positive = Integer(IntegerKind::Primitive(42));
1035                let negative = Integer(IntegerKind::Primitive(-42));
1036                let max = Integer(IntegerKind::Primitive(isize::MAX));
1037                let min = Integer(IntegerKind::Primitive(isize::MIN));
1038
1039                assert_eq!(zero.to_i64(), Some(0i64));
1040                assert_eq!(positive.to_i64(), Some(42i64));
1041                assert_eq!(negative.to_i64(), Some(-42i64));
1042                assert_eq!(max.to_i64(), Some(isize::MAX as i64));
1043                assert_eq!(min.to_i64(), Some(isize::MIN as i64));
1044            }
1045            #[test]
1046            fn test_primitive_to_u64() {
1047                let zero = Integer(IntegerKind::Primitive(0));
1048                let positive = Integer(IntegerKind::Primitive(42));
1049                let negative = Integer(IntegerKind::Primitive(-42));
1050                let max = Integer(IntegerKind::Primitive(isize::MAX));
1051
1052                assert_eq!(zero.to_u64(), Some(0u64));
1053                assert_eq!(positive.to_u64(), Some(42u64));
1054                assert_eq!(negative.to_u64(), None); assert_eq!(max.to_u64(), Some(isize::MAX as u64));
1056            }
1057            #[test]
1058            fn test_primitive_to_i128() {
1059                let zero = Integer(IntegerKind::Primitive(0));
1060                let positive = Integer(IntegerKind::Primitive(42));
1061                let negative = Integer(IntegerKind::Primitive(-42));
1062                let max = Integer(IntegerKind::Primitive(isize::MAX));
1063                let min = Integer(IntegerKind::Primitive(isize::MIN));
1064
1065                assert_eq!(zero.to_i128(), Some(0i128));
1066                assert_eq!(positive.to_i128(), Some(42i128));
1067                assert_eq!(negative.to_i128(), Some(-42i128));
1068                assert_eq!(max.to_i128(), Some(isize::MAX as i128));
1069                assert_eq!(min.to_i128(), Some(isize::MIN as i128));
1070            }
1071            #[test]
1072            fn test_variable_to_i64() {
1073                let zero = Integer(IntegerKind::Variable(Box::new(BigInt::from(0))));
1074                let positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(100))));
1075                let negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(-100))));
1076                let large_positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(i64::MAX) + 1)));
1077                let large_negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(i64::MIN) - 1)));
1078
1079                assert_eq!(zero.to_i64(), Some(0i64));
1080                assert_eq!(positive.to_i64(), Some(100i64));
1081                assert_eq!(negative.to_i64(), Some(-100i64));
1082                assert_eq!(large_positive.to_i64(), None); assert_eq!(large_negative.to_i64(), None); }
1085
1086            #[test]
1087            fn test_variable_to_u64() {
1088                let zero = Integer(IntegerKind::Variable(Box::new(BigInt::from(0))));
1089                let positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(100))));
1090                let negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(-100))));
1091                let large_positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(u64::MAX) + 1)));
1092
1093                assert_eq!(zero.to_u64(), Some(0u64));
1094                assert_eq!(positive.to_u64(), Some(100u64));
1095                assert_eq!(negative.to_u64(), None); assert_eq!(large_positive.to_u64(), None); }
1098
1099            #[test]
1100            fn test_variable_to_i128() {
1101                let zero = Integer(IntegerKind::Variable(Box::new(BigInt::from(0))));
1102                let positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(1000))));
1103                let negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(-1000))));
1104                let very_large_positive = Integer(IntegerKind::Variable(Box::new(BigInt::from(i128::MAX) + BigInt::from(1))));
1105                let very_large_negative = Integer(IntegerKind::Variable(Box::new(BigInt::from(i128::MIN) - BigInt::from(1))));
1106
1107
1108                assert_eq!(zero.to_i128(), Some(0i128));
1109                assert_eq!(positive.to_i128(), Some(1000i128));
1110                assert_eq!(negative.to_i128(), Some(-1000i128));
1111                assert_eq!(very_large_positive.to_i128(), None);
1112                assert_eq!(very_large_negative.to_i128(), None);
1113            }
1114        }
1115    };
1116}
1117
1118test_integer_conversions_and_operations!(
1119    i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
1120);