Skip to main content

fastnum/bint/int/impls/
cast.rs

1use crate::{
2    bint::{intrinsics, Int, ParseError, UInt},
3    utils::const_generics::{Dimension, Narrow, Widen},
4    Cast, TryCast,
5};
6
7impl<const N: usize, const M: usize> Cast<Int<N>> for Int<M>
8where
9    Dimension<N, M>: Widen,
10{
11    #[inline(always)]
12    fn cast(self) -> Int<N> {
13        // SAFETY: `N` is always greater or equal than `M`. So we can safely cast to the
14        // widest type.
15        #[allow(unsafe_code)]
16        unsafe {
17            self._transmute()
18        }
19    }
20}
21
22impl<const N: usize, const M: usize> TryCast<Int<N>> for Int<M>
23where
24    Dimension<N, M>: Narrow,
25{
26    type Error = ParseError;
27
28    #[inline(always)]
29    fn try_cast(self) -> Result<Int<N>, Self::Error> {
30        // For signed integers, narrowing is valid if the upper (M-N) digits are properly sign-extended.
31        // That is, they should all be 0x00...00 for positive, or all 0xFF...FF for negative.
32
33        let bits = self.to_bits();
34        let digits = bits.digits();
35
36        // Determine the expected value for upper digits based on the sign bit of digit N-1
37        // The sign bit is the MSB of digit N-1
38        let sign_digit = digits[N - 1];
39        // Branchless: use arithmetic right shift to propagate the sign bit
40        let expected_digit = ((sign_digit as i64) >> 63) as u64;
41
42        // Check if all upper digits match the expected sign extension
43        let mut i = N;
44        while i < M {
45            if digits[i] != expected_digit {
46                return Err(ParseError::PosOverflow);
47            }
48            i += 1;
49        }
50
51        // If we get here, the value fits. Manually create the narrow value by copying the lower N digits.
52        // SAFETY: We've verified that the upper (M-N) digits are properly sign-extended,
53        // so it's safe to transmute to the narrow type.
54        #[allow(unsafe_code)]
55        unsafe {
56            let narrow_digits = intrinsics::_transmute::<M, N, N>(digits);
57            Ok(Int::from_digits(narrow_digits))
58        }
59    }
60}
61
62impl<const N: usize, const M: usize> TryCast<UInt<N>> for Int<M> {
63    type Error = ParseError;
64
65    #[inline(always)]
66    fn try_cast(self) -> Result<UInt<N>, Self::Error> {
67        if self.is_negative() {
68            Err(ParseError::Signed)
69        } else if self.bits() <= Int::<N>::BITS {
70            // SAFETY: UInt<M> is wider (`N` < `M`) but its value fit to UInt<N>. So we can
71            // safely cast to the narrow type.
72            #[allow(unsafe_code)]
73            {
74                Ok(unsafe { self._transmute() }.to_bits())
75            }
76        } else {
77            Err(ParseError::PosOverflow)
78        }
79    }
80}