1use crate::{
4 arch::word::Word,
5 math::{max_exp_in_dword, max_exp_in_word},
6};
7use static_assertions::const_assert;
8
9type FastDivideSmall = num_modular::PreMulInv1by1<Word>;
10type FastDivideNormalized = num_modular::Normalized2by1Divisor<Word>;
11
12pub type Digit = u32;
14
15pub const MIN_RADIX: Digit = 2;
17
18pub const MAX_RADIX: Digit = 36;
20
21#[inline]
23pub const fn is_radix_valid(radix: Digit) -> bool {
24 MIN_RADIX <= radix && radix <= MAX_RADIX
25}
26
27const_assert!(b'a' > b'0' + 10 && b'A' > b'0' + 10);
28
29#[derive(Clone, Copy, Eq, PartialEq)]
31#[repr(u8)]
32pub enum DigitCase {
33 NoLetters = 0,
34 Lower = b'a' - b'0' - 10,
35 Upper = b'A' - b'0' - 10,
36}
37
38#[inline]
43pub const fn digit_from_ascii_byte(byte: u8, radix: Digit) -> Option<Digit> {
44 assert!(is_radix_valid(radix));
45
46 let res = match byte {
47 c @ b'0'..=b'9' => (c - b'0') as Digit,
48 c @ b'a'..=b'z' => (c - b'a') as Digit + 10,
49 c @ b'A'..=b'Z' => (c - b'A') as Digit + 10,
50 _ => return None,
51 };
52 if res < radix {
53 Some(res)
54 } else {
55 None
56 }
57}
58
59#[derive(Clone, Copy)]
61pub struct RadixInfo {
62 pub(crate) digits_per_word: usize,
64
65 pub(crate) range_per_word: Word,
68
69 pub(crate) fast_div_radix: FastDivideSmall,
71
72 pub(crate) fast_div_range_per_word: FastDivideNormalized,
75}
76
77pub const RADIX10_INFO: RadixInfo = RadixInfo::for_radix(10);
79
80pub const MAX_WORD_DIGITS_NON_POW_2: usize = max_exp_in_word(3).0 + 1;
82pub const MAX_DWORD_DIGITS_NON_POW_2: usize = max_exp_in_dword(3).0 + 1;
84
85#[inline]
89pub fn radix_info(radix: Digit) -> RadixInfo {
90 debug_assert!(is_radix_valid(radix));
91
92 match radix {
93 10 => RADIX10_INFO,
94 _ => RadixInfo::for_radix(radix),
95 }
96}
97
98impl RadixInfo {
99 const fn for_radix(radix: Digit) -> RadixInfo {
100 let (digits_per_word, range_per_word) = max_exp_in_word(radix as Word);
101 let shift = range_per_word.leading_zeros();
102 let fast_div_radix = FastDivideSmall::new(radix as Word);
103 let fast_div_range_per_word = FastDivideNormalized::new(range_per_word << shift);
104 RadixInfo {
105 digits_per_word,
106 range_per_word,
107 fast_div_radix,
108 fast_div_range_per_word,
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_digit_from_utf8_byte() {
119 assert_eq!(digit_from_ascii_byte(b'7', 10), Some(7));
120 assert_eq!(digit_from_ascii_byte(b'a', 16), Some(10));
121 assert_eq!(digit_from_ascii_byte(b'z', 36), Some(35));
122 assert_eq!(digit_from_ascii_byte(b'Z', 36), Some(35));
123 assert_eq!(digit_from_ascii_byte(b'?', 10), None);
124 assert_eq!(digit_from_ascii_byte(b'a', 10), None);
125 assert_eq!(digit_from_ascii_byte(b'z', 35), None);
126 assert_eq!(digit_from_ascii_byte(255, 35), None);
127 }
128}