#![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;
#[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)
};
}
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]) };
}};
}
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) };
}};
}
#[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");
assert!((2..=36).contains(&radix), "radix must be >= 2 and <= 36");
let radix2 = radix * radix;
let radix4 = radix2 * radix2;
let radix = T::from_u32(radix);
assert!(table.len() >= radix2 as usize * 2, "table must be 2 * radix^2 long");
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));
write_digits!(buffer, index, table, r2);
write_digits!(buffer, index, table, r1);
}
}
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;
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) * usize::as_cast(value);
write_digits!(buffer, index, table, r);
}
index
}
#[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;
let index = unsafe { write_digits(value, radix, table, buffer, index, count) };
let end = start.saturating_sub(step);
let zeros = unsafe { &mut i!(buffer[end..index]) };
zeros.fill(b'0');
end
}
#[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,
{
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");
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];
_ = unsafe { write_digits(value, radix, table, buffer, buffer.len(), count) };
count
}
#[inline(always)]
pub 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);
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");
if value <= u64::MAX as u128 {
return algorithm(value as u64, radix, table, buffer);
}
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];
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;
}
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
}