#![doc(hidden)]
use lexical_util::digit::char_to_digit_const;
use lexical_util::error::Error;
use lexical_util::format::NumberFormat;
use lexical_util::iterator::{AsBytes, Bytes, DigitsIter, Iter};
use lexical_util::num::{as_cast, Integer};
use lexical_util::result::Result;
use crate::Options;
const fn can_try_parse_multidigits<'a, Iter: DigitsIter<'a>, const FORMAT: u128>(_: &Iter) -> bool {
let format = NumberFormat::<FORMAT> {};
Iter::IS_CONTIGUOUS && (cfg!(not(feature = "power-of-two")) || format.mantissa_radix() <= 10)
}
#[cfg_attr(not(feature = "format"), allow(unused_macros))]
macro_rules! required_digits {
() => {
NumberFormat::<FORMAT>::REQUIRED_INTEGER_DIGITS
|| NumberFormat::<FORMAT>::REQUIRED_MANTISSA_DIGITS
};
}
macro_rules! into_ok_complete {
($value:expr, $index:expr, $count:expr) => {{
#[cfg(not(feature = "format"))]
return Ok(as_cast($value));
#[cfg(feature = "format")]
if required_digits!() && $count == 0 {
into_error!(Empty, $index);
} else {
return Ok(as_cast($value));
}
}};
}
macro_rules! into_ok_partial {
($value:expr, $index:expr, $count:expr) => {{
#[cfg(not(feature = "format"))]
return Ok((as_cast($value), $index));
#[cfg(feature = "format")]
if required_digits!() && $count == 0 {
into_error!(Empty, $index);
} else {
return Ok((as_cast($value), $index));
}
}};
}
macro_rules! invalid_digit_complete {
($value:expr, $index:expr, $count:expr) => {
into_error!(InvalidDigit, $index - 1)
};
}
macro_rules! invalid_digit_partial {
($value:expr, $index:expr, $count:expr) => {
into_ok_partial!($value, $index - 1, $count)
};
}
macro_rules! into_error {
($code:ident, $index:expr) => {{
return Err(Error::$code($index));
}};
}
#[cfg(feature = "format")]
macro_rules! fmt_invalid_digit {
(
$value:ident, $iter:ident, $c:expr, $start_index:ident, $invalid_digit:ident, $is_end:expr
) => {{
debug_assert!($iter.is_contiguous() || $is_end);
let base_suffix = NumberFormat::<FORMAT>::BASE_SUFFIX;
let uncased_base_suffix = NumberFormat::<FORMAT>::CASE_SENSITIVE_BASE_SUFFIX;
if base_suffix != 0 && $iter.cursor() - $start_index > 1 {
let is_suffix = if uncased_base_suffix {
$c == base_suffix
} else {
$c.eq_ignore_ascii_case(&base_suffix)
};
if is_suffix && $is_end && $iter.is_buffer_empty() {
break;
} else if !$iter.is_buffer_empty() {
unsafe { $iter.step_unchecked() };
}
}
$invalid_digit!($value, $iter.cursor(), $iter.current_count())
}};
}
#[cfg(not(feature = "format"))]
macro_rules! fmt_invalid_digit {
(
$value:ident, $iter:ident, $c:expr, $start_index:ident, $invalid_digit:ident, $is_end:expr
) => {{
$invalid_digit!($value, $iter.cursor(), $iter.current_count());
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! parse_sign {
(
$byte:ident,
$is_signed:expr,
$no_positive:expr,
$required:expr,
$invalid_positive:ident,
$missing:ident
) => {
match $byte.integer_iter().first() {
Some(&b'+') if !$no_positive => {
unsafe { $byte.step_unchecked() };
Ok(false)
},
Some(&b'+') if $no_positive => Err(Error::$invalid_positive($byte.cursor())),
Some(&b'-') if $is_signed => {
unsafe { $byte.step_unchecked() };
Ok(true)
},
Some(_) if $required => Err(Error::$missing($byte.cursor())),
_ if $required => Err(Error::$missing($byte.cursor())),
_ => Ok(false),
}
};
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn parse_sign<T: Integer, const FORMAT: u128>(byte: &mut Bytes<'_, FORMAT>) -> Result<bool> {
let format = NumberFormat::<FORMAT> {};
parse_sign!(
byte,
T::IS_SIGNED,
format.no_positive_mantissa_sign(),
format.required_mantissa_sign(),
InvalidPositiveSign,
MissingSign
)
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn is_4digits<const FORMAT: u128>(v: u32) -> bool {
let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX;
debug_assert!(radix <= 10);
let add = 0x46 + 10 - radix;
let add = add + (add << 8) + (add << 16) + (add << 24);
let sub = 0x3030_3030;
let a = v.wrapping_add(add);
let b = v.wrapping_sub(sub);
(a | b) & 0x8080_8080 == 0
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn parse_4digits<const FORMAT: u128>(mut v: u32) -> u32 {
let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX;
debug_assert!(radix <= 10);
v -= 0x3030_3030;
v = (v * radix) + (v >> 8);
v = ((v & 0x0000007f) * radix * radix) + ((v >> 16) & 0x0000007f);
v
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn try_parse_4digits<'a, T, Iter, const FORMAT: u128>(iter: &mut Iter) -> Option<T>
where
T: Integer,
Iter: DigitsIter<'a>,
{
debug_assert!(NumberFormat::<{ FORMAT }>::MANTISSA_RADIX <= 10);
debug_assert!(Iter::IS_CONTIGUOUS);
let bytes = u32::from_le(iter.peek_u32()?);
if is_4digits::<FORMAT>(bytes) {
unsafe { iter.step_by_unchecked(4) };
Some(T::as_cast(parse_4digits::<FORMAT>(bytes)))
} else {
None
}
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn is_8digits<const FORMAT: u128>(v: u64) -> bool {
let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX;
debug_assert!(radix <= 10);
let add = 0x46 + 10 - radix;
let add = add + (add << 8) + (add << 16) + (add << 24);
let add = (add as u64) | ((add as u64) << 32);
let sub = 0x3030_3030_3030_3030;
let a = v.wrapping_add(add);
let b = v.wrapping_sub(sub);
(a | b) & 0x8080_8080_8080_8080 == 0
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn parse_8digits<const FORMAT: u128>(mut v: u64) -> u64 {
let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX as u64;
debug_assert!(radix <= 10);
let radix2 = radix * radix;
let radix4 = radix2 * radix2;
let radix6 = radix2 * radix4;
let mask = 0x0000_00FF_0000_00FFu64;
let mul1 = radix2 + (radix6 << 32);
let mul2 = 1 + (radix4 << 32);
v -= 0x3030_3030_3030_3030;
v = (v * radix) + (v >> 8);
let v1 = (v & mask).wrapping_mul(mul1);
let v2 = ((v >> 16) & mask).wrapping_mul(mul2);
((v1.wrapping_add(v2) >> 32) as u32) as u64
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn try_parse_8digits<'a, T, Iter, const FORMAT: u128>(iter: &mut Iter) -> Option<T>
where
T: Integer,
Iter: DigitsIter<'a>,
{
debug_assert!(NumberFormat::<{ FORMAT }>::MANTISSA_RADIX <= 10);
debug_assert!(Iter::IS_CONTIGUOUS);
let bytes = u64::from_le(iter.peek_u64()?);
if is_8digits::<FORMAT>(bytes) {
unsafe { iter.step_by_unchecked(8) };
Some(T::as_cast(parse_8digits::<FORMAT>(bytes)))
} else {
None
}
}
macro_rules! parse_1digit_unchecked {
(
$value:ident,
$iter:ident,
$add_op:ident,
$start_index:ident,
$invalid_digit:ident,
$is_end:expr
) => {{
let radix = NumberFormat::<FORMAT>::MANTISSA_RADIX;
while let Some(&c) = $iter.next() {
let digit = match char_to_digit_const(c, radix) {
Some(v) => v,
None => fmt_invalid_digit!($value, $iter, c, $start_index, $invalid_digit, $is_end),
};
$value = $value.wrapping_mul(as_cast(radix)).$add_op(as_cast(digit));
}
}};
}
macro_rules! parse_1digit_checked {
(
$value:ident,
$iter:ident,
$add_op:ident,
$start_index:ident,
$invalid_digit:ident,
$overflow:ident
) => {{
let radix = NumberFormat::<FORMAT>::MANTISSA_RADIX;
while let Some(&c) = $iter.next() {
let digit = match char_to_digit_const(c, radix) {
Some(v) => v,
None => fmt_invalid_digit!($value, $iter, c, $start_index, $invalid_digit, true),
};
$value =
match $value.checked_mul(as_cast(radix)).and_then(|x| x.$add_op(as_cast(digit))) {
Some(value) => value,
None => into_error!($overflow, $iter.cursor() - 1),
}
}
}};
}
macro_rules! parse_digits_unchecked {
(
$value:ident,
$iter:ident,
$add_op:ident,
$start_index:ident,
$invalid_digit:ident,
$no_multi_digit:expr,
$is_end:expr
) => {{
let can_multi = can_try_parse_multidigits::<_, FORMAT>(&$iter);
let use_multi = can_multi && !$no_multi_digit;
let format = NumberFormat::<FORMAT> {};
if use_multi && T::BITS >= 64 && $iter.buffer_length() >= 8 {
let radix8 = T::from_u32(format.radix8());
while let Some(value) = try_parse_8digits::<T, _, FORMAT>(&mut $iter) {
$value = $value.wrapping_mul(radix8).$add_op(value);
}
} else if use_multi && T::BITS == 32 && $iter.buffer_length() >= 4 {
let radix4 = T::from_u32(format.radix4());
while let Some(value) = try_parse_4digits::<T, _, FORMAT>(&mut $iter) {
$value = $value.wrapping_mul(radix4).$add_op(value);
}
}
parse_1digit_unchecked!($value, $iter, $add_op, $start_index, $invalid_digit, $is_end)
}};
}
macro_rules! parse_digits_checked {
(
$value:ident,
$iter:ident,
$add_op:ident,
$add_op_uc:ident,
$start_index:ident,
$invalid_digit:ident,
$overflow:ident,
$no_multi_digit:expr,
$overflow_digits:expr
) => {{
if cfg!(not(feature = "format")) || $iter.is_contiguous() {
if let Some(mut small) = $iter.take_n($overflow_digits) {
let mut small_iter = small.integer_iter();
parse_digits_unchecked!(
$value,
small_iter,
$add_op_uc,
$start_index,
$invalid_digit,
$no_multi_digit,
false
);
}
}
parse_1digit_checked!($value, $iter, $add_op, $start_index, $invalid_digit, $overflow)
}};
}
#[rustfmt::skip]
macro_rules! algorithm {
($bytes:ident, $into_ok:ident, $invalid_digit:ident, $no_multi_digit:expr) => {{
let mut byte = $bytes.bytes::<FORMAT>();
let radix = NumberFormat::<FORMAT>::MANTISSA_RADIX;
let is_negative = parse_sign::<T, FORMAT>(&mut byte)?;
let mut iter = byte.integer_iter();
if iter.is_buffer_empty() {
#[cfg(not(feature = "format"))]
into_error!(Empty, iter.cursor());
#[cfg(feature = "format")]
if required_digits!() {
into_error!(Empty, iter.cursor());
} else {
$into_ok!(T::ZERO, iter.cursor(), 0)
}
}
#[allow(unused_variables, unused_mut)]
let mut start_index = iter.cursor();
#[cfg_attr(not(feature = "format"), allow(unused_variables))]
let format = NumberFormat::<FORMAT> {};
#[cfg(feature = "format")]
if format.has_base_prefix() || format.no_integer_leading_zeros() {
let zeros = iter.skip_zeros();
start_index += zeros;
let mut is_prefix = false;
let base_prefix = format.base_prefix();
if base_prefix != 0 && zeros == 1 {
if iter.read_if_value(base_prefix, format.case_sensitive_base_prefix()).is_some() {
is_prefix = true;
if iter.is_buffer_empty() {
into_error!(Empty, iter.cursor());
} else {
start_index += 1;
}
}
}
if !is_prefix && format.no_integer_leading_zeros() && zeros != 0 {
let index = iter.cursor() - zeros;
if zeros > 1 {
into_error!(InvalidLeadingZeros, index);
}
match iter.peek().map(|&c| char_to_digit_const(c, format.radix())) {
Some(Some(_)) => into_error!(InvalidLeadingZeros, index),
Some(None) => $invalid_digit!(<T>::ZERO, iter.cursor() + 1, iter.current_count()),
None => $into_ok!(<T>::ZERO, index, iter.current_count()),
};
}
}
let overflow_digits = T::overflow_digits(radix);
let cannot_overflow = iter.as_slice().len() <= overflow_digits;
let mut value = T::ZERO;
if cannot_overflow && is_negative {
parse_digits_unchecked!(value, iter, wrapping_sub, start_index, $invalid_digit, $no_multi_digit, true);
} if cannot_overflow {
parse_digits_unchecked!(value, iter, wrapping_add, start_index, $invalid_digit, $no_multi_digit, true);
} else if is_negative {
parse_digits_checked!(value, iter, checked_sub, wrapping_sub, start_index, $invalid_digit, Underflow, $no_multi_digit, overflow_digits);
} else {
parse_digits_checked!(value, iter, checked_add, wrapping_add, start_index, $invalid_digit, Overflow, $no_multi_digit, overflow_digits);
}
$into_ok!(value, iter.buffer_length(), iter.current_count())
}};
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn algorithm_complete<T, const FORMAT: u128>(bytes: &[u8], options: &Options) -> Result<T>
where
T: Integer,
{
algorithm!(bytes, into_ok_complete, invalid_digit_complete, options.get_no_multi_digit())
}
#[cfg_attr(not(feature = "compact"), inline(always))]
pub fn algorithm_partial<T, const FORMAT: u128>(
bytes: &[u8],
options: &Options,
) -> Result<(T, usize)>
where
T: Integer,
{
algorithm!(bytes, into_ok_partial, invalid_digit_partial, options.get_no_multi_digit())
}