use crate::{
arch::word::Word,
math::{max_exp_in_dword, max_exp_in_word},
};
use static_assertions::const_assert;
type FastDivideSmall = num_modular::PreMulInv1by1<Word>;
type FastDivideNormalized = num_modular::Normalized2by1Divisor<Word>;
pub type Digit = u32;
pub const MIN_RADIX: Digit = 2;
pub const MAX_RADIX: Digit = 36;
#[inline]
pub const fn is_radix_valid(radix: Digit) -> bool {
MIN_RADIX <= radix && radix <= MAX_RADIX
}
const_assert!(b'a' > b'0' + 10 && b'A' > b'0' + 10);
#[derive(Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum DigitCase {
NoLetters = 0,
Lower = b'a' - b'0' - 10,
Upper = b'A' - b'0' - 10,
}
#[inline]
pub const fn digit_from_ascii_byte(byte: u8, radix: Digit) -> Option<Digit> {
assert!(is_radix_valid(radix));
let res = match byte {
c @ b'0'..=b'9' => (c - b'0') as Digit,
c @ b'a'..=b'z' => (c - b'a') as Digit + 10,
c @ b'A'..=b'Z' => (c - b'A') as Digit + 10,
_ => return None,
};
if res < radix {
Some(res)
} else {
None
}
}
#[derive(Clone, Copy)]
pub struct RadixInfo {
pub(crate) digits_per_word: usize,
pub(crate) range_per_word: Word,
pub(crate) fast_div_radix: FastDivideSmall,
pub(crate) fast_div_range_per_word: FastDivideNormalized,
}
pub const RADIX10_INFO: RadixInfo = RadixInfo::for_radix(10);
pub const MAX_WORD_DIGITS_NON_POW_2: usize = max_exp_in_word(3).0 + 1;
pub const MAX_DWORD_DIGITS_NON_POW_2: usize = max_exp_in_dword(3).0 + 1;
#[inline]
pub fn radix_info(radix: Digit) -> RadixInfo {
debug_assert!(is_radix_valid(radix));
match radix {
10 => RADIX10_INFO,
_ => RadixInfo::for_radix(radix),
}
}
impl RadixInfo {
const fn for_radix(radix: Digit) -> RadixInfo {
let (digits_per_word, range_per_word) = max_exp_in_word(radix as Word);
let shift = range_per_word.leading_zeros();
let fast_div_radix = FastDivideSmall::new(radix as Word);
let fast_div_range_per_word = FastDivideNormalized::new(range_per_word << shift);
RadixInfo {
digits_per_word,
range_per_word,
fast_div_radix,
fast_div_range_per_word,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_digit_from_utf8_byte() {
assert_eq!(digit_from_ascii_byte(b'7', 10), Some(7));
assert_eq!(digit_from_ascii_byte(b'a', 16), Some(10));
assert_eq!(digit_from_ascii_byte(b'z', 36), Some(35));
assert_eq!(digit_from_ascii_byte(b'Z', 36), Some(35));
assert_eq!(digit_from_ascii_byte(b'?', 10), None);
assert_eq!(digit_from_ascii_byte(b'a', 10), None);
assert_eq!(digit_from_ascii_byte(b'z', 35), None);
assert_eq!(digit_from_ascii_byte(255, 35), None);
}
}