#![cfg(not(feature = "compact"))]
#![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;
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 { index_unchecked_mut!($bytes[$index] = $table[$r + 1]) };
$index -= 1;
unsafe { index_unchecked_mut!($bytes[$index] = $table[$r]) };
}};
}
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 { index_unchecked_mut!($bytes[$index]) = digit_to_char($r) };
}};
}
unsafe fn write_digits<T: UnsignedInteger>(
mut value: T,
radix: u32,
table: &[u8],
buffer: &mut [u8],
mut index: usize,
) -> usize {
debug_assert_radix(radix);
let radix = T::from_u32(radix);
let radix2 = radix * radix;
let radix4 = radix2 * radix2;
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));
write_digits!(buffer, index, table, r2);
write_digits!(buffer, index, table, r1);
}
while value >= radix2 {
let r = usize::as_cast(T::TWO * (value % radix2));
value /= radix2;
write_digits!(buffer, index, table, r);
}
if value < radix {
let r = u32::as_cast(value);
write_digit!(buffer, index, r);
} else {
let r = usize::as_cast(T::TWO * value);
write_digits!(buffer, index, table, r);
}
index
}
unsafe fn write_step_digits<T: UnsignedInteger>(
value: T,
radix: u32,
table: &[u8],
buffer: &mut [u8],
index: usize,
step: usize,
) -> usize {
debug_assert_radix(radix);
let start = index;
let index = unsafe { write_digits(value, radix, table, buffer, index) };
let end = start.saturating_sub(step);
unsafe {
let zeros = &mut index_unchecked_mut!(buffer[end..index]);
slice_fill_unchecked!(zeros, b'0');
}
end
}
#[inline]
pub unsafe fn algorithm<T>(value: T, radix: u32, table: &[u8], buffer: &mut [u8]) -> usize
where
T: UnsignedInteger,
{
debug_assert!(T::BITS >= 32, "Must have at least 32 bits in the input.");
debug_assert_radix(radix);
unsafe { write_digits(value, radix, table, buffer, buffer.len()) }
}
#[inline]
pub unsafe fn algorithm_u128<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
value: u128,
table: &[u8],
buffer: &mut [u8],
) -> usize {
assert!(NumberFormat::<{ FORMAT }> {}.is_valid());
let radix = radix_from_flags(FORMAT, MASK, SHIFT);
if value <= u64::MAX as _ {
return unsafe { algorithm(value as u64, radix, table, buffer) };
}
let step = u64_step(radix_from_flags(FORMAT, MASK, SHIFT));
let (value, low) = u128_divrem(value, radix_from_flags(FORMAT, MASK, SHIFT));
let mut index = buffer.len();
index = unsafe { write_step_digits(low, radix, table, buffer, index, step) };
if value <= u64::MAX as _ {
return unsafe { write_digits(value as u64, radix, table, buffer, index) };
}
let (value, mid) = u128_divrem(value, radix_from_flags(FORMAT, MASK, SHIFT));
index = unsafe { write_step_digits(mid, radix, table, buffer, index, step) };
if index != 0 {
index = unsafe { write_digits(value as u64, radix, table, buffer, index) };
}
index
}