Skip to main content

fixed_bigint/fixeduint/
string_conversion.rs

1use core::fmt::Write;
2use num_traits::{Num, ToPrimitive, Zero};
3
4use super::{FixedUInt, MachineWord, make_empty_error, make_overflow_err, make_parse_int_err};
5use const_num_traits::Nct;
6
7#[cfg(feature = "num-traits")]
8impl<T: MachineWord, const N: usize> num_traits::Num for FixedUInt<T, N, Nct> {
9    type FromStrRadixErr = core::num::ParseIntError;
10    fn from_str_radix(
11        input: &str,
12        radix: u32,
13    ) -> Result<Self, <Self as num_traits::Num>::FromStrRadixErr> {
14        if input.is_empty() {
15            return Err(make_empty_error());
16        }
17
18        if !(2..=16).contains(&radix) {
19            return Err(make_overflow_err()); // Invalid radix
20        }
21
22        let mut ret = Self::zero();
23        let range = match input.find(|c: char| c != '0') {
24            Some(x) => &input[x..],
25            _ => input,
26        };
27
28        for c in range.chars() {
29            let digit = match c.to_digit(radix) {
30                Some(d) => d,
31                None => return Err(make_parse_int_err()), // Invalid character for the radix
32            };
33
34            ret = num_traits::CheckedMul::checked_mul(&ret, &Self::from(radix as u8))
35                .ok_or(make_overflow_err())?;
36            ret = num_traits::CheckedAdd::checked_add(&ret, &Self::from(digit as u8))
37                .ok_or(make_overflow_err())?;
38        }
39
40        Ok(ret)
41    }
42}
43
44impl<T: MachineWord, const N: usize> core::fmt::UpperHex for FixedUInt<T, N, Nct>
45where
46    u8: core::convert::TryFrom<T>,
47{
48    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
49        self.hex_fmt(formatter, true)
50    }
51}
52
53impl<T: MachineWord, const N: usize> core::fmt::LowerHex for FixedUInt<T, N, Nct>
54where
55    u8: core::convert::TryFrom<T>,
56{
57    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
58        self.hex_fmt(formatter, false)
59    }
60}
61
62impl<T: MachineWord, const N: usize> core::fmt::Display for FixedUInt<T, N, Nct> {
63    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64        const MAX_DIGITS: usize = 20;
65
66        if self.is_zero() {
67            return f.write_char('0');
68        }
69
70        // 20 is sized for u64
71        let mut digit_blocks = [[0u8; MAX_DIGITS]; N];
72        let mut digit_count = 0;
73        let mut number = *self;
74        let ten = Self::from(10u8);
75
76        // Extract digits into our storage
77        while !number.is_zero() && digit_count < N * MAX_DIGITS {
78            let (quotient, remainder) = number.div_rem(&ten);
79            let digit = remainder.to_u8().unwrap();
80
81            let block_idx = digit_count / MAX_DIGITS;
82            let digit_idx = digit_count % MAX_DIGITS;
83            digit_blocks[block_idx][digit_idx] = b'0' + digit;
84
85            digit_count += 1;
86            number = quotient;
87        }
88
89        // Write digits in reverse order
90        for i in (0..digit_count).rev() {
91            let block_idx = i / MAX_DIGITS;
92            let digit_idx = i % MAX_DIGITS;
93            f.write_char(digit_blocks[block_idx][digit_idx] as char)?;
94        }
95
96        Ok(())
97    }
98}
99
100#[cfg(feature = "num-traits")]
101impl<T: MachineWord, const N: usize> core::str::FromStr for FixedUInt<T, N, Nct> {
102    type Err = core::num::ParseIntError;
103
104    fn from_str(s: &str) -> Result<Self, Self::Err> {
105        Self::from_str_radix(s, 10)
106    }
107}