#![cfg(not(feature = "compact"))]
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) };
}};
}
#[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");
let radix = T::from_u32(radix);
let radix2 = radix * radix;
let radix4 = radix2 * radix2;
assert!(radix <= T::from_u32(36), "radix must be <= 36");
assert!(table.len() >= radix2.as_usize() * 2, "table must be 2 * radix^2 long");
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
}
#[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 index_unchecked_mut!(buffer[end..index]) };
zeros.fill(b'0');
end
}
#[inline(always)]
pub fn algorithm<T>(value: T, radix: u32, table: &[u8], buffer: &mut [u8], count: usize) -> usize
where
T: UnsignedInteger,
{
assert!(T::BITS >= 32, "Must have at least 32 bits in the input.");
assert!(radix <= 36, "radix must be <= 36");
assert!(table.len() >= (radix * radix * 2) as usize, "table must be 2 * radix^2 long");
assert!(count <= buffer.len());
let buffer = &mut buffer[..count];
unsafe { write_digits(value, radix, table, buffer, buffer.len(), count) }
}
#[inline(always)]
pub fn algorithm_u128<const FORMAT: u128, const MASK: u128, const SHIFT: i32>(
value: u128,
table: &[u8],
buffer: &mut [u8],
count: usize,
) -> usize {
assert!(NumberFormat::<{ FORMAT }> {}.is_valid());
assert!(count <= buffer.len());
let buffer = &mut buffer[..count];
let radix = radix_from_flags(FORMAT, MASK, SHIFT);
assert!(radix <= 36, "radix must be <= 36");
assert!(table.len() >= (radix * radix * 2) as usize, "table must be 2 * radix^2 long");
if value <= u64::MAX as u128 {
return unsafe { algorithm(value as u64, radix, table, buffer, count) };
}
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, 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_from_flags(FORMAT, MASK, SHIFT));
index = unsafe { write_step_digits(mid, radix, table, buffer, index, step, count) };
if index != 0 {
index = unsafe { write_digits(value as u64, radix, table, buffer, index, count) };
}
index
}