Skip to main content

fixed_bigint/fixeduint/
num_traits_casts.rs

1use super::{FixedUInt, MachineWord};
2use crate::personality::Personality;
3use num_traits::{Bounded, FromPrimitive, ToPrimitive};
4
5impl<T: MachineWord, const N: usize, P: Personality> num_traits::NumCast for FixedUInt<T, N, P> {
6    fn from<X>(arg: X) -> Option<Self>
7    where
8        X: ToPrimitive,
9    {
10        Self::from_u64(arg.to_u64()?)
11    }
12}
13
14impl<T: MachineWord, const N: usize, P: Personality> num_traits::ToPrimitive
15    for FixedUInt<T, N, P>
16{
17    fn to_i64(&self) -> Option<i64> {
18        None
19    }
20    fn to_u64(&self) -> Option<u64> {
21        let mut ret: u64 = 0;
22        // Determine how many words are needed to fill a u64 (64 bits)
23        // If WORD_SIZE is 4 (u32), we read 2 words. If 8 (u64), we read 1.
24        let iter_limit = core::cmp::min(8 / Self::WORD_SIZE, N);
25
26        // Overflow check: any remaining higher limbs must be zero
27        for i in iter_limit..N {
28            if self.array[i] != T::zero() {
29                return None;
30            }
31        }
32
33        for (i, word) in self.array.iter().take(iter_limit).enumerate() {
34            // Convert generic T to bytes (Little Endian)
35            let bytes = word.to_le_bytes();
36
37            // Iterate over bytes and shift them into the u64 result
38            for (j, &byte) in bytes.as_ref().iter().enumerate() {
39                // Calculate the global bit position for this byte
40                let bit_shift = (i * Self::WORD_SIZE + j) * 8;
41
42                // Safety check: ensure we don't shift out of u64 bounds
43                if bit_shift < 64 {
44                    ret |= (byte as u64) << bit_shift;
45                }
46            }
47        }
48
49        Some(ret)
50    }
51}
52
53impl<T: MachineWord, const N: usize, P: Personality> num_traits::FromPrimitive
54    for FixedUInt<T, N, P>
55{
56    fn from_i64(_: i64) -> Option<Self> {
57        None
58    }
59    fn from_u64(input: u64) -> Option<Self> {
60        // If max_value() fits in a u64, verify the input does not exceed it.
61        // When to_u64() returns `None`, the target type is wider than 64 bits
62        // and therefore any u64 value will fit.
63        if let Some(max) = Self::max_value().to_u64() {
64            if input > max {
65                return None;
66            }
67        }
68        Some(Self::from_le_bytes(&input.to_le_bytes()))
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    fn cast<T: num_traits::NumCast + PartialEq>(a: u32, b: u8) -> bool {
77        let my_a = T::from(a).unwrap();
78        let my_b = T::from(b).unwrap();
79        my_a == my_b
80    }
81
82    type Fixed = FixedUInt<u8, 1>;
83    // Test numcast
84    #[test]
85    fn test_numcast() {
86        assert!(cast::<Fixed>(123, 123));
87        assert!(cast::<Fixed>(225, 225));
88    }
89}