Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1,351 @@
//! Radix-generic, optimized, integer-to-string conversion routines.
//!
//! These routines are highly optimized: they unroll 4 loops at a time,
//! using pre-computed base^2 tables. This was popularized by Andrei
//! Alexandrescu, and uses 2 digits per division, which we further optimize in
//! up to 4 digits per division with a bit shift.
//!
//! See [Algorithm](/docs/Algorithm.md) for a more detailed description of
//! the algorithm choice here. See [Benchmarks](/docs/Benchmarks.md) for
//! recent benchmark data.
#![cfg(not(feature = "compact"))]
#![cfg(feature = "power-of-two")]
#![doc(hidden)]
use lexical_util::assert::debug_assert_radix;
use lexical_util::digit::digit_to_char;
use lexical_util::div128::u128_divrem;
use lexical_util::format::{radix_from_flags, NumberFormat};
use lexical_util::num::{AsCast, UnsignedInteger};
use lexical_util::step::u64_step;
use crate::digit_count::DigitCount;
/// Index a buffer and get a mutable reference, without bounds checking.
/// The `($x:ident[$i:expr] = $y:ident[$j:expr])` is not used with [`compact`].
/// The newer version of the lint is `unused_macro_rules`, but this isn't
/// supported until nightly-2022-05-12.
///
/// By default, writers tend to be safe, due to Miri, Valgrind,
/// and other tests and careful validation against a wide range
/// of randomized input. Parsers are much trickier to validate.
///
/// [`compact`]: crate#compact
#[allow(unknown_lints, unused_macro_rules)]
macro_rules! i {
($x:ident[$i:expr]) => {
*$x.get_unchecked_mut($i)
};
($x:ident[$i:expr] = $y:ident[$j:expr]) => {
*$x.get_unchecked_mut($i) = *$y.get_unchecked($j)
};
}
/// Write 2 digits to buffer.
///
/// # Safety
///
/// Safe if `bytes` is large enough to hold 2 characters, `index >= 2`,
/// and if the 2 * remainder, or `r`, has it so `r + 1 < table.len()`.
macro_rules! write_digits {
($bytes:ident, $index:ident, $table:ident, $r:ident) => {{
debug_assert!($index >= 2);
debug_assert!($bytes.len() >= 2);
debug_assert!($r + 1 < $table.len());
$index -= 1;
unsafe { i!($bytes[$index] = $table[$r + 1]) };
$index -= 1;
unsafe { i!($bytes[$index] = $table[$r]) };
}};
}
/// Write 1 digit to buffer.
///
/// # Safety
///
/// Safe if `bytes` is large enough to hold 1 characters, and `r < 36`.
/// Adding in direct safety checks here destroys performance, often by
/// 30%+ so it's up to the caller to beware.
macro_rules! write_digit {
($bytes:ident, $index:ident, $r:ident) => {{
debug_assert!($index >= 1);
debug_assert!($bytes.len() >= 1);
debug_assert!($r < 36);
$index -= 1;
unsafe { i!($bytes[$index]) = digit_to_char($r) };
}};
}
// NOTE: Don't use too many generics:
// We don't need generics for most of the internal algorithms,
// and doing so kills performance. Why? I don't know, but assuming
// it messed with the compiler's code generation.
/// Write integral digits to buffer.
///
/// This algorithm first writes 4, then 2 digits at a time, finally
/// the last 1 or 2 digits, using power reduction to speed up the
/// algorithm a lot.
///
/// # Safety
///
/// This is safe as long as the buffer is large enough to hold `T::MAX`
/// digits in radix `N` and the index >= digit count. Note that making
/// small changes here can destroy performance, so it's crucial we do this
/// correctly.
///
/// If `buffer.len() >= T::DIGITS` and `index >= T::DIGITS`, then this is
/// safe. We first carve off 4 digits off the end, similar to the algorithm
/// in compact, then 2 at a time, then 1, index will never wrap under 0.
/// Since we validate the table size and radix inside, this is the only
/// safety precondition that must be held up.
///
/// See [algorithm] and the [crate] documentation for more detailed
/// information on the safety considerations.
#[inline(always)]
unsafe fn write_digits<T: UnsignedInteger>(
mut value: T,
radix: u32,
table: &[u8],
buffer: &mut [u8],
mut index: usize,
count: usize,
) -> usize {
debug_assert_radix(radix);
debug_assert!(buffer.len() >= count, "buffer must at least be as the digit count");
// Calculate if we can do multi-digit optimizations
assert!((2..=36).contains(&radix), "radix must be >= 2 and <= 36");
let radix2 = radix * radix;
let radix4 = radix2 * radix2;
// Pre-compute our powers of radix.
let radix = T::from_u32(radix);
// SAFETY: All of these are safe for the buffer writes as long as
// the buffer is large enough to hold `T::MAX` digits in radix `N`.
// We confirm (which will be compiled out) that the table cannot
// overflow since it's the indexing is `0..radix^2 * 2`.
assert!(table.len() >= radix2 as usize * 2, "table must be 2 * radix^2 long");
// Decode 4 digits at a time.
if T::BITS >= 32 || radix4 < T::MAX.as_u32() {
let radix2 = T::from_u32(radix2);
let radix4 = T::from_u32(radix4);
while value >= radix4 {
let r = value % radix4;
value /= radix4;
let r1 = usize::as_cast(T::TWO * (r / radix2));
let r2 = usize::as_cast(T::TWO * (r % radix2));
// SAFETY: This is always safe, since the table is `2*radix^2`, and
// `r1` and `r2` must be in the range `[0, 2*radix^2-1)`, since the maximum
// value of r is `radix4-1`, which must have a `div` and `r`
// in the range `[0, radix^2-1)`.
write_digits!(buffer, index, table, r2);
write_digits!(buffer, index, table, r1);
}
}
// Decode 2 digits at a time.
if T::BITS >= 16 || radix2 < T::MAX.as_u32() {
let radix2 = T::from_u32(radix2);
while value >= radix2 {
let r = usize::as_cast(T::TWO * (value % radix2));
value /= radix2;
// SAFETY: this is always safe, since the table is `2*radix^2`, and
// `r` must be in the range `[0, 2*radix^2-1)`.
write_digits!(buffer, index, table, r);
}
}
// Decode last 2 digits.
if value < radix {
let r = u32::as_cast(value);
// SAFETY: this is always safe, since `value < radix`, so it must be < 36.
write_digit!(buffer, index, r);
} else {
// NOTE: If this is a `u8`, we need to first widen the type.
let r = usize::as_cast(T::TWO) * usize::as_cast(value);
// SAFETY: this is always safe, since the table is `2*radix^2`, and
// the value must `<= radix^2`, so rem must be in the range
// `[0, 2*radix^2-1)`.
write_digits!(buffer, index, table, r);
}
index
}
/// Specialized digits writer for u128, since it writes at least step digits.
///
/// # Safety
///
/// This is safe as long as the buffer is large enough to hold `T::MAX`
/// digits in radix `N`. See [algorithm] for more safety considerations.
#[inline(always)]
unsafe fn write_step_digits<T: UnsignedInteger>(
value: T,
radix: u32,
table: &[u8],
buffer: &mut [u8],
index: usize,
step: usize,
count: usize,
) -> usize {
debug_assert_radix(radix);
let start = index;
// SAFETY: safe as long as the call to `write_step_digits` is safe.
let index = unsafe { write_digits(value, radix, table, buffer, index, count) };
// Write the remaining 0 bytes.
let end = start.saturating_sub(step);
// SAFETY: this is always safe since `end < index && index < start`.
let zeros = unsafe { &mut i!(buffer[end..index]) };
zeros.fill(b'0');
end
}
/// Optimized implementation for radix-N numbers.
///
/// This uses an Alexandrescu algorithm, which prints 2 digits at a time
/// which is much faster than a naive approach. However, the jeaiii algorithm
/// can be faster still for decimal numbers:
/// <https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/>
///
/// # Safety
///
/// Safe as long as [`digit_count`] returns the number of written digits.
/// For performance reasons, this is always calculated as the exact number
/// of digits. If the value is too small, then the buffer will underflow,
/// causing out-of-bounds read/writes. Care must be used that [`digit_count`]
/// is correctly implemented.
///
/// Since [`digit_count`] is implemented as an unsafe trait, these guarantees
/// must be held.
///
/// See the crate [`crate`] documentation for more security considerations.
///
/// [`digit_count`]: `crate::digit_count::DigitCount`
#[inline(always)]
#[allow(clippy::unnecessary_safety_comment)]
pub fn algorithm<T>(value: T, radix: u32, table: &[u8], buffer: &mut [u8]) -> usize
where
T: UnsignedInteger + DigitCount,
{
// NOTE: These checks should be resolved at compile time, so
// they're unlikely to add any performance overhead.
assert!((2..=36).contains(&radix), "radix must be >= 2 and <= 36");
assert!(table.len() >= (radix * radix * 2) as usize, "table must be 2 * radix^2 long");
// get our digit count and only write up until that range
// the digit count should be the exact number of digits written by
// the number. this is for performance reasons: using `memcpy` destroys
// performance.
let count = value.digit_count(radix);
assert!(
count <= buffer.len(),
"The buffer must be large enough to contain the significant digits."
);
let buffer = &mut buffer[..count];
// SAFETY: Both forms of unchecked indexing cannot overflow.
// The table always has `2*radix^2` elements, so it must be a legal index.
// The buffer is ensured to have at least `FORMATTED_SIZE` or
// `FORMATTED_SIZE_DECIMAL` characters, which is the maximum number of
// digits an integer of that size may write.
_ = unsafe { write_digits(value, radix, table, buffer, buffer.len(), count) };
count
}
/// Optimized implementation for radix-N 128-bit numbers.
///
/// # Safety
///
/// Safe as long as [`digit_count`] returns the number of written digits.
/// For performance reasons, this is always calculated as the exact number
/// of digits. If the value is too small, then the buffer will underflow,
/// causing out-of-bounds read/writes. Care must be used that [`digit_count`]
/// is correctly implemented.
///
/// Since [`digit_count`] is implemented as an unsafe trait, these guarantees
/// must be held.
///
/// See the crate [`crate`] documentation for more security considerations.
///
/// [`digit_count`]: `crate::digit_count::DigitCount`
#[inline(always)]
pub fn algorithm_u128<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
value: u128,
table: &[u8],
buffer: &mut [u8],
) -> usize {
// NOTE: Use the const version of radix for `u64_step` and
// `u128_divrem` to ensure they're evaluated at compile time.
assert!(NumberFormat::<{ FORMAT }> {}.is_valid());
// NOTE: These checks should be resolved at compile time, so
// they're unlikely to add any performance overhead.
let radix = radix_from_flags(FORMAT, MASK, SHIFT);
assert!((2..=36).contains(&radix), "radix must be >= 2 and <= 36");
assert!(table.len() >= (radix * radix * 2) as usize, "table must be 2 * radix^2 long");
// Quick approximations to make the algorithm **a lot** faster.
// If the value can be represented in a 64-bit integer, we can
// do this as a native integer.
if value <= u64::MAX as u128 {
return algorithm(value as u64, radix, table, buffer);
}
// get our digit count and only write up until that range
// the digit count should be the exact number of digits written by
// the number. this is for performance reasons: using `memcpy` destroys
// performance.
let count = value.digit_count(radix);
assert!(
count <= buffer.len(),
"The buffer must be large enough to contain the significant digits."
);
let buffer = &mut buffer[..count];
// LOGIC: Both forms of unchecked indexing cannot overflow.
// The table always has `2*radix^2` elements, so it must be a legal index.
// The buffer is ensured to have at least `FORMATTED_SIZE` or
// `FORMATTED_SIZE_DECIMAL` characters, which is the maximum number of
// digits an integer of that size may write.
// We use a fast 128-bit division algorithm, described in depth
// in lexical_util/div128.
// Decode 4-digits at a time.
// To deal with internal 0 values or values with internal 0 digits set,
// we store the starting index, and if not all digits are written,
// we just skip down `digits` digits for the next value.
let step = u64_step(radix);
let (value, low) = u128_divrem(value, radix);
let mut index = count;
index = unsafe { write_step_digits(low, radix, table, buffer, index, step, count) };
if value <= u64::MAX as u128 {
unsafe { write_digits(value as u64, radix, table, buffer, index, count) };
return count;
}
// Value has to be greater than 1.8e38
let (value, mid) = u128_divrem(value, radix);
index = unsafe { write_step_digits(mid, radix, table, buffer, index, step, count) };
if index != 0 {
debug_assert!(value != 0, "Writing high digits, must have a non-zero value.");
index = unsafe { write_digits(value as u64, radix, table, buffer, index, count) };
} else {
debug_assert!(value == 0, "No more digits left to write, remainder must be 0.");
}
debug_assert!(
index == 0,
"The index after writing all digits should be at the start of the buffer."
);
count
}
+154
View File
@@ -0,0 +1,154 @@
//! Implements the algorithm in terms of the lexical API.
#![doc(hidden)]
use lexical_util::format::{NumberFormat, STANDARD};
use lexical_util::num::SignedInteger;
use lexical_util::{to_lexical, to_lexical_with_options};
use crate::options::Options;
use crate::write::WriteInteger;
// UNSIGNED
/// Callback for unsigned integer formatter.
///
/// # Safety
///
/// Safe as long as the buffer can hold `FORMATTED_SIZE` elements
/// (or `FORMATTED_SIZE_DECIMAL` for decimal).
#[cfg_attr(not(feature = "compact"), inline(always))]
fn unsigned<T, const FORMAT: u128>(value: T, buffer: &mut [u8]) -> usize
where
T: WriteInteger,
{
let format = NumberFormat::<FORMAT> {};
if cfg!(feature = "format") && format.required_mantissa_sign() {
buffer[0] = b'+';
let buffer = &mut buffer[1..];
value.write_mantissa::<FORMAT>(buffer) + 1
} else {
value.write_mantissa::<FORMAT>(buffer)
}
}
// SIGNED
/// Callback for signed integer formatter.
///
/// # Safety
///
/// Safe as long as the buffer can hold `FORMATTED_SIZE` elements
/// (or `FORMATTED_SIZE_DECIMAL` for decimal).
#[cfg_attr(not(feature = "compact"), inline(always))]
fn signed<Signed, Unsigned, const FORMAT: u128>(value: Signed, buffer: &mut [u8]) -> usize
where
Signed: SignedInteger,
Unsigned: WriteInteger,
{
let format = NumberFormat::<FORMAT> {};
if value < Signed::ZERO {
// Need to cast the value to the same size as unsigned type, since if
// the value is **exactly** `Narrow::MIN`, and it it is then cast
// as the wrapping negative as the unsigned value, a wider type
// will have a very different value.
let unsigned = Unsigned::as_cast(value.wrapping_neg());
buffer[0] = b'-';
let buffer = &mut buffer[1..];
unsigned.write_mantissa_signed::<FORMAT>(buffer) + 1
} else if cfg!(feature = "format") && format.required_mantissa_sign() {
let unsigned = Unsigned::as_cast(value);
buffer[0] = b'+';
let buffer = &mut buffer[1..];
unsigned.write_mantissa_signed::<FORMAT>(buffer) + 1
} else {
let unsigned = Unsigned::as_cast(value);
unsigned.write_mantissa_signed::<FORMAT>(buffer)
}
}
// API
// Implement `ToLexical` for numeric type.
macro_rules! unsigned_to_lexical {
($($t:tt)*) => ($(
impl ToLexical for $t {
#[cfg_attr(not(feature = "compact"), inline)]
fn to_lexical(self, bytes: &mut [u8])
-> &mut [u8]
{
let len = unsigned::<$t, { STANDARD }>(self, bytes);
&mut bytes[..len]
}
}
impl ToLexicalWithOptions for $t {
type Options = Options;
#[cfg_attr(not(feature = "compact"), inline)]
fn to_lexical_with_options<'a, const FORMAT: u128>(
self,
bytes: &'a mut [u8],
options: &Self::Options,
) -> &'a mut [u8]
{
_ = options;
assert!(NumberFormat::<{ FORMAT }> {}.is_valid());
let len = unsigned::<$t, FORMAT>(self, bytes);
&mut bytes[..len]
}
}
)*)
}
to_lexical!("lexical_write_integer", 1234, u64);
to_lexical_with_options!("lexical_write_integer", 1234, u64, Options);
unsigned_to_lexical! { u8 u16 u32 u64 u128 usize }
// Implement `ToLexical` for numeric type.
macro_rules! signed_to_lexical {
($($signed:tt $unsigned:tt ; )*) => ($(
impl ToLexical for $signed {
#[cfg_attr(not(feature = "compact"), inline)]
fn to_lexical(self, bytes: &mut [u8])
-> &mut [u8]
{
let len = signed::<$signed, $unsigned, { STANDARD }>(self, bytes);
&mut bytes[..len]
}
}
impl ToLexicalWithOptions for $signed {
type Options = Options;
#[cfg_attr(not(feature = "compact"), inline)]
fn to_lexical_with_options<'a, const FORMAT: u128>(
self,
bytes: &'a mut [u8],
options: &Self::Options,
) -> &'a mut [u8]
{
_ = options;
assert!(NumberFormat::<{ FORMAT }> {}.is_valid());
let len = signed::<$signed, $unsigned, FORMAT>(self, bytes);
&mut bytes[..len]
}
}
)*)
}
signed_to_lexical! {
i8 u8 ;
i16 u16 ;
i32 u32 ;
i64 u64 ;
i128 u128 ;
}
#[cfg(target_pointer_width = "16")]
signed_to_lexical! { isize u16 ; }
#[cfg(target_pointer_width = "32")]
signed_to_lexical! { isize u32 ; }
#[cfg(target_pointer_width = "64")]
signed_to_lexical! { isize u64 ; }
@@ -0,0 +1,73 @@
//! Radix-generic, lexical integer-to-string conversion routines.
//!
//! These routines are optimized for code size: they are significantly
//! slower at the cost of smaller binary size.
#![cfg(feature = "compact")]
#![doc(hidden)]
use lexical_util::algorithm::copy_to_dst;
use lexical_util::constants::FormattedSize;
use lexical_util::digit::digit_to_char;
use lexical_util::num::{AsCast, UnsignedInteger};
// NOTE: Testing our algorithms can be done effectively as:
// table = [
// '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
// 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
// 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
// ]
// def to_string(value, radix):
// result = ''
// while value >= radix:
// r = value % radix
// value //= radix
// result = table[int(r)] + result
//
// r = value % radix
// result = table[int(r)] + result
//
// return result
/// Write integer to string.
pub trait Compact: UnsignedInteger + FormattedSize {
/// Write our integer to string without optimizations.
///
/// This iterates over the buffer in reverse, and ensures that at least
/// 128 elements exist in the temporary buffer. This ensures that the digits
/// can never overflow, even in base 2. The buffer then must be able to hold
/// at least 128 digits as well, after subslicing the data. This guarantee
/// is likely already made.
fn compact(self, radix: u32, buffer: &mut [u8]) -> usize {
// NOTE: We do not have to validate the buffer length because `copy_to_dst` is
// safe.
assert!(Self::BITS <= 128);
let mut digits: [u8; 128] = [0u8; 128];
let mut index = digits.len();
// Decode all but the last digit.
let radix = Self::from_u32(radix);
let mut value = self;
while value >= radix {
let r = value % radix;
value /= radix;
index -= 1;
digits[index] = digit_to_char(u32::as_cast(r));
}
// Decode last digit.
let r = value;
index -= 1;
digits[index] = digit_to_char(u32::as_cast(r));
let slc = &digits[index..];
copy_to_dst(buffer, slc)
}
}
macro_rules! compact_impl {
($($t:ty)*) => ($(
impl Compact for $t {}
)*)
}
compact_impl! { u8 u16 u32 u64 u128 usize }
@@ -0,0 +1,312 @@
//! Radix-generic, lexical integer-to-string conversion routines.
//!
//! An optimization for decimal is pre-computing the number of digits written
//! prior to actually writing digits, avoiding the use of temporary buffers.
//! This scales well with integer size, short of `u128`, due to the slower
//! division algorithms required.
//!
//! See [`Algorithm.md`] for a more detailed description of the algorithm
//! choice here.
//!
//! [`Algorithm.md`]: https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
use lexical_util::num::UnsignedInteger;
use crate::digit_count::fast_log2;
use crate::jeaiii;
/// Calculate the fast, integral log10 of a value.
///
/// This is relatively easy to explain as well: we calculate the log2
/// of the value, then multiply by an integral constant for the log10(2).
///
/// Note that this value is frequently off by 1, so we need to round-up
/// accordingly. This magic number is valid at least up until `1<<18`,
/// which works for all values, since our max log2 is 127.
#[inline(always)]
pub fn fast_log10<T: UnsignedInteger>(x: T) -> usize {
let log2 = fast_log2(x);
(log2 * 1233) >> 12
}
/// Fast algorithm to calculate the number of digits in an integer.
///
/// We only use this for 32-bit or smaller values: for larger numbers,
/// we first write digits until we get to 32-bits, then we call this.
///
/// The values are as follows:
///
/// - `2^32 for j = 1`
/// - `⌈log10(2^j)⌉ * 2^128 + 2^128 – 10^(⌈log10(2j)⌉) for j from 2 to 30`
/// - `⌈log10(2^j)⌉ for j = 31 and j = 32`
///
/// This algorithm is described in detail in "Computing the number of digits
/// of an integer even faster", available
/// [here](https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/).
#[inline(always)]
pub fn fast_digit_count(x: u32) -> usize {
const TABLE: [u64; 32] = [
4294967296,
8589934582,
8589934582,
8589934582,
12884901788,
12884901788,
12884901788,
17179868184,
17179868184,
17179868184,
21474826480,
21474826480,
21474826480,
21474826480,
25769703776,
25769703776,
25769703776,
30063771072,
30063771072,
30063771072,
34349738368,
34349738368,
34349738368,
34349738368,
38554705664,
38554705664,
38554705664,
41949672960,
41949672960,
41949672960,
42949672960,
42949672960,
];
// This always safe, since `fast_log2` will always return a value
// <= 32. This is because the range of values from `ctlz(x | 1)` is
// `[0, 31]`, so `32 - 1 - ctlz(x | 1)` must be in the range `[0, 31]`.
let shift = TABLE[fast_log2(x)];
let count = (x as u64 + shift) >> 32;
count as usize
}
/// Slightly slower algorithm to calculate the number of digits in an integer.
///
/// This uses no static storage, and uses a fast log10(2) estimation
/// to calculate the number of digits, from the log2 value.
///
/// This algorithm is described in detail in "Computing the number of digits
/// of an integer even faster", available
/// [here](https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/).
#[inline(always)]
pub fn fallback_digit_count<T: UnsignedInteger>(x: T, table: &[T]) -> usize {
// This value is always within 1: calculate if we need to round-up
// based on a pre-computed table.
let log10 = fast_log10(x);
let shift_up = table.get(log10).map_or(false, |&y| x >= y);
log10 + shift_up as usize + 1
}
/// Quickly calculate the number of decimal digits in a type.
///
/// # Safety
///
/// Safe as long as `digit_count` returns at least the number of
/// digits that would be written by the integer. If the value is
/// too small, then the buffer might underflow, causing out-of-bounds
/// read/writes.
pub unsafe trait DecimalCount: UnsignedInteger {
/// Get the number of digits in a value.
fn decimal_count(self) -> usize;
}
// SAFETY: Safe since `fast_digit_count` is always correct for `<= u32::MAX`.
unsafe impl DecimalCount for u8 {
#[inline(always)]
fn decimal_count(self) -> usize {
fast_digit_count(self as u32)
}
}
// SAFETY: Safe since `fast_digit_count` is always correct for `<= u32::MAX`.
unsafe impl DecimalCount for u16 {
#[inline(always)]
fn decimal_count(self) -> usize {
fast_digit_count(self as u32)
}
}
// SAFETY: Safe since `fast_digit_count` is always correct for `<= u32::MAX`.
unsafe impl DecimalCount for u32 {
#[inline(always)]
fn decimal_count(self) -> usize {
fast_digit_count(self)
}
}
// SAFETY: Safe since `fallback_digit_count` is valid for the current table,
// as described in <https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/>
unsafe impl DecimalCount for u64 {
#[inline(always)]
fn decimal_count(self) -> usize {
const TABLE: [u64; 19] = [
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000,
];
fallback_digit_count(self, &TABLE)
}
}
// SAFETY: Safe since `fallback_digit_count` is valid for the current table,
// as described in <https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/>
unsafe impl DecimalCount for u128 {
#[inline(always)]
fn decimal_count(self) -> usize {
const TABLE: [u128; 38] = [
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000,
100000000000000000000,
1000000000000000000000,
10000000000000000000000,
100000000000000000000000,
1000000000000000000000000,
10000000000000000000000000,
100000000000000000000000000,
1000000000000000000000000000,
10000000000000000000000000000,
100000000000000000000000000000,
1000000000000000000000000000000,
10000000000000000000000000000000,
100000000000000000000000000000000,
1000000000000000000000000000000000,
10000000000000000000000000000000000,
100000000000000000000000000000000000,
1000000000000000000000000000000000000,
10000000000000000000000000000000000000,
100000000000000000000000000000000000000,
];
fallback_digit_count(self, &TABLE)
}
}
// SAFETY: Safe since it uses the default implementation for the type size.
unsafe impl DecimalCount for usize {
#[inline(always)]
fn decimal_count(self) -> usize {
match Self::BITS {
8 | 16 | 32 => (self as u32).decimal_count(),
64 => (self as u64).decimal_count(),
128 => (self as u128).decimal_count(),
_ => unimplemented!(),
}
}
}
/// Write integer to decimal string.
pub trait Decimal: DecimalCount {
fn decimal(self, buffer: &mut [u8]) -> usize;
/// Specialized overload is the type is sized.
///
/// # Panics
///
/// If the data original provided was unsigned and therefore
/// has more digits than the signed variant. This only affects
/// `i64` (see #191).
#[inline(always)]
fn decimal_signed(self, buffer: &mut [u8]) -> usize {
self.decimal(buffer)
}
}
// Implement decimal for type.
macro_rules! decimal_impl {
($($t:ty; $f:ident)*) => ($(
impl Decimal for $t {
#[inline(always)]
fn decimal(self, buffer: &mut [u8]) -> usize {
jeaiii::$f(self, buffer)
}
}
)*);
}
decimal_impl! {
u8; from_u8
u16; from_u16
u32; from_u32
u128; from_u128
}
impl Decimal for u64 {
#[inline(always)]
fn decimal(self, buffer: &mut [u8]) -> usize {
jeaiii::from_u64(self, buffer)
}
#[inline(always)]
fn decimal_signed(self, buffer: &mut [u8]) -> usize {
jeaiii::from_i64(self, buffer)
}
}
impl Decimal for usize {
#[inline(always)]
fn decimal(self, buffer: &mut [u8]) -> usize {
match usize::BITS {
8 => (self as u8).decimal(buffer),
16 => (self as u16).decimal(buffer),
32 => (self as u32).decimal(buffer),
64 => (self as u64).decimal(buffer),
128 => (self as u128).decimal(buffer),
_ => unimplemented!(),
}
}
#[inline(always)]
fn decimal_signed(self, buffer: &mut [u8]) -> usize {
match usize::BITS {
8 => (self as u8).decimal_signed(buffer),
16 => (self as u16).decimal_signed(buffer),
32 => (self as u32).decimal_signed(buffer),
64 => (self as u64).decimal_signed(buffer),
128 => (self as u128).decimal_signed(buffer),
_ => unimplemented!(),
}
}
}
@@ -0,0 +1,262 @@
//! Get the number of digits for an integer formatted for a radix.
//!
//! This will always accurately calculate the number of digits for
//! a given radix, using optimizations for cases with a power-of-two
//! and decimal numbers.
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
use lexical_util::{
assert::debug_assert_radix,
div128::u128_divrem,
num::{AsPrimitive, UnsignedInteger},
step::u64_step,
};
use crate::decimal::DecimalCount;
/// Fast integral log2.
///
/// This is fairly trivial to explain, since the log2 is related to the
/// number of bits in the value. Therefore, it has to be related to
/// `T::BITS - ctlz(x)`. For example, `log2(2) == 1`, and `log2(1) == 0`,
/// and `log2(3) == 1`. Therefore, we must take the log of an odd number,
/// and subtract one.
///
/// This algorithm is described in detail in "Computing the number of digits
/// of an integer quickly", available
/// [here](https://lemire.me/blog/2021/05/28/computing-the-number-of-digits-of-an-integer-quickly/).
#[inline(always)]
pub fn fast_log2<T: UnsignedInteger>(x: T) -> usize {
T::BITS - 1 - (x | T::ONE).leading_zeros() as usize
}
// Algorithms to calculate the number of digits from a single value.
macro_rules! digit_count {
// Highly-optimized digit count for 2^N values.
(@2 $x:expr) => {{
digit_log2($x)
}};
(@4 $x:expr) => {{
digit_log4($x)
}};
(@8 $x:expr) => {{
digit_log8($x)
}};
(@16 $x:expr) => {{
digit_log16($x)
}};
(@32 $x:expr) => {{
digit_log32($x)
}};
// Uses a naive approach to calculate the number of digits.
// This uses multi-digit optimizations when possible, and always
// accurately calculates the number of digits similar to how
// the digit generation algorithm works, just without the table
// lookups.
//
// There's no good way to do this, since float logn functions are
// lossy and the value might not be exactly represented in the type
// (that is, `>= 2^53`), in which case `log(b^x, b)`, `log(b^x + 1, b)`,
// and `log(b^x - 1, b)` would all be the same. Rust's integral [`ilog`]
// functions just use naive 1-digit at a time multiplication, so it's
// less efficient than our optimized variant.
//
// [`ilog`]: https://github.com/rust-lang/rust/blob/0e98766/library/core/src/num/uint_macros.rs#L1290-L1320
(@naive $t:ty, $radix:expr, $x:expr) => {{
// If we can do multi-digit optimizations, it's ideal,
// so we want to check if our type size max value is >=
// to the value.
let radix = $radix as u32;
let radix2 = radix * radix;
let radix4 = radix2 * radix2;
// NOTE: For radix 10, 0-9 would be 1 digit while 10-99 would be 2 digits,
// so this needs to be `>=` and not `>`.
let mut digits = 1;
let mut value = $x;
// try 4-digit optimizations
if <$t>::BITS >= 32 || radix4 < <$t>::MAX.as_u32() {
let radix4 = <$t as AsPrimitive>::from_u32(radix4);
while value >= radix4 {
digits += 4;
value /= radix4;
}
}
// try 2-digit optimizations
if <$t>::BITS >= 16 || radix2 < <$t>::MAX.as_u32() {
let radix2 = <$t as AsPrimitive>::from_u32(radix2);
while value >= radix2 {
digits += 2;
value /= radix2;
}
}
// can only do a single digit
let radix = <$t as AsPrimitive>::from_u32(radix);
while value >= radix {
digits += 1;
value /= radix;
}
digits
}};
}
/// Highly-optimized digit count for base2 values.
///
/// This is always the number of `BITS - ctlz(x | 1)`, so it's
/// `fast_log2(x) + 1`. This is because 0 has 1 digit, as does 1,
/// but 2 and 3 have 2, etc.
#[inline(always)]
fn digit_log2<T: UnsignedInteger>(x: T) -> usize {
fast_log2(x) + 1
}
/// Highly-optimized digit count for base4 values.
///
/// This is very similar to base 2, except we divide by 2
/// and adjust by 1. For example, `fast_log2(3) == 1`, so
/// `fast_log2(3) / 2 == 0`, which then gives us our result.
///
/// This works because `log2(x) / 2 == log4(x)`. Flooring is
/// the correct approach since `log2(15) == 3`, which should be
/// 2 digits (so `3 / 2 + 1`).
#[inline(always)]
fn digit_log4<T: UnsignedInteger>(x: T) -> usize {
(fast_log2(x) / 2) + 1
}
/// Highly-optimized digit count for base8 values.
///
/// This works because `log2(x) / 3 == log8(x)`. Flooring is
/// the correct approach since `log2(63) == 5`, which should be
/// 2 digits (so `5 / 3 + 1`).
#[inline(always)]
fn digit_log8<T: UnsignedInteger>(x: T) -> usize {
(fast_log2(x) / 3) + 1
}
/// Highly-optimized digit count for base16 values.
///
/// This works because `log2(x) / 4 == log16(x)`. Flooring is
/// the correct approach since `log2(255) == 7`, which should be
/// 2 digits (so `7 / 4 + 1`).
#[inline(always)]
fn digit_log16<T: UnsignedInteger>(x: T) -> usize {
(fast_log2(x) / 4) + 1
}
/// Highly-optimized digit count for base32 values.
///
/// This works because `log2(x) / 5 == log32(x)`. Flooring is
/// the correct approach since `log2(1023) == 9`, which should be
/// 2 digits (so `9 / 5 + 1`).
#[inline(always)]
fn digit_log32<T: UnsignedInteger>(x: T) -> usize {
(fast_log2(x) / 5) + 1
}
/// Quickly calculate the number of digits in a type.
///
/// This uses optimizations for powers-of-two and decimal
/// numbers, which can correctly calculate the number of
/// values without requiring logs or other expensive
/// calculations.
///
/// # Safety
///
/// Safe as long as `digit_count` returns at least the number of
/// digits that would be written by the integer. If the value is
/// too small, then the buffer might underflow, causing out-of-bounds
/// read/writes.
pub unsafe trait DigitCount: UnsignedInteger + DecimalCount {
/// Get the number of digits in a value.
#[inline(always)]
fn digit_count(self, radix: u32) -> usize {
assert!((2..=36).contains(&radix), "radix must be >= 2 and <= 36");
match radix {
// decimal
10 => self.decimal_count(),
// 2^N
2 => digit_count!(@2 self),
4 => digit_count!(@4 self),
8 => digit_count!(@8 self),
16 => digit_count!(@16 self),
32 => digit_count!(@32 self),
// fallback
_ => digit_count!(@naive Self, radix, self),
}
}
/// Get the number of digits in a value, always using the slow algorithm.
///
/// This is exposed for testing purposes.
#[inline(always)]
fn slow_digit_count(self, radix: u32) -> usize {
digit_count!(@naive Self, radix, self)
}
}
// Implement digit counts for all types.
macro_rules! digit_impl {
($($t:ty)*) => ($(
// SAFETY: Safe since it uses the default implementation.
unsafe impl DigitCount for $t {
}
)*);
}
digit_impl! { u8 u16 usize u32 u64 }
// SAFETY: Safe since it specialized in terms of `div128_rem`, which
// is the same way the digits are generated.
unsafe impl DigitCount for u128 {
/// Get the number of digits in a value.
#[inline(always)]
fn digit_count(self, radix: u32) -> usize {
debug_assert_radix(radix);
match radix {
// decimal
10 => self.decimal_count(),
// 2^N
2 => digit_count!(@2 self),
4 => digit_count!(@4 self),
8 => digit_count!(@8 self),
16 => digit_count!(@16 self),
32 => digit_count!(@32 self),
// fallback
_ => {
// NOTE: This follows the same implementation as the digit count
// generation, so this is safe.
if self <= u64::MAX as u128 {
return digit_count!(@naive u64, radix, self as u64);
}
// Doesn't fit in 64 bits, let's try our divmod.
let step = u64_step(radix);
let (value, _) = u128_divrem(self, radix);
let mut count = step;
if value <= u64::MAX as u128 {
count += digit_count!(@naive u64, radix, value as u64);
} else {
// Value has to be greater than 1.8e38
let (value, _) = u128_divrem(value, radix);
count += step;
if value != 0 {
count += digit_count!(@naive u64, radix, value as u64);
}
}
count
},
}
}
}
+434
View File
@@ -0,0 +1,434 @@
//! Optimized integer-to-string conversion routines for decimal values.
//! This algorihm is described in [`Faster Integer Formatting`], which uses
//! binary search trees for highly optimized digit writing. For large numbers,
//! the increased branching can destroy performance, but for 32-bit or smaller
//! integers it is always faster and can be optimized in 64-bit cases.
//!
//! This is based off of the work by James Anhalt (jeaiii) and Junekey Jeon
//! (jk-jeon). This has a few advantages, one is that indexing can be done
//! without bounds checking, without any major performance hits, which minimizes
//! the unchecked indexing and therefore potential unsoundness.
//!
//! This has some additional changes for performance enhancements, most notably,
//! it flattens out most of the comparisons and uses larger first, which
//! paradoxically seems to improve performance, potentially due to less
//! branching.
//!
//! See [Algorithm](/docs/Algorithm.md) for a more detailed description of
//! the algorithm choice here. See [Benchmarks](/docs/Benchmarks.md) for
//! recent benchmark data.
//!
//! [`Faster Integer Formatting`]: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
use lexical_util::digit::digit_to_char_const;
use lexical_util::div128::fast_u128_divrem;
use crate::table::DIGIT_TO_BASE10_SQUARED;
// Mask to extract the lower half.
const LO32: u64 = u32::MAX as u64;
/// Get the next 2 digits from the input.
#[inline(always)]
fn next2(prod: &mut u64) -> u32 {
*prod = (*prod & LO32) * 100;
(*prod >> 32) as u32
}
/// Quickly calculate `n / 1e10` and `n % 1e10`.
#[inline(always)]
fn u128_divrem_10_10pow10(n: u128) -> (u128, u64) {
fast_u128_divrem(
n,
10000000000,
18889465931478580854784,
10,
73075081866545145910184241635814150983,
31,
)
}
/// Quickly calculate `n / 1e10` and `n % 1e10`.
///
/// We use this for quickly breaking our integer into
/// chunks of 10 digits for fast u128 formatting.
#[inline(always)]
fn div128_rem_1e10(n: u128) -> (u128, u64) {
u128_divrem_10_10pow10(n)
}
// Index a value from a buffer without bounds checking.
macro_rules! i {
($array:ident[$index:expr]) => {
// SAFETY: Safe if `array.len() > index`.
unsafe { *$array.get_unchecked($index) }
};
}
// Write N digits to our buffer.
macro_rules! write_n {
(@1 $buffer:ident, $index:expr, $n:expr) => {{
let index = $index;
let digit = digit_to_char_const($n as u32, 10);
$buffer[index] = digit;
index + 1
}};
(@2 $buffer:ident, $index:expr, $r:expr) => {{
let index = $index;
let r = $r as usize;
// NOTE: This always should be true due to how we calculate our bounds.
// `r` is always a single digit, so `2 * r` must be smaller than our
// square table.
debug_assert!(r < DIGIT_TO_BASE10_SQUARED.len());
$buffer[index] = i!(DIGIT_TO_BASE10_SQUARED[r]);
$buffer[index + 1] = i!(DIGIT_TO_BASE10_SQUARED[r + 1]);
index + 2
}};
// Identical to `@2` except it's writing from the end, not front.
// This is for our Alexandrescu-popularized algorithm.
(@2sub $buffer:ident, $index:ident, $r:expr) => {{
$index -= 2;
_ = write_n!(@2 $buffer, $index, $r);
}};
// This writes 4 digits, using 2sub twice after getting the high and low.
(@4sub $buffer:ident, $index:ident, $value:ident) => {{
let r = $value % 10000;
$value /= 10000;
let r1 = 2 * (r / 100);
let r2 = 2 * (r % 100);
write_n!(@2sub $buffer, $index, r2);
write_n!(@2sub $buffer, $index, r1);
}};
}
// Print the next 2 digits, using `next2`.
macro_rules! print_n {
(@2 $buffer:ident, $index:ident, $prod:ident) => {
$index = write_n!(@2 $buffer, $index, next2(&mut $prod) * 2);
};
(@n $buffer:ident, $index:ident, $n:ident, $magic:expr, $shift:expr, $remaining:expr) => {{
let mut prod = ($n as u64) * $magic;
prod >>= $shift;
let two = (prod >> 32) as u32;
if two < 10 {
$index = write_n!(@1 $buffer, $index, two);
for _ in 0..$remaining {
print_n!(@2 $buffer, $index, prod);
}
} else {
$index = write_n!(@2 $buffer, $index, two * 2);
for _ in 0..$remaining {
print_n!(@2 $buffer, $index, prod);
}
}
$index
}};
}
// Optimized digit writers for the number of digits for each.
// This avoids code duplication while keeping our flat logic.
macro_rules! write_digits {
(@1 $buffer:ident, $n:ident) => {
write_n!(@1 $buffer, 0, $n)
};
(@2 $buffer:ident, $n:ident) => {
write_n!(@2 $buffer, 0, $n * 2)
};
// NOTE: This is only used for u8
(@3 $buffer:ident, $n:ident) => {{
// `42949673 = ceil(2^32 / 10^2)`
let mut y = $n as u64 * 42949673u64;
_ = write_n!(@1 $buffer, 0, y >> 32);
write_n!(@2 $buffer, 1, next2(&mut y) * 2)
}};
(@3-4 $buffer:ident, $n:ident) => {{
// `42949673 = ceil(2^32 / 10^2)`
let mut index = 0;
print_n!(@n $buffer, index, $n, 42949673u64, 0, 1)
}};
(@5 $buffer:ident, $n:ident) => {{
// `429497 == ceil(2^32 / 10^4)`
let mut y = $n as u64 * 429497u64;
_ = write_n!(@1 $buffer, 0, y >> 32);
_ = write_n!(@2 $buffer, 1, next2(&mut y) * 2);
write_n!(@2 $buffer, 3, next2(&mut y) * 2)
}};
(@5-6 $buffer:ident, $n:ident) => {{
// `429497 == ceil(2^32 / 10^4)`
let mut index = 0;
print_n!(@n $buffer, index, $n, 429497u64, 0, 2)
}};
(@7-8 $buffer:ident, $n:ident) => {{
// `281474978 == ceil(2^48 / 10^6) + 1`
let mut index = 0;
print_n!(@n $buffer, index, $n, 281474978u64, 16, 3)
}};
(@9 $buffer:ident, $n:ident) => {{
// 1441151882 = ceil(2^57 / 10^8) + 1
let mut y = ($n as u64) * 1441151882u64;
y >>= 25;
_ = write_n!(@1 $buffer, 0, y >> 32);
_ = write_n!(@2 $buffer, 1, next2(&mut y) * 2);
_ = write_n!(@2 $buffer, 3, next2(&mut y) * 2);
_ = write_n!(@2 $buffer, 5, next2(&mut y) * 2);
write_n!(@2 $buffer, 7, next2(&mut y) * 2)
}};
(@10 $buffer:ident, $n:ident) => {{
// `1441151881 = ceil(2^57 / 10^8)`
let mut y = ($n as u64) * 1441151881u64;
y >>= 25;
_ = write_n!(@2 $buffer, 0, (y >> 32) * 2);
_ = write_n!(@2 $buffer, 2, next2(&mut y) * 2);
_ = write_n!(@2 $buffer, 4, next2(&mut y) * 2);
_ = write_n!(@2 $buffer, 6, next2(&mut y) * 2);
write_n!(@2 $buffer, 8, next2(&mut y) * 2)
}};
(@10u64 $buffer:ident, $n:ident) => {{
// Unfortunately, there is no good way without using 128 bits,
// since the smallest interval overflows a 64-bit integer at
// ~>= 5.5e9. This requires the value to be in `[1e9, 1e10)`,
// since there's no lower bound for the calculation and so it
// will not work with smaller values.
// D = 32, k = 8, L = 28
// `11529215047 = ceil(2^60 / 10^8)`
let prod = ($n as u128) * 11529215047u128;
let mut y = (prod >> 28) as u64;
_ = write_n!(@2 $buffer, 0, (y >> 32) * 2);
_ = write_n!(@2 $buffer, 2, next2(&mut y) * 2);
_ = write_n!(@2 $buffer, 4, next2(&mut y) * 2);
_ = write_n!(@2 $buffer, 6, next2(&mut y) * 2);
write_n!(@2 $buffer, 8, next2(&mut y) * 2)
}};
(@10alex $buffer:ident, $n:ident, $offset:ident) => {{
// This always writes 10 digits for any value `[0, 1e10)`,
// but it uses a slower algorithm to do so. Since we don't
// have to worry about
let mut value = $n;
let mut index = 10 + $offset;
write_n!(@4sub $buffer, index, value);
write_n!(@4sub $buffer, index, value);
write_n!(@2sub $buffer, index, value * 2);
10 + $offset
}};
}
/// Optimized jeaiii algorithm for u8.
#[inline(always)]
pub fn from_u8(n: u8, buffer: &mut [u8]) -> usize {
// NOTE: For some reason, doing the large comparisons **FIRST**
// seems to be faster than the inverse, for both large and small
// values, which seems to make little sense. But, the benchmarks
// tell us reality.
let buffer = &mut buffer[..3];
if n >= 100 {
write_digits!(@3 buffer, n)
} else if n >= 10 {
write_digits!(@2 buffer, n)
} else {
write_digits!(@1 buffer, n)
}
}
/// Optimized jeaiii algorithm for u16.
#[inline(always)]
pub fn from_u16(n: u16, buffer: &mut [u8]) -> usize {
// NOTE: Like before, this optimizes better for large and small
// values if there's a flat comparison with larger values first.
let buffer = &mut buffer[..5];
if n >= 1_0000 {
write_digits!(@5 buffer, n)
} else if n >= 100 {
write_digits!(@3-4 buffer, n)
} else if n >= 10 {
write_digits!(@2 buffer, n)
} else {
write_digits!(@1 buffer, n)
}
}
/// Optimized jeaiii algorithm for u32.
#[inline(always)]
#[allow(clippy::collapsible_else_if)] // reason = "branching is fine-tuned for performance"
pub fn from_u32(n: u32, buffer: &mut [u8]) -> usize {
// NOTE: Like before, this optimizes better for large and small
// values if there's a flat comparison with larger values first.
let buffer = &mut buffer[..10];
if n < 1_0000 {
if n >= 100 {
write_digits!(@3-4 buffer, n)
} else if n >= 10 {
write_digits!(@2 buffer, n)
} else {
write_digits!(@1 buffer, n)
}
} else if n < 1_0000_0000 {
if n >= 100_0000 {
write_digits!(@7-8 buffer, n)
} else {
write_digits!(@5-6 buffer, n)
}
} else {
if n >= 10_0000_0000 {
write_digits!(@10 buffer, n)
} else {
write_digits!(@9 buffer, n)
}
}
}
/// Optimized jeaiii algorithm for u64.
#[inline(always)]
#[allow(clippy::collapsible_else_if)] // reason = "branching is fine-tuned for performance"
fn from_u64_impl(n: u64, buffer: &mut [u8], is_signed: bool) -> usize {
// NOTE: Like before, this optimizes better for large and small
// values if there's a flat comparison with larger values first.
const FACTOR: u64 = 100_0000_0000;
// NOTE `i64` takes a max of 19 digits, while `u64` takes a max of 20.
let buffer = if is_signed {
&mut buffer[..19]
} else {
&mut buffer[..20]
};
if n < 1_0000 {
// 1 to 4 digits
if n >= 100 {
write_digits!(@3-4 buffer, n)
} else if n >= 10 {
write_digits!(@2 buffer, n)
} else {
write_digits!(@1 buffer, n)
}
} else if n < FACTOR {
// 5 to 10 digits
if n >= 10_0000_0000 {
// NOTE: We DO NOT know if this is >= u32::MAX,
// and the `write_digits!(@10)` is only accurate
// if `n <= 5.5e9`, which we cannot guarantee.
write_digits!(@10u64 buffer, n)
} else if n >= 1_0000_0000 {
write_digits!(@9 buffer, n)
} else if n >= 100_0000 {
write_digits!(@7-8 buffer, n)
} else {
write_digits!(@5-6 buffer, n)
}
} else {
// 11-20 digits, can do in 2 steps (11-19 if is signed).
// NOTE: `hi` has to be in `[0, 2^31)`, while `lo` is in `[0, 10^11)`
// So, we can use our `from_u64_small` for hi. For our `lo`, we always
// need to write 10 digits. However, the `jeaiii` algorithm is too
// slow, so we use a modified variant of our 2-digit unfolding for
// exactly 10 digits to read our values. We can optimize this in
// 2x 4 digits and 1x 2 digits.
let hi = (n / FACTOR) as u32;
let lo = n % FACTOR;
let offset = from_u32(hi, buffer);
write_digits!(@10alex buffer, lo, offset)
}
}
/// Optimized jeaiii algorithm for u64.
#[inline(always)]
pub fn from_u64(n: u64, buffer: &mut [u8]) -> usize {
from_u64_impl(n, buffer, false)
}
/// Optimized jeaiii algorithm for i64, which must be positive.
///
/// This value **MUST** have originally been from an `i64`, since it
/// uses `19` for the bounds checked, so this will panic if `>= 10^19`
/// is passed to the function.
#[inline(always)]
pub fn from_i64(n: u64, buffer: &mut [u8]) -> usize {
debug_assert!(n <= 1000_0000_0000_0000_0000u64);
from_u64_impl(n, buffer, true)
}
/// Optimized jeaiii algorithm for u128.
#[inline(always)]
#[allow(clippy::collapsible_else_if)] // reason = "branching is fine-tuned for performance"
pub fn from_u128(n: u128, buffer: &mut [u8]) -> usize {
// NOTE: Like before, this optimizes better for large and small
// values if there's a flat comparison with larger values first.
let buffer = &mut buffer[..39];
if n < 1_0000 {
// 1 to 4 digits
if n >= 100 {
write_digits!(@3-4 buffer, n)
} else if n >= 10 {
write_digits!(@2 buffer, n)
} else {
write_digits!(@1 buffer, n)
}
} else if n < 100_0000_0000 {
// 5 to 10 digits
if n >= 10_0000_0000 {
// NOTE: We DO NOT know if this is >= u32::MAX,
// and the `write_digits!(@10)` is only accurate
// if `n <= 5.5e9`, which we cannot guarantee.
write_digits!(@10u64 buffer, n)
} else if n >= 1_0000_0000 {
write_digits!(@9 buffer, n)
} else if n >= 100_0000 {
write_digits!(@7-8 buffer, n)
} else {
write_digits!(@5-6 buffer, n)
}
} else {
// 11-39 digits, can do in 2-4 steps
// NOTE: We need to use fast division (`u128_divrem`) for this, which
// we can do in 2-4 steps (`2^128 - 1 == ~3.4e38`). So, we need to
// calculate the number of digits to avoid shifting into place, then
// once we do, we can write 1-3 `lo` digits and the `hi` digits (which
// must be in the range `[0, 2^29)`). Our `jeaiii` algorithm is too
// slow, so we use a modified variant of our 2-digit unfolding for
// exactly 10 digits to read our values. We can optimize this in
// 2x 4 digits and 1x 2 digits.
if n >= 100_0000_0000_0000_0000_0000_0000_0000 {
// 4 steps
let (mid, d) = div128_rem_1e10(n);
let (mid, c) = div128_rem_1e10(mid);
let (hi, b) = div128_rem_1e10(mid);
// NOTE: `2^128 == ~3.4e38`, so `a` must be in the
// range `[0, 2^29)`)
let a = hi as u32;
let mut offset = from_u32(a, buffer);
offset = write_digits!(@10alex buffer, b, offset);
offset = write_digits!(@10alex buffer, c, offset);
write_digits!(@10alex buffer, d, offset)
} else if n >= 1_0000_0000_0000_0000_0000 {
// 3 steps
let (mid, lo) = div128_rem_1e10(n);
let (hi, mid) = div128_rem_1e10(mid);
let hi = hi as u64;
let mut offset = from_u64(hi, buffer);
offset = write_digits!(@10alex buffer, mid, offset);
write_digits!(@10alex buffer, lo, offset)
} else {
// 2 steps
let (hi, lo) = div128_rem_1e10(n);
let hi = hi as u64;
let offset = from_u64(hi, buffer);
write_digits!(@10alex buffer, lo, offset)
}
}
}
+374
View File
@@ -0,0 +1,374 @@
//! Fast lexical integer-to-string conversion routines.
//!
//! This contains high-performance methods to write integers
//! directly to bytes, can be converted to [`str`] using
//! [`str::from_utf8`]. Using [`to_lexical`] is analogous to [`to_string`],
//! just writing to an existing buffer.
//!
//! [`str::from_utf8`]: core::str::from_utf8
//! [`to_lexical`]: ToLexical::to_lexical
//!
//! # Getting Started
//!
//! To write a number to bytes, use [`to_lexical`]:
//!
//! ```rust
//! # #[no_std]
//! # use core::str;
//! use lexical_write_integer::{FormattedSize, ToLexical};
//!
//! let mut buffer = [0u8; u64::FORMATTED_SIZE_DECIMAL];
//! let digits = 1234u64.to_lexical(&mut buffer);
//! assert_eq!(str::from_utf8(digits), Ok("1234"));
//! ```
//!
//! Using [`FormattedSize::FORMATTED_SIZE_DECIMAL`] guarantees the buffer
//! will be large enough to write the digits for all numbers of that
//! type.
//!
//! # Features
//!
//! * `format` - Add support for custom integer formatting (currently
//! unsupported).
//! * `power-of-two` - Add support for writing power-of-two integer strings.
//! * `radix` - Add support for strings of any radix.
//! * `compact` - Reduce code size at the cost of performance.
//! * `std` (Default) - Disable to allow use in a [`no_std`] environment.
//!
//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html
//!
//! A complete description of supported features includes:
//!
//! #### format
//!
//! Add support for custom integer formatting. Currently no custom styles are
//! supported but this could include digit [`separator`] support in the future.
//!
//! [`separator`]: NumberFormatBuilder::digit_separator
//!
//! <!--
//! For a list of all supported fields, see [Write Integer
//! Fields][NumberFormatBuilder#write-integer-fields].
//! -->
//!
//! #### power-of-two
//!
//! Enable writing numbers using radixes that are powers of two, that is, `2`,
//! `4`, `8`, `16`, and `32`. In these cases, you should use [`FORMATTED_SIZE`]
//! to create a sufficiently large buffer.
//!
//! [`FORMATTED_SIZE`]: FormattedSize::FORMATTED_SIZE
//!
//! ```rust
//! # #[cfg(feature = "power-of-two")] {
//! # use core::str;
//! use lexical_write_integer::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions};
//!
//! let mut buffer = [0u8; u64::FORMATTED_SIZE];
//! const BINARY: u128 = NumberFormatBuilder::binary();
//! const OPTIONS: Options = Options::new();
//! let digits = 1234u64.to_lexical_with_options::<BINARY>(&mut buffer, &OPTIONS);
//! assert_eq!(str::from_utf8(digits), Ok("10011010010"));
//! # }
//! ```
//!
//! #### radix
//!
//! Enable writing numbers using all radixes from `2` to `36`. This requires
//! more static storage than [`power-of-two`][crate#power-of-two], and increases
//! compile times, but can be quite useful for esoteric programming languages
//! which use duodecimal integers, for example.
//!
//! ```rust
//! # #[cfg(feature = "radix")] {
//! # use core::str;
//! use lexical_write_integer::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions};
//!
//! let mut buffer = [0u8; u64::FORMATTED_SIZE];
//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12);
//! const OPTIONS: Options = Options::new();
//! let digits = 1234u64.to_lexical_with_options::<FORMAT>(&mut buffer, &OPTIONS);
//! assert_eq!(str::from_utf8(digits), Ok("86A"));
//! # }
//! ```
//!
//! #### compact
//!
//! Reduce the generated code size at the cost of performance. This minimizes
//! the number of static tables, inlining, and generics used, drastically
//! reducing the size of the generated binaries.
//!
//! #### std
//!
//! Enable use of the standard library. Currently, the standard library
//! is not used, and may be disabled without any change in functionality
//! on stable.
//!
//! # Higher-Level APIs
//!
//! If you would like support for writing to [`String`] directly, use
//! [`lexical`] instead. If you would like an API that supports multiple numeric
//! conversions rather than just writing integers, use [`lexical-core`] instead.
//!
//! [`lexical`]: https://crates.io/crates/lexical
//! [`lexical-core`]: https://crates.io/crates/lexical-core
//!
//! # Version Support
//!
//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for
//! const generic support. Older versions of lexical support older Rust
//! versions.
//!
//! # Algorithm
//!
//! We use 3 algorithms for serializing numbers:
//! 1. [`Jeaiii Algorithm`] (decimal only)
//! 2. Power reduction to write 4 digits at a time (radix only)
//! 3. Compact, single-digit serialization
//!
//! ## Decimal
//!
//! Our decimal-based digit writers are based on the [`Jeaiii Algorithm`], which
//! branches based on the number of digits and writes digits to minimize the
//! number of additions and multiplications. This avoids the need to calculate
//! the number of digits ahead of time, by just branching on the value.
//!
//! James Anhalt's itoa algorithm along with Junekey Jeon's performance tweaks
//! have excellent performance, however, this can be further optimized. Both
//! James Anhalt's and Junekey Jeon's use a binary search for determining the
//! correct number of digits to print (for 32-bit integers).
//!
//! ```text
//! /\____________
//! / \______ \______
//! /\ \ \ \ \
//! 0 1 /\ /\ /\ /\
//! 2 3 4 5 6 7 8 9
//! ```
//!
//! This leads to a max tree depth of 4, and the major performance bottleneck
//! with larger type sizes is the branching. A minor modification can optimize
//! this, leadingg to a max tree depth of 3 while only required 1 extra
//! comparison at the top level. Also, we invert the comparisons: oddly enough,
//! our benchmarks show doing larger comparisons then smaller improves
//! performance for numbers with both large and small numbers of digits.
//!
//! ```text
//! ____________________
//! ___/_ __|__ \
//! / | \ / \ /\
//! /\ 1 0 /\ /\ 8 9
//! 3 2 6 7 4 5
//! ```
//!
//! For larger integers, we can apply the a similar algorithm with minor
//! modifications to minimize branching while keeping excellent performance.
//!
//! ## Radix
//!
//! Our radix-based algorithms work like this, carving off the lower digits and
//! writing them to the back of the buffer.
//!
//! ```rust,ignore
//! let mut value = 12345u32;
//! let buffer = [0u8; 32];
//! let digits = value.digit_count();
//! let bytes = buffer[..digits];
//!
//! let table = ...; // some pre-computed table of 2 * radix^2 length
//!
//! let radix = 10;
//! let radix2 = radix * radix;
//! let radix4 = radix2 * radix2
//! let mut index = bytes.len();
//! while value >= 10000 {
//! let r = value % radix4;
//! value /= radix4;
//! let r1 = 2 * (r / radix2) as usize;
//! let r2 = 2 * (r % radix2) as usize;
//!
//! // write 5, then 4
//! index -= 1;
//! bytes[index] = table[r2 + 1];
//! index -= 1;
//! bytes[index] = table[r2];
//!
//! // write 3 then 2
//! index -= 1;
//! bytes[index] = table[r1 + 1];
//! index -= 1;
//! bytes[index] = table[r1];
//! }
//!
//! // continue with radix^2 and then a single digit.
//! ```
//!
//! We can efficiently determine at compile time if the pre-computed
//! tables are large enough so there are no non-local safety considerations
//! there. The current logic call stack is:
//! 1. [`to_lexical`]
//! 2. [`decimal`][`dec`], [`compact`][`cmp`], or [`radix`][`rdx`] (gets the
//! correct tables and calls algorithm)
//! 3. [`jeaiii`]
//!
//! # Compact
//!
//! A compact, fallback algorithm uses a naive, simple algorithm,
//! where each loop generates a single digit. This comes at a performance
//! penalty, but produces smaller binaries. It is analogous to the below
//! code.
//!
//! ```rust,ignore
//! const fn digit_to_char(digit: u32) -> u8 {
//! match r {
//! b'0'..=b'9' => c - b'0',
//! b'A'..=b'Z' => c - b'A' + 10,
//! b'a'..=b'z' => c - b'a' + 10,
//! _ => 0xFF, // unreachable
//! }
//! }
//!
//! let mut value = 12345u32;
//! let buffer = [0u8; 32];
//! let digits = value.digit_count();
//! let bytes = buffer[..digits];
//!
//! let radix = 10;
//! let mut index = bytes.len();
//! while value >= radix {
//! let r = value % radix;
//! value /= radix;
//! index -= 1;
//! bytes[index] = digit_to_char(r);
//! }
//!
//! index -= 1;
//! bytes[index] = digit_to_char(value);
//! ```
//!
//! # Design
//!
//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md)
//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Benchmarks.md)
//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks)
//!
//! # Safety Guarantees
//!
//! <div class="warning info-warning">
//! <style>
//! .info-warning::before {
//! color: #87CEFAb0 !important;
//! }
//! .info-warning {
//! border-left: 2px solid #87CEFAb0 !important;
//! }
//! </style>
//!
//! This module uses some unsafe code to achieve accept acceptable performance.
//! Providing a buffer of insufficient size will cause the code to panic and
//! cannot lead to out-of-bounds access. The safety guarantees and logic are
//! described below.
//!
//! </div>
//!
//! ### Decimal
//!
//! Our decimal writer uses a branched algorithm and therefore the indexing for
//! each element in the buffer is known ahead of time. The digit
//! [`generation`][`digit-gen`] is [well-established][`Jeaiii Algorithm`] to
//! ensure the the lookup value is less than the size of the pre-computed table
//! (`2 * 10^2`, or 200), and as long as this invariant holds true, then no
//! undefined behavior can occur.
//!
//! [`digit-gen`]: https://github.com/jk-jeon/idiv/blob/main/subproject/example/jeaiii_analysis.cpp
//!
//! ### Radix
//!
//! The non-decimal writers rely on pre-computed tables and an exact calculation
//! of the digit count ([`digit_count`]) to avoid any overhead. Avoiding
//! intermediary copies is **CRITICAL** for fast performance so the entire
//! buffer must be known but assigned to use algorithms the compiler cannot
//! easily verify. This is because we use multi-digit optimizations with our
//! pre-computed tables, so we cannot just iterate over the slice and assign
//! iteratively. Using checked indexing for the pre-compuited table can lead to
//! 30%+ decreases in performance. However, with careful analysis and factoring
//! of the code, it's trivial to demonstrate both the table lookups and buffer
//! indexing are safe.
//!
//! For radixes that are 2^N, we use the `ceil(log(value | 1, radix))` which can
//! always be calculated through the number of leading [`zeros`][`log2_lz`]. For
//! other radixes, we calculate the number of digits exactly the same way as if
//! we were writing digits in an initial pass.
//!
//! ### Compact
//!
//! The compact decimal writer uses no unsafe indexing.
//!
//! [`digit_count`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/digit_count.rs#L180
//! [`to_lexical`]: crate::ToLexical::to_lexical
//! [`dec`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/decimal.rs#L278
//! [`jeaiii`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/jeaiii.rs
//! [`cmp`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/compact.rs
//! [`rdx`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/radix.rs
//! [`Jeaiii Algorithm`]: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/
//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html
//! [`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html
//! [`to_string`]: https://doc.rust-lang.org/alloc/string/trait.ToString.html#tymethod.to_string
//! [`log2_lz`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/digit_count.rs#L119
// We want to have the same safety guarantees as Rust core,
// so we allow unused unsafe to clearly document safety guarantees.
#![allow(unused_unsafe)]
#![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![deny(
clippy::doc_markdown,
clippy::unnecessary_safety_comment,
clippy::semicolon_if_nothing_returned,
clippy::unwrap_used,
clippy::as_underscore
)]
#![allow(
// used when concepts are logically separate
clippy::match_same_arms,
// loss of precision is intentional
clippy::integer_division,
// mathematical names use 1-character identifiers
clippy::min_ident_chars,
// these are not cryptographically secure contexts
clippy::integer_division_remainder_used,
// this can be intentional
clippy::module_name_repetitions,
// this is intentional: already passing a pointer and need performance
clippy::needless_pass_by_value,
// we use this for inline formatting for unsafe blocks
clippy::semicolon_inside_block,
)]
pub mod algorithm;
pub mod compact;
pub mod decimal;
pub mod digit_count;
pub mod jeaiii;
pub mod options;
pub mod radix;
pub mod table;
pub mod write;
mod api;
mod table_binary;
mod table_decimal;
mod table_radix;
// Re-exports
pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE};
pub use lexical_util::error::Error;
pub use lexical_util::format::{self, NumberFormat, NumberFormatBuilder};
pub use lexical_util::options::WriteOptions;
pub use lexical_util::result::Result;
pub use self::api::{ToLexical, ToLexicalWithOptions};
#[doc(inline)]
pub use self::options::{Options, OptionsBuilder};
@@ -0,0 +1,210 @@
//! Configuration options for writing integers.
//!
//! This currently has no functionality, since we do not
//! support any features for writing integers at this time.
//!
//! # Examples
//!
//! ```rust
//! # use core::str;
//! use lexical_write_integer::{Options, ToLexicalWithOptions};
//! use lexical_write_integer::format::STANDARD;
//!
//! const OPTIONS: Options = Options::builder()
//! .build_strict();
//!
//! const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::<u64, STANDARD>();
//! let mut buffer = [0u8; BUFFER_SIZE];
//! let value = 1234u64;
//! let digits = value.to_lexical_with_options::<STANDARD>(&mut buffer, &OPTIONS);
//! assert_eq!(str::from_utf8(digits), Ok("1234"));
//! ```
use lexical_util::constants::FormattedSize;
use lexical_util::format::NumberFormat;
use lexical_util::options::WriteOptions;
use lexical_util::result::Result;
/// Builder for [`Options`].
///
/// # Examples
///
/// ```rust
/// use core::str;
///
/// use lexical_write_integer::{Options, ToLexicalWithOptions};
/// use lexical_write_integer::format::STANDARD;
///
/// const OPTIONS: Options = Options::builder()
/// .build_strict();
///
/// const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::<u64, STANDARD>();
/// let mut buffer = [0u8; BUFFER_SIZE];
/// let value = 1234u64;
/// let digits = value.to_lexical_with_options::<STANDARD>(&mut buffer, &OPTIONS);
/// assert_eq!(str::from_utf8(digits), Ok("1234"));
/// ```
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct OptionsBuilder {}
impl OptionsBuilder {
/// Create new options builder with default options.
#[inline(always)]
pub const fn new() -> Self {
Self {}
}
// BUILDERS
/// Check if the builder state is valid.
#[inline(always)]
pub const fn is_valid(&self) -> bool {
true
}
/// Build the [`Options`] struct without validation.
///
/// <div class="warning">
///
/// This is completely safe, however, misusing this could cause panics at
/// runtime. Always check if [`is_valid`] prior to using the built
/// options.
///
/// </div>
///
/// [`is_valid`]: Self::is_valid
#[inline(always)]
pub const fn build_unchecked(&self) -> Options {
Options {}
}
/// Build the [`Options`] struct.
///
/// This can never panic.
///
/// <!-- # Panics
///
/// If the built options are not valid. This should always
/// be used within a const context to avoid panics at runtime.
/// -->
#[inline(always)]
pub const fn build_strict(&self) -> Options {
match self.build() {
Ok(value) => value,
Err(error) => core::panic!("{}", error.description()),
}
}
/// Build the [`Options`] struct.
#[inline(always)]
pub const fn build(&self) -> Result<Options> {
Ok(self.build_unchecked())
}
}
impl Default for OptionsBuilder {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
/// Immutable options to customize writing integers.
///
/// # Examples
///
/// ```rust
/// use core::str;
///
/// use lexical_write_integer::{Options, ToLexicalWithOptions};
/// use lexical_write_integer::format::STANDARD;
///
/// const OPTIONS: Options = Options::builder()
/// .build_strict();
///
/// const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::<u64, STANDARD>();
/// let mut buffer = [0u8; BUFFER_SIZE];
/// let value = 1234u64;
/// let digits = value.to_lexical_with_options::<STANDARD>(&mut buffer, &OPTIONS);
/// assert_eq!(str::from_utf8(digits), Ok("1234"));
/// ```
// FIXME: Add phantom data for private fields.
// This is a BREAKING change so requires a major API release.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Options {}
impl Options {
/// Create options with default values.
#[inline(always)]
pub const fn new() -> Self {
Self {}
}
/// Create the default options for a given radix.
#[inline(always)]
#[cfg(feature = "power-of-two")]
pub const fn from_radix(_: u8) -> Self {
Self::new()
}
/// Check if the options state is valid.
#[inline(always)]
pub const fn is_valid(&self) -> bool {
true
}
/// Get an upper bound on the required buffer size.
///
/// This is always [`FORMATTED_SIZE`][FormattedSize::FORMATTED_SIZE]
/// or [`FORMATTED_SIZE_DECIMAL`][FormattedSize::FORMATTED_SIZE_DECIMAL],
/// depending on the radix.
#[inline(always)]
pub const fn buffer_size_const<T: FormattedSize, const FORMAT: u128>(&self) -> usize {
if (NumberFormat::<FORMAT> {}.radix()) == 10 {
T::FORMATTED_SIZE_DECIMAL
} else {
T::FORMATTED_SIZE
}
}
// BUILDERS
/// Get [`OptionsBuilder`] as a static function.
#[inline(always)]
pub const fn builder() -> OptionsBuilder {
OptionsBuilder::new()
}
/// Create [`OptionsBuilder`] using existing values.
#[inline(always)]
pub const fn rebuild(&self) -> OptionsBuilder {
OptionsBuilder {}
}
}
impl Default for Options {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl WriteOptions for Options {
#[inline(always)]
fn is_valid(&self) -> bool {
Self::is_valid(self)
}
#[doc = lexical_util::write_options_doc!()]
#[inline(always)]
fn buffer_size<T: FormattedSize, const FORMAT: u128>(&self) -> usize {
self.buffer_size_const::<T, FORMAT>()
}
}
// PRE-DEFINED CONSTANTS
// ---------------------
/// Standard number format.
#[rustfmt::skip]
pub const STANDARD: Options = Options::new();
+62
View File
@@ -0,0 +1,62 @@
//! Radix-generic, lexical integer-to-string conversion routines.
//!
//! These routines are decently optimized: they unroll 4 loops at a time,
//! using pre-computed base^2 tables. However, due to static storage
//! reasons, it makes no sense to pre-compute the number of digits,
//! and therefore
//!
//! See [Algorithm](/docs/Algorithm.md) for a more detailed description of
//! the algorithm choice here.
#![cfg(not(feature = "compact"))]
#![cfg(feature = "power-of-two")]
#![doc(hidden)]
use lexical_util::format;
use lexical_util::num::{Integer, UnsignedInteger};
use crate::algorithm::{algorithm, algorithm_u128};
use crate::table::get_table;
/// Write integer to radix string.
pub trait Radix: UnsignedInteger {
/// # Safety
///
/// Safe as long as buffer is at least `FORMATTED_SIZE` elements long,
/// (or `FORMATTED_SIZE_DECIMAL` for decimal), and the radix is valid.
fn radix<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
self,
buffer: &mut [u8],
) -> usize;
}
// Implement radix for type.
macro_rules! radix_impl {
($($t:ty)*) => ($(
impl Radix for $t {
#[inline(always)]
fn radix<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
self,
buffer: &mut [u8]
) -> usize {
debug_assert!(<Self as Integer>::BITS <= 64);
let radix = format::radix_from_flags(FORMAT, MASK, SHIFT);
let table = get_table::<FORMAT, MASK, SHIFT>();
algorithm(self, radix, table, buffer)
}
}
)*);
}
radix_impl! { u8 u16 u32 u64 usize }
impl Radix for u128 {
#[inline(always)]
fn radix<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
self,
buffer: &mut [u8],
) -> usize {
let table = get_table::<FORMAT, MASK, SHIFT>();
algorithm_u128::<FORMAT, MASK, SHIFT>(self, table, buffer)
}
}
+11
View File
@@ -0,0 +1,11 @@
//! Pre-computed tables for writing integral strings.
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
// Re-export all the feature-specific files.
#[cfg(feature = "power-of-two")]
pub use crate::table_binary::*;
pub use crate::table_decimal::*;
#[cfg(feature = "radix")]
pub use crate::table_radix::*;
@@ -0,0 +1,216 @@
//! Pre-computed tables for writing non-decimal strings.
#![cfg(not(feature = "compact"))]
#![cfg(feature = "power-of-two")]
#![doc(hidden)]
#[cfg(not(feature = "radix"))]
use lexical_util::assert::debug_assert_radix;
#[cfg(not(feature = "radix"))]
use lexical_util::format::radix_from_flags;
#[cfg(not(feature = "radix"))]
use crate::table_decimal::*;
/// Get lookup table for 2 digit radix conversions.
///
/// * `FORMAT` - Number format.
/// * `MASK` - Mask to extract the radix value.
/// * `SHIFT` - Shift to normalize the radix value in `[0, 0x3f]`.
#[inline(always)]
#[cfg(not(feature = "radix"))]
pub fn get_table<const FORMAT: u128, const MASK: u128, const SHIFT: i32>() -> &'static [u8] {
debug_assert_radix(radix_from_flags(FORMAT, MASK, SHIFT));
match radix_from_flags(FORMAT, MASK, SHIFT) {
2 => &DIGIT_TO_BASE2_SQUARED,
4 => &DIGIT_TO_BASE4_SQUARED,
8 => &DIGIT_TO_BASE8_SQUARED,
10 => &DIGIT_TO_BASE10_SQUARED,
16 => &DIGIT_TO_BASE16_SQUARED,
32 => &DIGIT_TO_BASE32_SQUARED,
_ => unreachable!(),
}
}
// RADIX^2 TABLES
// --------------
pub const DIGIT_TO_BASE2_SQUARED: [u8; 8] = [b'0', b'0', b'0', b'1', b'1', b'0', b'1', b'1'];
pub const DIGIT_TO_BASE4_SQUARED: [u8; 32] = [
b'0', b'0', b'0', b'1', b'0', b'2', b'0', b'3', b'1', b'0', b'1', b'1', b'1', b'2', b'1', b'3',
b'2', b'0', b'2', b'1', b'2', b'2', b'2', b'3', b'3', b'0', b'3', b'1', b'3', b'2', b'3', b'3',
];
pub const DIGIT_TO_BASE8_SQUARED: [u8; 128] = [
b'0', b'0', b'0', b'1', b'0', b'2', b'0', b'3', b'0', b'4', b'0', b'5', b'0', b'6', b'0', b'7',
b'1', b'0', b'1', b'1', b'1', b'2', b'1', b'3', b'1', b'4', b'1', b'5', b'1', b'6', b'1', b'7',
b'2', b'0', b'2', b'1', b'2', b'2', b'2', b'3', b'2', b'4', b'2', b'5', b'2', b'6', b'2', b'7',
b'3', b'0', b'3', b'1', b'3', b'2', b'3', b'3', b'3', b'4', b'3', b'5', b'3', b'6', b'3', b'7',
b'4', b'0', b'4', b'1', b'4', b'2', b'4', b'3', b'4', b'4', b'4', b'5', b'4', b'6', b'4', b'7',
b'5', b'0', b'5', b'1', b'5', b'2', b'5', b'3', b'5', b'4', b'5', b'5', b'5', b'6', b'5', b'7',
b'6', b'0', b'6', b'1', b'6', b'2', b'6', b'3', b'6', b'4', b'6', b'5', b'6', b'6', b'6', b'7',
b'7', b'0', b'7', b'1', b'7', b'2', b'7', b'3', b'7', b'4', b'7', b'5', b'7', b'6', b'7', b'7',
];
pub const DIGIT_TO_BASE16_SQUARED: [u8; 512] = [
b'0', b'0', b'0', b'1', b'0', b'2', b'0', b'3', b'0', b'4', b'0', b'5', b'0', b'6', b'0', b'7',
b'0', b'8', b'0', b'9', b'0', b'A', b'0', b'B', b'0', b'C', b'0', b'D', b'0', b'E', b'0', b'F',
b'1', b'0', b'1', b'1', b'1', b'2', b'1', b'3', b'1', b'4', b'1', b'5', b'1', b'6', b'1', b'7',
b'1', b'8', b'1', b'9', b'1', b'A', b'1', b'B', b'1', b'C', b'1', b'D', b'1', b'E', b'1', b'F',
b'2', b'0', b'2', b'1', b'2', b'2', b'2', b'3', b'2', b'4', b'2', b'5', b'2', b'6', b'2', b'7',
b'2', b'8', b'2', b'9', b'2', b'A', b'2', b'B', b'2', b'C', b'2', b'D', b'2', b'E', b'2', b'F',
b'3', b'0', b'3', b'1', b'3', b'2', b'3', b'3', b'3', b'4', b'3', b'5', b'3', b'6', b'3', b'7',
b'3', b'8', b'3', b'9', b'3', b'A', b'3', b'B', b'3', b'C', b'3', b'D', b'3', b'E', b'3', b'F',
b'4', b'0', b'4', b'1', b'4', b'2', b'4', b'3', b'4', b'4', b'4', b'5', b'4', b'6', b'4', b'7',
b'4', b'8', b'4', b'9', b'4', b'A', b'4', b'B', b'4', b'C', b'4', b'D', b'4', b'E', b'4', b'F',
b'5', b'0', b'5', b'1', b'5', b'2', b'5', b'3', b'5', b'4', b'5', b'5', b'5', b'6', b'5', b'7',
b'5', b'8', b'5', b'9', b'5', b'A', b'5', b'B', b'5', b'C', b'5', b'D', b'5', b'E', b'5', b'F',
b'6', b'0', b'6', b'1', b'6', b'2', b'6', b'3', b'6', b'4', b'6', b'5', b'6', b'6', b'6', b'7',
b'6', b'8', b'6', b'9', b'6', b'A', b'6', b'B', b'6', b'C', b'6', b'D', b'6', b'E', b'6', b'F',
b'7', b'0', b'7', b'1', b'7', b'2', b'7', b'3', b'7', b'4', b'7', b'5', b'7', b'6', b'7', b'7',
b'7', b'8', b'7', b'9', b'7', b'A', b'7', b'B', b'7', b'C', b'7', b'D', b'7', b'E', b'7', b'F',
b'8', b'0', b'8', b'1', b'8', b'2', b'8', b'3', b'8', b'4', b'8', b'5', b'8', b'6', b'8', b'7',
b'8', b'8', b'8', b'9', b'8', b'A', b'8', b'B', b'8', b'C', b'8', b'D', b'8', b'E', b'8', b'F',
b'9', b'0', b'9', b'1', b'9', b'2', b'9', b'3', b'9', b'4', b'9', b'5', b'9', b'6', b'9', b'7',
b'9', b'8', b'9', b'9', b'9', b'A', b'9', b'B', b'9', b'C', b'9', b'D', b'9', b'E', b'9', b'F',
b'A', b'0', b'A', b'1', b'A', b'2', b'A', b'3', b'A', b'4', b'A', b'5', b'A', b'6', b'A', b'7',
b'A', b'8', b'A', b'9', b'A', b'A', b'A', b'B', b'A', b'C', b'A', b'D', b'A', b'E', b'A', b'F',
b'B', b'0', b'B', b'1', b'B', b'2', b'B', b'3', b'B', b'4', b'B', b'5', b'B', b'6', b'B', b'7',
b'B', b'8', b'B', b'9', b'B', b'A', b'B', b'B', b'B', b'C', b'B', b'D', b'B', b'E', b'B', b'F',
b'C', b'0', b'C', b'1', b'C', b'2', b'C', b'3', b'C', b'4', b'C', b'5', b'C', b'6', b'C', b'7',
b'C', b'8', b'C', b'9', b'C', b'A', b'C', b'B', b'C', b'C', b'C', b'D', b'C', b'E', b'C', b'F',
b'D', b'0', b'D', b'1', b'D', b'2', b'D', b'3', b'D', b'4', b'D', b'5', b'D', b'6', b'D', b'7',
b'D', b'8', b'D', b'9', b'D', b'A', b'D', b'B', b'D', b'C', b'D', b'D', b'D', b'E', b'D', b'F',
b'E', b'0', b'E', b'1', b'E', b'2', b'E', b'3', b'E', b'4', b'E', b'5', b'E', b'6', b'E', b'7',
b'E', b'8', b'E', b'9', b'E', b'A', b'E', b'B', b'E', b'C', b'E', b'D', b'E', b'E', b'E', b'F',
b'F', b'0', b'F', b'1', b'F', b'2', b'F', b'3', b'F', b'4', b'F', b'5', b'F', b'6', b'F', b'7',
b'F', b'8', b'F', b'9', b'F', b'A', b'F', b'B', b'F', b'C', b'F', b'D', b'F', b'E', b'F', b'F',
];
pub const DIGIT_TO_BASE32_SQUARED: [u8; 2048] = [
b'0', b'0', b'0', b'1', b'0', b'2', b'0', b'3', b'0', b'4', b'0', b'5', b'0', b'6', b'0', b'7',
b'0', b'8', b'0', b'9', b'0', b'A', b'0', b'B', b'0', b'C', b'0', b'D', b'0', b'E', b'0', b'F',
b'0', b'G', b'0', b'H', b'0', b'I', b'0', b'J', b'0', b'K', b'0', b'L', b'0', b'M', b'0', b'N',
b'0', b'O', b'0', b'P', b'0', b'Q', b'0', b'R', b'0', b'S', b'0', b'T', b'0', b'U', b'0', b'V',
b'1', b'0', b'1', b'1', b'1', b'2', b'1', b'3', b'1', b'4', b'1', b'5', b'1', b'6', b'1', b'7',
b'1', b'8', b'1', b'9', b'1', b'A', b'1', b'B', b'1', b'C', b'1', b'D', b'1', b'E', b'1', b'F',
b'1', b'G', b'1', b'H', b'1', b'I', b'1', b'J', b'1', b'K', b'1', b'L', b'1', b'M', b'1', b'N',
b'1', b'O', b'1', b'P', b'1', b'Q', b'1', b'R', b'1', b'S', b'1', b'T', b'1', b'U', b'1', b'V',
b'2', b'0', b'2', b'1', b'2', b'2', b'2', b'3', b'2', b'4', b'2', b'5', b'2', b'6', b'2', b'7',
b'2', b'8', b'2', b'9', b'2', b'A', b'2', b'B', b'2', b'C', b'2', b'D', b'2', b'E', b'2', b'F',
b'2', b'G', b'2', b'H', b'2', b'I', b'2', b'J', b'2', b'K', b'2', b'L', b'2', b'M', b'2', b'N',
b'2', b'O', b'2', b'P', b'2', b'Q', b'2', b'R', b'2', b'S', b'2', b'T', b'2', b'U', b'2', b'V',
b'3', b'0', b'3', b'1', b'3', b'2', b'3', b'3', b'3', b'4', b'3', b'5', b'3', b'6', b'3', b'7',
b'3', b'8', b'3', b'9', b'3', b'A', b'3', b'B', b'3', b'C', b'3', b'D', b'3', b'E', b'3', b'F',
b'3', b'G', b'3', b'H', b'3', b'I', b'3', b'J', b'3', b'K', b'3', b'L', b'3', b'M', b'3', b'N',
b'3', b'O', b'3', b'P', b'3', b'Q', b'3', b'R', b'3', b'S', b'3', b'T', b'3', b'U', b'3', b'V',
b'4', b'0', b'4', b'1', b'4', b'2', b'4', b'3', b'4', b'4', b'4', b'5', b'4', b'6', b'4', b'7',
b'4', b'8', b'4', b'9', b'4', b'A', b'4', b'B', b'4', b'C', b'4', b'D', b'4', b'E', b'4', b'F',
b'4', b'G', b'4', b'H', b'4', b'I', b'4', b'J', b'4', b'K', b'4', b'L', b'4', b'M', b'4', b'N',
b'4', b'O', b'4', b'P', b'4', b'Q', b'4', b'R', b'4', b'S', b'4', b'T', b'4', b'U', b'4', b'V',
b'5', b'0', b'5', b'1', b'5', b'2', b'5', b'3', b'5', b'4', b'5', b'5', b'5', b'6', b'5', b'7',
b'5', b'8', b'5', b'9', b'5', b'A', b'5', b'B', b'5', b'C', b'5', b'D', b'5', b'E', b'5', b'F',
b'5', b'G', b'5', b'H', b'5', b'I', b'5', b'J', b'5', b'K', b'5', b'L', b'5', b'M', b'5', b'N',
b'5', b'O', b'5', b'P', b'5', b'Q', b'5', b'R', b'5', b'S', b'5', b'T', b'5', b'U', b'5', b'V',
b'6', b'0', b'6', b'1', b'6', b'2', b'6', b'3', b'6', b'4', b'6', b'5', b'6', b'6', b'6', b'7',
b'6', b'8', b'6', b'9', b'6', b'A', b'6', b'B', b'6', b'C', b'6', b'D', b'6', b'E', b'6', b'F',
b'6', b'G', b'6', b'H', b'6', b'I', b'6', b'J', b'6', b'K', b'6', b'L', b'6', b'M', b'6', b'N',
b'6', b'O', b'6', b'P', b'6', b'Q', b'6', b'R', b'6', b'S', b'6', b'T', b'6', b'U', b'6', b'V',
b'7', b'0', b'7', b'1', b'7', b'2', b'7', b'3', b'7', b'4', b'7', b'5', b'7', b'6', b'7', b'7',
b'7', b'8', b'7', b'9', b'7', b'A', b'7', b'B', b'7', b'C', b'7', b'D', b'7', b'E', b'7', b'F',
b'7', b'G', b'7', b'H', b'7', b'I', b'7', b'J', b'7', b'K', b'7', b'L', b'7', b'M', b'7', b'N',
b'7', b'O', b'7', b'P', b'7', b'Q', b'7', b'R', b'7', b'S', b'7', b'T', b'7', b'U', b'7', b'V',
b'8', b'0', b'8', b'1', b'8', b'2', b'8', b'3', b'8', b'4', b'8', b'5', b'8', b'6', b'8', b'7',
b'8', b'8', b'8', b'9', b'8', b'A', b'8', b'B', b'8', b'C', b'8', b'D', b'8', b'E', b'8', b'F',
b'8', b'G', b'8', b'H', b'8', b'I', b'8', b'J', b'8', b'K', b'8', b'L', b'8', b'M', b'8', b'N',
b'8', b'O', b'8', b'P', b'8', b'Q', b'8', b'R', b'8', b'S', b'8', b'T', b'8', b'U', b'8', b'V',
b'9', b'0', b'9', b'1', b'9', b'2', b'9', b'3', b'9', b'4', b'9', b'5', b'9', b'6', b'9', b'7',
b'9', b'8', b'9', b'9', b'9', b'A', b'9', b'B', b'9', b'C', b'9', b'D', b'9', b'E', b'9', b'F',
b'9', b'G', b'9', b'H', b'9', b'I', b'9', b'J', b'9', b'K', b'9', b'L', b'9', b'M', b'9', b'N',
b'9', b'O', b'9', b'P', b'9', b'Q', b'9', b'R', b'9', b'S', b'9', b'T', b'9', b'U', b'9', b'V',
b'A', b'0', b'A', b'1', b'A', b'2', b'A', b'3', b'A', b'4', b'A', b'5', b'A', b'6', b'A', b'7',
b'A', b'8', b'A', b'9', b'A', b'A', b'A', b'B', b'A', b'C', b'A', b'D', b'A', b'E', b'A', b'F',
b'A', b'G', b'A', b'H', b'A', b'I', b'A', b'J', b'A', b'K', b'A', b'L', b'A', b'M', b'A', b'N',
b'A', b'O', b'A', b'P', b'A', b'Q', b'A', b'R', b'A', b'S', b'A', b'T', b'A', b'U', b'A', b'V',
b'B', b'0', b'B', b'1', b'B', b'2', b'B', b'3', b'B', b'4', b'B', b'5', b'B', b'6', b'B', b'7',
b'B', b'8', b'B', b'9', b'B', b'A', b'B', b'B', b'B', b'C', b'B', b'D', b'B', b'E', b'B', b'F',
b'B', b'G', b'B', b'H', b'B', b'I', b'B', b'J', b'B', b'K', b'B', b'L', b'B', b'M', b'B', b'N',
b'B', b'O', b'B', b'P', b'B', b'Q', b'B', b'R', b'B', b'S', b'B', b'T', b'B', b'U', b'B', b'V',
b'C', b'0', b'C', b'1', b'C', b'2', b'C', b'3', b'C', b'4', b'C', b'5', b'C', b'6', b'C', b'7',
b'C', b'8', b'C', b'9', b'C', b'A', b'C', b'B', b'C', b'C', b'C', b'D', b'C', b'E', b'C', b'F',
b'C', b'G', b'C', b'H', b'C', b'I', b'C', b'J', b'C', b'K', b'C', b'L', b'C', b'M', b'C', b'N',
b'C', b'O', b'C', b'P', b'C', b'Q', b'C', b'R', b'C', b'S', b'C', b'T', b'C', b'U', b'C', b'V',
b'D', b'0', b'D', b'1', b'D', b'2', b'D', b'3', b'D', b'4', b'D', b'5', b'D', b'6', b'D', b'7',
b'D', b'8', b'D', b'9', b'D', b'A', b'D', b'B', b'D', b'C', b'D', b'D', b'D', b'E', b'D', b'F',
b'D', b'G', b'D', b'H', b'D', b'I', b'D', b'J', b'D', b'K', b'D', b'L', b'D', b'M', b'D', b'N',
b'D', b'O', b'D', b'P', b'D', b'Q', b'D', b'R', b'D', b'S', b'D', b'T', b'D', b'U', b'D', b'V',
b'E', b'0', b'E', b'1', b'E', b'2', b'E', b'3', b'E', b'4', b'E', b'5', b'E', b'6', b'E', b'7',
b'E', b'8', b'E', b'9', b'E', b'A', b'E', b'B', b'E', b'C', b'E', b'D', b'E', b'E', b'E', b'F',
b'E', b'G', b'E', b'H', b'E', b'I', b'E', b'J', b'E', b'K', b'E', b'L', b'E', b'M', b'E', b'N',
b'E', b'O', b'E', b'P', b'E', b'Q', b'E', b'R', b'E', b'S', b'E', b'T', b'E', b'U', b'E', b'V',
b'F', b'0', b'F', b'1', b'F', b'2', b'F', b'3', b'F', b'4', b'F', b'5', b'F', b'6', b'F', b'7',
b'F', b'8', b'F', b'9', b'F', b'A', b'F', b'B', b'F', b'C', b'F', b'D', b'F', b'E', b'F', b'F',
b'F', b'G', b'F', b'H', b'F', b'I', b'F', b'J', b'F', b'K', b'F', b'L', b'F', b'M', b'F', b'N',
b'F', b'O', b'F', b'P', b'F', b'Q', b'F', b'R', b'F', b'S', b'F', b'T', b'F', b'U', b'F', b'V',
b'G', b'0', b'G', b'1', b'G', b'2', b'G', b'3', b'G', b'4', b'G', b'5', b'G', b'6', b'G', b'7',
b'G', b'8', b'G', b'9', b'G', b'A', b'G', b'B', b'G', b'C', b'G', b'D', b'G', b'E', b'G', b'F',
b'G', b'G', b'G', b'H', b'G', b'I', b'G', b'J', b'G', b'K', b'G', b'L', b'G', b'M', b'G', b'N',
b'G', b'O', b'G', b'P', b'G', b'Q', b'G', b'R', b'G', b'S', b'G', b'T', b'G', b'U', b'G', b'V',
b'H', b'0', b'H', b'1', b'H', b'2', b'H', b'3', b'H', b'4', b'H', b'5', b'H', b'6', b'H', b'7',
b'H', b'8', b'H', b'9', b'H', b'A', b'H', b'B', b'H', b'C', b'H', b'D', b'H', b'E', b'H', b'F',
b'H', b'G', b'H', b'H', b'H', b'I', b'H', b'J', b'H', b'K', b'H', b'L', b'H', b'M', b'H', b'N',
b'H', b'O', b'H', b'P', b'H', b'Q', b'H', b'R', b'H', b'S', b'H', b'T', b'H', b'U', b'H', b'V',
b'I', b'0', b'I', b'1', b'I', b'2', b'I', b'3', b'I', b'4', b'I', b'5', b'I', b'6', b'I', b'7',
b'I', b'8', b'I', b'9', b'I', b'A', b'I', b'B', b'I', b'C', b'I', b'D', b'I', b'E', b'I', b'F',
b'I', b'G', b'I', b'H', b'I', b'I', b'I', b'J', b'I', b'K', b'I', b'L', b'I', b'M', b'I', b'N',
b'I', b'O', b'I', b'P', b'I', b'Q', b'I', b'R', b'I', b'S', b'I', b'T', b'I', b'U', b'I', b'V',
b'J', b'0', b'J', b'1', b'J', b'2', b'J', b'3', b'J', b'4', b'J', b'5', b'J', b'6', b'J', b'7',
b'J', b'8', b'J', b'9', b'J', b'A', b'J', b'B', b'J', b'C', b'J', b'D', b'J', b'E', b'J', b'F',
b'J', b'G', b'J', b'H', b'J', b'I', b'J', b'J', b'J', b'K', b'J', b'L', b'J', b'M', b'J', b'N',
b'J', b'O', b'J', b'P', b'J', b'Q', b'J', b'R', b'J', b'S', b'J', b'T', b'J', b'U', b'J', b'V',
b'K', b'0', b'K', b'1', b'K', b'2', b'K', b'3', b'K', b'4', b'K', b'5', b'K', b'6', b'K', b'7',
b'K', b'8', b'K', b'9', b'K', b'A', b'K', b'B', b'K', b'C', b'K', b'D', b'K', b'E', b'K', b'F',
b'K', b'G', b'K', b'H', b'K', b'I', b'K', b'J', b'K', b'K', b'K', b'L', b'K', b'M', b'K', b'N',
b'K', b'O', b'K', b'P', b'K', b'Q', b'K', b'R', b'K', b'S', b'K', b'T', b'K', b'U', b'K', b'V',
b'L', b'0', b'L', b'1', b'L', b'2', b'L', b'3', b'L', b'4', b'L', b'5', b'L', b'6', b'L', b'7',
b'L', b'8', b'L', b'9', b'L', b'A', b'L', b'B', b'L', b'C', b'L', b'D', b'L', b'E', b'L', b'F',
b'L', b'G', b'L', b'H', b'L', b'I', b'L', b'J', b'L', b'K', b'L', b'L', b'L', b'M', b'L', b'N',
b'L', b'O', b'L', b'P', b'L', b'Q', b'L', b'R', b'L', b'S', b'L', b'T', b'L', b'U', b'L', b'V',
b'M', b'0', b'M', b'1', b'M', b'2', b'M', b'3', b'M', b'4', b'M', b'5', b'M', b'6', b'M', b'7',
b'M', b'8', b'M', b'9', b'M', b'A', b'M', b'B', b'M', b'C', b'M', b'D', b'M', b'E', b'M', b'F',
b'M', b'G', b'M', b'H', b'M', b'I', b'M', b'J', b'M', b'K', b'M', b'L', b'M', b'M', b'M', b'N',
b'M', b'O', b'M', b'P', b'M', b'Q', b'M', b'R', b'M', b'S', b'M', b'T', b'M', b'U', b'M', b'V',
b'N', b'0', b'N', b'1', b'N', b'2', b'N', b'3', b'N', b'4', b'N', b'5', b'N', b'6', b'N', b'7',
b'N', b'8', b'N', b'9', b'N', b'A', b'N', b'B', b'N', b'C', b'N', b'D', b'N', b'E', b'N', b'F',
b'N', b'G', b'N', b'H', b'N', b'I', b'N', b'J', b'N', b'K', b'N', b'L', b'N', b'M', b'N', b'N',
b'N', b'O', b'N', b'P', b'N', b'Q', b'N', b'R', b'N', b'S', b'N', b'T', b'N', b'U', b'N', b'V',
b'O', b'0', b'O', b'1', b'O', b'2', b'O', b'3', b'O', b'4', b'O', b'5', b'O', b'6', b'O', b'7',
b'O', b'8', b'O', b'9', b'O', b'A', b'O', b'B', b'O', b'C', b'O', b'D', b'O', b'E', b'O', b'F',
b'O', b'G', b'O', b'H', b'O', b'I', b'O', b'J', b'O', b'K', b'O', b'L', b'O', b'M', b'O', b'N',
b'O', b'O', b'O', b'P', b'O', b'Q', b'O', b'R', b'O', b'S', b'O', b'T', b'O', b'U', b'O', b'V',
b'P', b'0', b'P', b'1', b'P', b'2', b'P', b'3', b'P', b'4', b'P', b'5', b'P', b'6', b'P', b'7',
b'P', b'8', b'P', b'9', b'P', b'A', b'P', b'B', b'P', b'C', b'P', b'D', b'P', b'E', b'P', b'F',
b'P', b'G', b'P', b'H', b'P', b'I', b'P', b'J', b'P', b'K', b'P', b'L', b'P', b'M', b'P', b'N',
b'P', b'O', b'P', b'P', b'P', b'Q', b'P', b'R', b'P', b'S', b'P', b'T', b'P', b'U', b'P', b'V',
b'Q', b'0', b'Q', b'1', b'Q', b'2', b'Q', b'3', b'Q', b'4', b'Q', b'5', b'Q', b'6', b'Q', b'7',
b'Q', b'8', b'Q', b'9', b'Q', b'A', b'Q', b'B', b'Q', b'C', b'Q', b'D', b'Q', b'E', b'Q', b'F',
b'Q', b'G', b'Q', b'H', b'Q', b'I', b'Q', b'J', b'Q', b'K', b'Q', b'L', b'Q', b'M', b'Q', b'N',
b'Q', b'O', b'Q', b'P', b'Q', b'Q', b'Q', b'R', b'Q', b'S', b'Q', b'T', b'Q', b'U', b'Q', b'V',
b'R', b'0', b'R', b'1', b'R', b'2', b'R', b'3', b'R', b'4', b'R', b'5', b'R', b'6', b'R', b'7',
b'R', b'8', b'R', b'9', b'R', b'A', b'R', b'B', b'R', b'C', b'R', b'D', b'R', b'E', b'R', b'F',
b'R', b'G', b'R', b'H', b'R', b'I', b'R', b'J', b'R', b'K', b'R', b'L', b'R', b'M', b'R', b'N',
b'R', b'O', b'R', b'P', b'R', b'Q', b'R', b'R', b'R', b'S', b'R', b'T', b'R', b'U', b'R', b'V',
b'S', b'0', b'S', b'1', b'S', b'2', b'S', b'3', b'S', b'4', b'S', b'5', b'S', b'6', b'S', b'7',
b'S', b'8', b'S', b'9', b'S', b'A', b'S', b'B', b'S', b'C', b'S', b'D', b'S', b'E', b'S', b'F',
b'S', b'G', b'S', b'H', b'S', b'I', b'S', b'J', b'S', b'K', b'S', b'L', b'S', b'M', b'S', b'N',
b'S', b'O', b'S', b'P', b'S', b'Q', b'S', b'R', b'S', b'S', b'S', b'T', b'S', b'U', b'S', b'V',
b'T', b'0', b'T', b'1', b'T', b'2', b'T', b'3', b'T', b'4', b'T', b'5', b'T', b'6', b'T', b'7',
b'T', b'8', b'T', b'9', b'T', b'A', b'T', b'B', b'T', b'C', b'T', b'D', b'T', b'E', b'T', b'F',
b'T', b'G', b'T', b'H', b'T', b'I', b'T', b'J', b'T', b'K', b'T', b'L', b'T', b'M', b'T', b'N',
b'T', b'O', b'T', b'P', b'T', b'Q', b'T', b'R', b'T', b'S', b'T', b'T', b'T', b'U', b'T', b'V',
b'U', b'0', b'U', b'1', b'U', b'2', b'U', b'3', b'U', b'4', b'U', b'5', b'U', b'6', b'U', b'7',
b'U', b'8', b'U', b'9', b'U', b'A', b'U', b'B', b'U', b'C', b'U', b'D', b'U', b'E', b'U', b'F',
b'U', b'G', b'U', b'H', b'U', b'I', b'U', b'J', b'U', b'K', b'U', b'L', b'U', b'M', b'U', b'N',
b'U', b'O', b'U', b'P', b'U', b'Q', b'U', b'R', b'U', b'S', b'U', b'T', b'U', b'U', b'U', b'V',
b'V', b'0', b'V', b'1', b'V', b'2', b'V', b'3', b'V', b'4', b'V', b'5', b'V', b'6', b'V', b'7',
b'V', b'8', b'V', b'9', b'V', b'A', b'V', b'B', b'V', b'C', b'V', b'D', b'V', b'E', b'V', b'F',
b'V', b'G', b'V', b'H', b'V', b'I', b'V', b'J', b'V', b'K', b'V', b'L', b'V', b'M', b'V', b'N',
b'V', b'O', b'V', b'P', b'V', b'Q', b'V', b'R', b'V', b'S', b'V', b'T', b'V', b'U', b'V', b'V',
];
@@ -0,0 +1,43 @@
//! Pre-computed tables for writing decimal strings.
#![cfg(not(feature = "compact"))]
#![doc(hidden)]
// RADIX^2 TABLES
// --------------
// Conditionally compile the pre-computed radix**2 tables.
// These tables take `2 * (value % (radix^2))`, and return
// two consecutive values corresponding to both digits.
//
// Total array storage:
// Without radix: ~430 B:
// 200 u8
// 11 f32
// 23 f64
// With radix: ~55 KB.
// 32210 u8
// 518 f32
// 2610 f64
// Provides ~5x performance enhancement.
//
// These arrays are cache-friendly, for each BASE[2-36] table,
// elements can be access sequentially 2-at-a-time, preventing as many
// cache misses inside inner loops. For example, accessing the two elements
// for a remainder of `3` for the radix^2 in radix 2 will give you `1` and `1`,
// at indexes 6 and 7.
pub const DIGIT_TO_BASE10_SQUARED: [u8; 200] = [
b'0', b'0', b'0', b'1', b'0', b'2', b'0', b'3', b'0', b'4', b'0', b'5', b'0', b'6', b'0', b'7',
b'0', b'8', b'0', b'9', b'1', b'0', b'1', b'1', b'1', b'2', b'1', b'3', b'1', b'4', b'1', b'5',
b'1', b'6', b'1', b'7', b'1', b'8', b'1', b'9', b'2', b'0', b'2', b'1', b'2', b'2', b'2', b'3',
b'2', b'4', b'2', b'5', b'2', b'6', b'2', b'7', b'2', b'8', b'2', b'9', b'3', b'0', b'3', b'1',
b'3', b'2', b'3', b'3', b'3', b'4', b'3', b'5', b'3', b'6', b'3', b'7', b'3', b'8', b'3', b'9',
b'4', b'0', b'4', b'1', b'4', b'2', b'4', b'3', b'4', b'4', b'4', b'5', b'4', b'6', b'4', b'7',
b'4', b'8', b'4', b'9', b'5', b'0', b'5', b'1', b'5', b'2', b'5', b'3', b'5', b'4', b'5', b'5',
b'5', b'6', b'5', b'7', b'5', b'8', b'5', b'9', b'6', b'0', b'6', b'1', b'6', b'2', b'6', b'3',
b'6', b'4', b'6', b'5', b'6', b'6', b'6', b'7', b'6', b'8', b'6', b'9', b'7', b'0', b'7', b'1',
b'7', b'2', b'7', b'3', b'7', b'4', b'7', b'5', b'7', b'6', b'7', b'7', b'7', b'8', b'7', b'9',
b'8', b'0', b'8', b'1', b'8', b'2', b'8', b'3', b'8', b'4', b'8', b'5', b'8', b'6', b'8', b'7',
b'8', b'8', b'8', b'9', b'9', b'0', b'9', b'1', b'9', b'2', b'9', b'3', b'9', b'4', b'9', b'5',
b'9', b'6', b'9', b'7', b'9', b'8', b'9', b'9',
];
File diff suppressed because it is too large Load Diff
+195
View File
@@ -0,0 +1,195 @@
//! Shared trait and methods for writing integers.
#![doc(hidden)]
use lexical_util::format;
/// Select the back-end.
#[cfg(feature = "compact")]
use crate::compact::Compact;
#[cfg(not(feature = "compact"))]
use crate::decimal::Decimal;
#[cfg(all(not(feature = "compact"), feature = "power-of-two"))]
use crate::radix::Radix;
/// Define the implementation to write significant digits.
macro_rules! write_mantissa {
($($t:tt)+) => {
/// Internal implementation to write significant digits for float writers.
#[doc(hidden)]
#[inline(always)]
fn write_mantissa<const FORMAT: u128>(self, buffer: &mut [u8]) -> usize {
self.write_integer::<FORMAT, { format::RADIX }, { format::RADIX_SHIFT }>(buffer)
}
/// Internal implementation to write significant digits for float writers.
#[doc(hidden)]
#[inline(always)]
fn write_mantissa_signed<const FORMAT: u128>(self, buffer: &mut [u8]) -> usize {
self.write_integer_signed::<FORMAT, { format::RADIX }, { format::RADIX_SHIFT }>(buffer)
}
};
}
/// Define the implementation to write exponent digits.
macro_rules! write_exponent {
($($t:tt)+) => (
// NOTE: This should always be signed, but for backwards compatibility as
// a precaution we keep the original just in case someone uses the private API.
/// Internal implementation to write exponent digits for float writers.
// NOTE: This is not part of the public API.
#[doc(hidden)]
#[inline(always)]
#[deprecated = "use `write_exponent_signed`, since exponents are always signed."]
fn write_exponent<const FORMAT: u128>(self, buffer: &mut [u8]) -> usize
{
self.write_integer::<FORMAT, { format::EXPONENT_RADIX }, { format::EXPONENT_RADIX_SHIFT }>(buffer)
}
/// Internal implementation to write exponent digits for float writers.
#[doc(hidden)]
#[inline(always)]
fn write_exponent_signed<const FORMAT: u128>(self, buffer: &mut [u8]) -> usize
{
self.write_integer_signed::<FORMAT, { format::EXPONENT_RADIX }, { format::EXPONENT_RADIX_SHIFT }>(buffer)
}
)
}
/// Write integer trait, implemented in terms of the compact back-end.
#[cfg(feature = "compact")]
pub trait WriteInteger: Compact {
/// Forward write integer parameters to an unoptimized backend.
///
/// # Preconditions
///
/// `self` must be non-negative and unsigned.
///
/// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE
/// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
fn write_integer<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
self,
buffer: &mut [u8],
) -> usize {
let radix = format::radix_from_flags(FORMAT, MASK, SHIFT);
self.compact(radix, buffer)
}
/// Forward write integer parameters to an optimized backend.
///
/// This requires a type that was previously signed.
///
/// # Preconditions
///
/// `self` must be non-negative but is `>= 0` and `<= Signed::MAX`.
///
/// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
#[inline(always)]
fn write_integer_signed<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
self,
buffer: &mut [u8],
) -> usize {
self.write_integer::<FORMAT, MASK, SHIFT>(buffer)
}
write_mantissa!(Compact);
write_exponent!(Compact);
}
/// Write integer trait, implemented in terms of the optimized, decimal
/// back-end.
#[cfg(all(not(feature = "compact"), not(feature = "power-of-two")))]
pub trait WriteInteger: Decimal {
/// Forward write integer parameters to an optimized backend.
///
/// # Preconditions
///
/// `self` must be non-negative and unsigned.
///
/// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
#[inline(always)]
fn write_integer<const __: u128, const ___: u128, const ____: i32>(
self,
buffer: &mut [u8],
) -> usize {
self.decimal(buffer)
}
/// Forward write integer parameters to an optimized backend.
///
/// This requires a type that was previously signed.
///
/// # Preconditions
///
/// `self` must be non-negative but is `>= 0` and `<= Signed::MAX`.
///
/// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
#[inline(always)]
fn write_integer_signed<const __: u128, const ___: u128, const ____: i32>(
self,
buffer: &mut [u8],
) -> usize {
self.decimal_signed(buffer)
}
write_mantissa!(Decimal);
write_exponent!(Decimal);
}
/// Write integer trait, implemented in terms of the optimized, decimal or radix
/// back-end.
#[cfg(all(not(feature = "compact"), feature = "power-of-two"))]
pub trait WriteInteger: Decimal + Radix {
/// Forward write integer parameters to an optimized backend.
///
/// # Preconditions
///
/// `self` must be non-negative and unsigned.
///
/// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE
/// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
#[inline(always)]
fn write_integer<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
self,
buffer: &mut [u8],
) -> usize {
if format::radix_from_flags(FORMAT, MASK, SHIFT) == 10 {
self.decimal(buffer)
} else {
self.radix::<FORMAT, MASK, SHIFT>(buffer)
}
}
/// Forward write integer parameters to an optimized backend.
///
/// This requires a type that was previously signed.
///
/// # Preconditions
///
/// `self` must be non-negative but is `>= 0` and `<= Signed::MAX`.
///
/// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
#[inline(always)]
fn write_integer_signed<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
self,
buffer: &mut [u8],
) -> usize {
if format::radix_from_flags(FORMAT, MASK, SHIFT) == 10 {
self.decimal_signed(buffer)
} else {
self.radix::<FORMAT, MASK, SHIFT>(buffer)
}
}
write_mantissa!(Decimal + Radix);
write_exponent!(Decimal + Radix);
}
macro_rules! write_integer_impl {
($($t:ty)*) => ($(
impl WriteInteger for $t {}
)*)
}
write_integer_impl! { u8 u16 u32 u64 u128 usize }