use crate::error::{ErrorCode, PResult};
use crate::swar::load_u64;
#[inline(always)]
const fn is_8_digits(v: u64) -> bool {
let a = v.wrapping_add(0x4646_4646_4646_4646);
let b = v.wrapping_sub(0x3030_3030_3030_3030);
(a | b) & 0x8080_8080_8080_8080 == 0
}
#[inline(always)]
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 - 0x3030_3030_3030_3030;
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
}
const SAFE_DIGITS: usize = 19;
#[inline(always)]
pub(crate) fn parse_u64(buf: &[u8], i: &mut usize) -> PResult<u64> {
let n = buf.len();
let idx = *i;
if idx >= n {
return Err(ErrorCode::UnexpectedEnd);
}
let first = buf[idx].wrapping_sub(b'0');
if first >= 10 {
return Err(not_a_number(buf[idx]));
}
if first == 0 {
if idx + 1 < n && is_digit(buf[idx + 1]) {
return Err(ErrorCode::InvalidNumber);
}
*i = idx + 1;
return Ok(0);
}
let stop = n.min(idx + SAFE_DIGITS);
let mut value = first as u64;
let mut k = idx + 1;
while k < stop {
let c = buf[k].wrapping_sub(b'0');
if c >= 10 {
*i = k;
return Ok(value);
}
value = value * 10 + c as u64;
k += 1;
}
parse_u64_wide(buf, i, idx)
}
#[cold]
#[inline(never)]
fn not_a_number(first: u8) -> ErrorCode {
if first == b'-' {
ErrorCode::NumberOutOfRange
} else {
ErrorCode::ExpectedNumber
}
}
#[inline(never)]
fn parse_u64_wide(buf: &[u8], i: &mut usize, start: usize) -> PResult<u64> {
let n = buf.len();
let mut idx = start;
let mut value: u64 = 0;
while idx + 8 <= n {
let word = unsafe { load_u64(buf, idx) };
if !is_8_digits(word) {
break;
}
value = value
.wrapping_mul(100_000_000)
.wrapping_add(parse_8_digits(word));
idx += 8;
}
while idx < n {
let c = buf[idx].wrapping_sub(b'0');
if c >= 10 {
break;
}
value = value.wrapping_mul(10).wrapping_add(c as u64);
idx += 1;
}
let len = idx - start;
*i = idx;
if len > SAFE_DIGITS {
return recheck_wide(buf, start, idx);
}
Ok(value)
}
#[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 = *i < buf.len() && buf[*i] == b'-';
if negative {
*i += 1;
}
let magnitude = parse_u64(buf, i)?;
if negative {
if magnitude > (i64::MAX as u64) + 1 {
return Err(ErrorCode::NumberOutOfRange);
}
Ok((magnitude as i64).wrapping_neg())
} else {
if magnitude > i64::MAX as u64 {
return Err(ErrorCode::NumberOutOfRange);
}
Ok(magnitude as i64)
}
}