ttpkit_utils/
num.rs

1//! Number encoding and decoding.
2
3use atoi::FromRadix10SignedChecked;
4use itoa::Integer;
5
6use crate::error::Error;
7
8use self::private::{HexExtensions, Overflow};
9
10/// Decimal number encoder.
11pub struct DecEncoder {
12    inner: itoa::Buffer,
13}
14
15impl DecEncoder {
16    /// Create a new encoder.
17    #[inline]
18    pub fn new() -> Self {
19        Self {
20            inner: itoa::Buffer::new(),
21        }
22    }
23
24    /// Encode a given number.
25    pub fn encode<T>(&mut self, n: T) -> &[u8]
26    where
27        T: DecNumber,
28    {
29        self.inner.format(n).as_bytes()
30    }
31}
32
33impl Default for DecEncoder {
34    #[inline]
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40/// Hexadecimal number encoder.
41pub struct HexEncoder {
42    buffer: [u8; 32],
43}
44
45impl HexEncoder {
46    const LOWER_HEX_DIGITS: &[u8] = b"0123456789abcdef";
47
48    /// Create a new encoder.
49    #[inline]
50    pub fn new() -> Self {
51        Self { buffer: [b'0'; 32] }
52    }
53
54    /// Encode a given number.
55    pub fn encode<T>(&mut self, mut n: T) -> &[u8]
56    where
57        T: HexNumber,
58    {
59        let mut idx = 31;
60
61        while n != T::ZERO {
62            let digit = n.pop_hex_digit();
63
64            self.buffer[idx] = Self::LOWER_HEX_DIGITS[digit as usize];
65
66            idx = idx.wrapping_sub(1);
67        }
68
69        if idx == 31 {
70            self.buffer[31] = b'0';
71        } else {
72            idx = idx.wrapping_add(1);
73        }
74
75        &self.buffer[idx..]
76    }
77}
78
79impl Default for HexEncoder {
80    #[inline]
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86/// Decode a decimal number.
87pub fn decode_dec<T>(src: &[u8]) -> Result<T, Error>
88where
89    T: DecNumber,
90{
91    let is_valid = src
92        .iter()
93        .copied()
94        .enumerate()
95        .all(|(idx, b)| (idx == 0 && b == b'-') || b.is_ascii_digit());
96
97    if !is_valid || src.is_empty() {
98        return Err(Error::from_static_msg("invalid decimal number"));
99    }
100
101    atoi::atoi(src).ok_or_else(|| Error::from_static_msg("overflow"))
102}
103
104/// Decode a hexadecimal number.
105pub fn decode_hex<T>(src: &[u8]) -> Result<T, Error>
106where
107    T: HexNumber,
108{
109    let mut n = T::ZERO;
110
111    if src.is_empty() {
112        return Err(Error::from_static_msg("empty hex string"));
113    }
114
115    for &b in src {
116        let digit = if b.is_ascii_digit() {
117            b - b'0'
118        } else if (b'a'..=b'f').contains(&b) {
119            b - b'a' + 10
120        } else if (b'A'..=b'F').contains(&b) {
121            b - b'A' + 10
122        } else {
123            return Err(Error::from_static_msg("not a hex digit"));
124        };
125
126        n.push_hex_digit(digit)?;
127    }
128
129    Ok(n)
130}
131
132/// Decimal number.
133pub trait DecNumber: FromRadix10SignedChecked + Integer {}
134
135macro_rules! impl_dec_number {
136    ($t:ty) => {
137        impl DecNumber for $t {}
138    };
139}
140
141impl_dec_number!(i8);
142impl_dec_number!(i16);
143impl_dec_number!(i32);
144impl_dec_number!(i64);
145impl_dec_number!(i128);
146impl_dec_number!(isize);
147
148impl_dec_number!(u8);
149impl_dec_number!(u16);
150impl_dec_number!(u32);
151impl_dec_number!(u64);
152impl_dec_number!(u128);
153impl_dec_number!(usize);
154
155/// Hexadecimal number.
156pub trait HexNumber: HexExtensions {}
157
158macro_rules! impl_hex_number {
159    ($t:ty) => {
160        impl HexExtensions for $t {
161            const ZERO: Self = 0;
162
163            fn pop_hex_digit(&mut self) -> u8 {
164                let digit = (*self & 0xf) as u8;
165
166                *self >>= 4;
167
168                digit
169            }
170
171            fn push_hex_digit(&mut self, digit: u8) -> Result<(), Overflow> {
172                if (*self >> ((std::mem::size_of::<$t>() << 3) - 4)) != 0 {
173                    return Err(Overflow);
174                }
175
176                *self = (*self << 4) | ((digit & 0xf) as $t);
177
178                Ok(())
179            }
180        }
181
182        impl HexNumber for $t {}
183    };
184}
185
186impl_hex_number!(u8);
187impl_hex_number!(u16);
188impl_hex_number!(u32);
189impl_hex_number!(u64);
190impl_hex_number!(u128);
191impl_hex_number!(usize);
192
193mod private {
194    /// Hexadecimal number extensions.
195    pub trait HexExtensions: PartialEq + Sized {
196        const ZERO: Self;
197
198        /// Pop the least significant hex digit.
199        fn pop_hex_digit(&mut self) -> u8;
200
201        /// Push a least significant hex digit.
202        fn push_hex_digit(&mut self, digit: u8) -> Result<(), Overflow>;
203    }
204
205    /// Overflow error.
206    pub struct Overflow;
207}
208
209impl From<Overflow> for Error {
210    fn from(_: Overflow) -> Self {
211        Error::from_static_msg("overflow")
212    }
213}