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);