use super::ZEROS;
use crate::error::{ErrorCode, PResult};
use crate::swar::{first_match, load_u64};
#[inline(always)]
pub(crate) const fn digit_stop_mask(v: u64) -> u64 {
let a = v.wrapping_add(0x4646_4646_4646_4646);
let b = v.wrapping_sub(ZEROS);
(a | b) & 0x8080_8080_8080_8080
}
#[inline(always)]
pub(crate) const fn parse_8_digits(v: u64) -> u64 {
const MASK: u64 = 0x0000_00FF_0000_00FF;
const MUL1: u64 = 0x000F_4240_0000_0064; const MUL2: u64 = 0x0000_2710_0000_0001; let mut val = v - ZEROS;
val = (val * 10) + (val >> 8);
(((val & MASK).wrapping_mul(MUL1)) + (((val >> 16) & MASK).wrapping_mul(MUL2))) >> 32
}
#[inline(always)]
pub(crate) const fn is_digit(c: u8) -> bool {
c.wrapping_sub(b'0') < 10
}
pub(crate) const SAFE_DIGITS: usize = 19;
pub(crate) const POW10_U64: [u64; 9] = [
1,
10,
100,
1_000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
];
#[inline(always)]
pub(crate) fn parse_leading_digits(word: u64, k: usize) -> u64 {
debug_assert!(k <= 7);
let s = 8 * (8 - k.max(1));
let filled = (word << s) | (ZEROS >> (64 - s));
parse_8_digits(core::hint::select_unpredictable(k == 0, ZEROS, filled))
}
#[inline(always)]
pub(crate) fn parse_u64(buf: &[u8], i: &mut usize) -> PResult<u64> {
let idx = *i;
if idx + 16 <= buf.len() {
let (first_word, second_word) = unsafe { (load_u64(buf, idx), load_u64(buf, idx + 8)) };
let first = first_word as u8;
let stop = digit_stop_mask(first_word);
if stop != 0 {
let k = first_match(stop);
if k == 0 {
return Err(not_a_number(first));
}
if first == b'0' && k > 1 {
return Err(ErrorCode::InvalidNumber);
}
*i = idx + k;
return Ok(parse_leading_digits(first_word, k));
}
if first == b'0' {
return Err(ErrorCode::InvalidNumber);
}
let stop = digit_stop_mask(second_word);
if stop != 0 {
let k = first_match(stop);
*i = idx + 8 + k;
return Ok(
parse_8_digits(first_word) * POW10_U64[k] + parse_leading_digits(second_word, k)
);
}
}
let (value, end) = parse_u64_wide(buf, idx)?;
*i = end;
Ok(value)
}
#[cold]
#[inline(never)]
fn not_a_number(first: u8) -> ErrorCode {
if first == b'-' {
ErrorCode::NumberOutOfRange
} else {
ErrorCode::ExpectedNumber
}
}
#[inline(always)]
pub(crate) fn fold_digits(buf: &[u8], mut idx: usize, mut acc: u64) -> (u64, usize) {
let n = buf.len();
while idx + 8 <= n {
let word = unsafe { load_u64(buf, idx) };
let stop = digit_stop_mask(word);
if stop != 0 {
let k = first_match(stop);
acc = acc
.wrapping_mul(POW10_U64[k])
.wrapping_add(parse_leading_digits(word, k));
return (acc, idx + k);
}
acc = acc
.wrapping_mul(100_000_000)
.wrapping_add(parse_8_digits(word));
idx += 8;
}
while idx < n {
let d = buf[idx].wrapping_sub(b'0');
if d >= 10 {
break;
}
acc = acc.wrapping_mul(10).wrapping_add(d as u64);
idx += 1;
}
(acc, idx)
}
#[inline(never)]
fn parse_u64_wide(buf: &[u8], start: usize) -> PResult<(u64, usize)> {
if start >= buf.len() {
return Err(ErrorCode::UnexpectedEnd);
}
let first = buf[start];
if !is_digit(first) {
return Err(not_a_number(first));
}
let (value, end) = fold_digits(buf, start, 0);
finish_wide(buf, start, end, value)
}
#[inline(always)]
fn finish_wide(buf: &[u8], start: usize, end: usize, value: u64) -> PResult<(u64, usize)> {
let len = end - start;
if buf[start] == b'0' && len > 1 {
return Err(ErrorCode::InvalidNumber);
}
if len > SAFE_DIGITS {
return recheck_wide(buf, start, end).map(|v| (v, end));
}
Ok((value, end))
}
#[cold]
#[inline(never)]
fn recheck_wide(buf: &[u8], start: usize, end: usize) -> PResult<u64> {
let mut value: u64 = 0;
let mut idx = start;
while idx < end {
let c = buf[idx] - b'0';
value = match value.checked_mul(10).and_then(|v| v.checked_add(c as u64)) {
Some(v) => v,
None => return Err(ErrorCode::NumberOutOfRange),
};
idx += 1;
}
Ok(value)
}
#[inline(always)]
pub(crate) fn reject_float_tail(buf: &[u8], i: usize) -> PResult<()> {
if i < buf.len() {
match buf[i] {
b'.' | b'e' | b'E' => return Err(ErrorCode::InvalidNumber),
_ => {}
}
}
Ok(())
}
#[inline(always)]
pub(crate) fn parse_i64(buf: &[u8], i: &mut usize) -> PResult<i64> {
let negative = buf.get(*i) == Some(&b'-');
*i += negative as usize;
let magnitude = parse_u64(buf, i)?;
if magnitude > (i64::MAX as u64) + negative as u64 {
return Err(ErrorCode::NumberOutOfRange);
}
let flip = (negative as i64).wrapping_neg();
Ok(((magnitude as i64) ^ flip).wrapping_sub(flip))
}