lexical_util/digit.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
//! Utilities to process digits.
//!
//! This both contains routines to convert to and from digits,
//! as well as iterate over digits while skipping digit separators.
// CONST FNS
// ---------
// These are optimized functions for when the radix is known at compile-time,
// which is **most** of our cases. There are cases where for codegen, using a
// runtime algorithm is preferable.
/// Unchecked, highly optimized algorithm to convert a char to a digit.
/// This only works if the input character is known to be a valid digit.
#[inline]
pub const fn char_to_valid_digit_const(c: u8, radix: u32) -> u32 {
if radix <= 10 {
// Optimize for small radixes.
(c.wrapping_sub(b'0')) as u32
} else {
// Fallback, still decently fast.
let digit = match c {
b'0'..=b'9' => c - b'0',
b'A'..=b'Z' => c - b'A' + 10,
b'a'..=b'z' => c - b'a' + 10,
_ => 0xFF,
};
digit as u32
}
}
/// Convert a character to a digit with a radix known at compile time.
///
/// This optimizes for cases where radix is <= 10, and uses a decent,
/// match-based fallback algorithm.
#[inline]
pub const fn char_to_digit_const(c: u8, radix: u32) -> Option<u32> {
let digit = char_to_valid_digit_const(c, radix);
if digit < radix {
Some(digit)
} else {
None
}
}
/// Determine if a character is a digit with a radix known at compile time.
#[inline]
pub const fn char_is_digit_const(c: u8, radix: u32) -> bool {
char_to_digit_const(c, radix).is_some()
}
/// Convert a digit to a character with a radix known at compile time.
///
/// This optimizes for cases where radix is <= 10, and uses a decent,
/// match-based fallback algorithm.
#[inline]
#[cfg(any(feature = "write", feature = "floats"))]
pub const fn digit_to_char_const(digit: u32, radix: u32) -> u8 {
if radix <= 10 || digit < 10 {
// Can short-circuit if we know the radix is small at compile time.
digit as u8 + b'0'
} else {
digit as u8 + b'A' - 10
}
}
// NON-CONST
// ---------
// These are less optimized functions for when the radix is not known at
// compile-time, which is a few (but important) cases. These generally have
// improved compiler optimization passes when generics are used more sparingly.
/// Convert a character to a digit.
#[inline]
#[cfg(feature = "parse")]
pub const fn char_to_digit(c: u8, radix: u32) -> Option<u32> {
// Fallback, still decently fast.
let digit = match c {
b'0'..=b'9' => c - b'0',
b'A'..=b'Z' => c - b'A' + 10,
b'a'..=b'z' => c - b'a' + 10,
_ => 0xFF,
} as u32;
if digit < radix {
Some(digit)
} else {
None
}
}
/// Determine if a character is a digit.
#[inline]
#[cfg(feature = "parse")]
pub const fn char_is_digit(c: u8, radix: u32) -> bool {
char_to_digit(c, radix).is_some()
}
/// Convert a digit to a character. This uses a pre-computed table to avoid branching.
///
/// # Safety
///
/// Safe as long as `digit < 36`.
#[inline]
#[cfg(feature = "write")]
pub unsafe fn digit_to_char(digit: u32) -> u8 {
const TABLE: [u8; 36] = [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E',
b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T',
b'U', b'V', b'W', b'X', b'Y', b'Z',
];
debug_assert!(digit < 36, "digit_to_char() invalid character.");
unsafe { *TABLE.get_unchecked(digit as usize) }
}