#![doc(hidden)]
use lexical_util::format::NumberFormat;
use lexical_util::num::{as_cast, Integer, UnsignedInteger};
use lexical_util::step::max_step;
macro_rules! into_error {
($code:ident, $index:expr) => {
Err((lexical_util::error::Error::$code($index)))
};
}
macro_rules! into_ok_complete {
($value:expr, $index:expr) => {
Ok(as_cast($value))
};
}
macro_rules! into_ok_partial {
($value:expr, $index:expr) => {
Ok((as_cast($value), $index))
};
}
macro_rules! invalid_digit_complete {
(
$value:ident,
$iter:ident,
$format:ident,
$is_negative:ident,
$start_index:ident,
$t:ident,
$u:ident
) => {{
into_error!(InvalidDigit, $iter.cursor() - 1)
}};
}
macro_rules! invalid_digit_partial {
(
$value:ident,
$iter:ident,
$format:ident,
$is_negative:ident,
$start_index:ident,
$t:ident,
$u:ident
) => {{
let radix = NumberFormat::<{ $format }>::MANTISSA_RADIX;
let count = $iter.current_count() - $start_index - 1;
if is_overflow::<$t, $u, $format>($value, count, $is_negative) {
let min = min_step(radix, <$t as Integer>::BITS, <$t>::IS_SIGNED);
if <$t>::IS_SIGNED && $is_negative {
into_error!(Underflow, (count - 1).min(min + 1))
} else {
into_error!(Overflow, (count - 1).min(min + 1))
}
} else {
into_ok_partial!($value, $iter.cursor() - 1)
}
}};
}
macro_rules! parse_sign {
($iter:ident, $format:ident) => {
match $iter.peek() {
Some(&b'+') if !$format.no_positive_mantissa_sign() => (false, 1),
Some(&b'+') if $format.no_positive_mantissa_sign() => {
return into_error!(InvalidPositiveSign, 0);
},
Some(&b'-') if T::IS_SIGNED => (true, 1),
Some(_) if $format.required_mantissa_sign() => return into_error!(MissingSign, 0),
_ => (false, 0),
}
};
}
#[cfg_attr(not(feature = "compact"), inline)]
pub(super) fn is_overflow<T, U, const FORMAT: u128>(
value: U,
count: usize,
is_negative: bool,
) -> bool
where
T: Integer,
U: UnsignedInteger,
{
let format = NumberFormat::<{ FORMAT }> {};
let max = max_step(format.radix(), T::BITS, T::IS_SIGNED);
let radix: U = as_cast(format.radix());
let min_value: U = radix.pow(max as u32 - 1);
if T::IS_SIGNED {
let max_value: U = as_cast::<U, _>(T::MAX) + U::ONE;
if count > max
|| (count == max
&& (value < min_value || value > max_value || (!is_negative && value == max_value)))
{
return true;
}
} else if count > max || (count == max && value < min_value) {
return true;
}
false
}
macro_rules! parse_value {
(
$iter:ident,
$is_negative:ident,
$format:ident,
$start_index:ident,
$t:ident,
$u:ident,
$parser:ident,
$invalid_digit:ident,
$into_ok:ident
) => {{
let mut value = <$u>::ZERO;
let format = NumberFormat::<{ $format }> {};
$parser!(value, $iter, $format, $is_negative, $start_index, $t, $u, $invalid_digit);
let count = $iter.current_count() - $start_index;
if is_overflow::<$t, $u, $format>(value, count, $is_negative) {
let min = min_step(format.radix(), <$t as Integer>::BITS, <$t>::IS_SIGNED);
if <$t>::IS_SIGNED && $is_negative {
into_error!(Underflow, (count - 1).min(min + 1))
} else {
into_error!(Overflow, (count - 1).min(min + 1))
}
} else if <$t>::IS_SIGNED && $is_negative {
$into_ok!(as_cast::<$t, _>(value.wrapping_neg()), $iter.length())
} else {
$into_ok!(value, $iter.length())
}
}};
}
#[rustfmt::skip]
macro_rules! parse_1digit {
(
$value:ident,
$iter:ident,
$format:ident,
$is_negative:ident,
$start_index:ident,
$t:ident,
$u:ident,
$invalid_digit:ident
) => {{
let format = NumberFormat::<{ $format }>;
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 => {
let base_suffix = format.base_suffix();
if cfg!(feature = "format") && base_suffix != 0 && $iter.cursor() - $start_index > 1 {
let is_suffix = if format.case_sensitive_base_suffix() {
c == base_suffix
} else {
c.to_ascii_lowercase() == base_suffix.to_ascii_lowercase()
};
if is_suffix && $iter.is_done() {
break;
} else if is_suffix {
unsafe { $iter.step_unchecked() };
}
}
return $invalid_digit!(
$value,
$iter,
$format,
$is_negative,
$start_index,
$t,
$u
);
},
};
$value = $value.wrapping_mul(as_cast(radix));
$value = $value.wrapping_add(as_cast(digit));
}
}};
}
#[rustfmt::skip]
macro_rules! algorithm {
(
$bytes:ident,
$format:ident,
$t:ident,
$u:ident,
$parser:ident,
$invalid_digit:ident,
$into_ok:ident
) => {{
let format = NumberFormat::<{ $format }> {};
let mut byte = $bytes.bytes::<{ $format }>();
let mut iter = byte.integer_iter();
let (is_negative, shift) = parse_sign!(iter, format);
unsafe { iter.step_by_unchecked(shift) };
if iter.is_done() {
return into_error!(Empty, shift);
}
let mut start_index = iter.cursor();
let zeros = iter.skip_zeros();
start_index += zeros;
let base_prefix = format.base_prefix();
let mut is_prefix = false;
if cfg!(feature = "format") && base_prefix != 0 && zeros == 1 {
if let Some(&c) = iter.peek() {
is_prefix = if format.case_sensitive_base_prefix() {
c == base_prefix
} else {
c.to_ascii_lowercase() == base_prefix.to_ascii_lowercase()
};
if is_prefix {
unsafe { iter.step_unchecked() };
if iter.is_done() {
return into_error!(Empty, iter.cursor());
} else {
start_index += 1;
}
}
}
}
if cfg!(feature = "format") && !is_prefix && format.no_integer_leading_zeros() && zeros != 0 {
let index = iter.cursor() - zeros;
if zeros > 1 {
return into_error!(InvalidLeadingZeros, index);
}
match iter.peek().map(|&c| char_to_digit_const(c, format.radix())) {
Some(Some(_)) => return into_error!(InvalidLeadingZeros, index),
_ => return $into_ok!(<$t>::ZERO, index),
};
}
parse_value!(
iter,
is_negative,
$format,
start_index,
$t,
$u,
$parser,
$invalid_digit,
$into_ok
)
}};
}