use std::{fmt, io};
use num_traits::{
ops::overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub},
FromPrimitive, Zero,
};
use crate::ByteReader;
#[inline]
pub fn ascii_digits<I>(reader: &mut ByteReader, mut offset: usize) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingAdd + OverflowingMul,
{
let mut value = I::zero();
let mut overflow = false;
while let Some(digit @ b'0'..=b'9') = reader.request_byte_at_offset(offset) {
offset += 1;
let (new_value, overflowed) = value.overflowing_mul(&I::from_u8(10).unwrap());
overflow |= overflowed;
value = new_value;
let (new_value, overflowed) = value.overflowing_add(&I::from_u8(digit - b'0').unwrap());
overflow |= overflowed;
value = new_value;
}
((!overflow).then(|| value), offset)
}
#[cold]
#[inline(never)]
fn ascii_digits_cont_pos<I>(
reader: &mut ByteReader,
mut offset: usize,
value: Option<I>,
) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingAdd + OverflowingMul,
{
#![allow(clippy::or_fun_call)]
let mut overflow = value.is_none();
let mut value = value.unwrap_or(I::zero());
while let Some(digit @ b'0'..=b'9') = reader.request_byte_at_offset(offset) {
offset += 1;
let (new_value, overflowed) = value.overflowing_mul(&I::from_u8(10).unwrap());
overflow |= overflowed;
value = new_value;
let (new_value, overflowed) = value.overflowing_add(&I::from_u8(digit - b'0').unwrap());
overflow |= overflowed;
value = new_value;
}
((!overflow).then(|| value), offset)
}
#[cold]
#[inline(never)]
fn ascii_digits_cont_neg<I>(
reader: &mut ByteReader,
mut offset: usize,
value: Option<I>,
) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingSub + OverflowingMul,
{
#![allow(clippy::or_fun_call)]
let mut overflow = value.is_none();
let mut value = value.unwrap_or(I::zero());
while let Some(digit @ b'0'..=b'9') = reader.request_byte_at_offset(offset) {
offset += 1;
let (new_value, overflowed) = value.overflowing_mul(&I::from_u8(10).unwrap());
overflow |= overflowed;
value = new_value;
let (new_value, overflowed) = value.overflowing_sub(&I::from_u8(digit - b'0').unwrap());
overflow |= overflowed;
value = new_value;
}
((!overflow).then(|| value), offset)
}
#[inline]
pub fn signed_ascii_digits<I>(reader: &mut ByteReader, mut offset: usize) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingAdd + OverflowingSub + OverflowingMul,
{
let mut value = I::zero();
let mut overflow = false;
if let Some(b'-') = reader.request_byte_at_offset(offset) {
if let Some(digit @ b'0'..=b'9') = reader.request_byte_at_offset(offset + 1) {
value = I::zero() - (I::from_u8(digit - b'0').unwrap());
offset += 2;
while let Some(digit @ b'0'..=b'9') = reader.request_byte_at_offset(offset) {
offset += 1;
let (new_value, overflowed) = value.overflowing_mul(&I::from_u8(10).unwrap());
overflow |= overflowed;
value = new_value;
let (new_value, overflowed) =
value.overflowing_sub(&I::from_u8(digit - b'0').unwrap());
overflow |= overflowed;
value = new_value;
}
}
} else {
while let Some(digit @ b'0'..=b'9') = reader.request_byte_at_offset(offset) {
offset += 1;
let (new_value, overflowed) = value.overflowing_mul(&I::from_u8(10).unwrap());
overflow |= overflowed;
value = new_value;
let (new_value, overflowed) = value.overflowing_add(&I::from_u8(digit - b'0').unwrap());
overflow |= overflowed;
value = new_value;
}
}
((!overflow).then(|| value), offset)
}
#[inline]
pub fn ascii_digits_multi<I>(reader: &mut ByteReader, offset: usize) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingAdd + OverflowingMul,
{
#![allow(clippy::or_fun_call)]
if reader.buf_len() < offset + 8 {
return ascii_digits_multi_cold(reader, offset);
}
let word = unsafe { u64::from_le_bytes(*(reader.buf_ptr().add(offset) as *const [u8; 8])) };
let (value, matching_digits) = swar_ascii_digits_u64_le(word);
let value = I::from_u32(value);
if matching_digits == 8 {
return ascii_digits_cont_pos(reader, offset + 8, value);
}
(value, offset + matching_digits)
}
#[cold]
#[inline(never)]
fn ascii_digits_multi_cold<I>(reader: &mut ByteReader, offset: usize) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingAdd + OverflowingMul,
{
ascii_digits(reader, offset)
}
#[inline]
pub fn signed_ascii_digits_multi<I>(reader: &mut ByteReader, offset: usize) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingAdd + OverflowingSub + OverflowingMul,
{
#![allow(clippy::or_fun_call)]
if reader.buf_len() < offset + 8 {
return signed_ascii_digits_multi_cold(reader, offset);
}
let word = unsafe { u64::from_le_bytes(*(reader.buf_ptr().add(offset) as *const [u8; 8])) };
if word & 0xff == b'-' as u64 {
let word = word >> 8;
let (value, matching_digits) = swar_ascii_digits_u64_le(word);
let value = I::from_i32(-(value as i32));
if matching_digits == 7 {
return ascii_digits_cont_neg(reader, offset + 8, value);
}
(
value,
offset + ((matching_digits != 0) as usize) + matching_digits,
)
} else {
let (value, matching_digits) = swar_ascii_digits_u64_le(word);
let value = I::from_u32(value);
if matching_digits == 8 {
return ascii_digits_cont_pos(reader, offset + 8, value);
}
(value, offset + matching_digits)
}
}
#[cold]
#[inline(never)]
fn signed_ascii_digits_multi_cold<I>(reader: &mut ByteReader, offset: usize) -> (Option<I>, usize)
where
I: Zero + FromPrimitive + OverflowingAdd + OverflowingSub + OverflowingMul,
{
signed_ascii_digits(reader, offset)
}
#[inline]
fn swar_ascii_digits_u64_le(word: u64) -> (u32, usize) {
let high_nibble_matches = word ^ 0x3030303030303030;
let low_nibbles = word & 0x0f0f0f0f0f0f0f0f;
let low_nibble_matches = low_nibbles.wrapping_add(0x0606060606060606);
let matches = (high_nibble_matches | low_nibble_matches) & 0xf0f0f0f0f0f0f0f0;
let shift = matches.trailing_zeros() & !7;
if shift == 0 {
return (0, 0);
}
let partial = (low_nibbles << (64 - shift)).wrapping_mul(2561) >> 8;
let partial = (partial & 0x00ff00ff00ff00ff).wrapping_mul(6553601) >> 16;
let value = (partial & 0x0000ffff0000ffff).wrapping_mul(42949672960001) >> 32;
(value as u32, (shift / 8) as usize)
}
#[inline]
pub fn tabs_or_spaces(input: &mut ByteReader, mut offset: usize) -> usize {
while let Some(b' ') | Some(b'\t') = input.request_byte_at_offset(offset) {
offset += 1;
}
offset
}
#[inline]
pub fn newline(input: &mut ByteReader, offset: usize) -> usize {
match input.request_byte_at_offset(offset) {
Some(b'\n') => offset + 1,
Some(b'\r') if matches!(input.request_byte_at_offset(offset + 1), Some(b'\n')) => {
offset + 2
}
_ => offset,
}
}
#[inline]
pub fn next_newline(input: &mut ByteReader, mut offset: usize) -> usize {
while !matches!(input.request_byte_at_offset(offset), Some(b'\n') | None) {
offset += 1;
}
offset + input.request_byte_at_offset(offset).is_some() as usize
}
#[inline]
pub fn fixed(input: &mut ByteReader, offset: usize, fixed: &[u8]) -> usize {
for (i, &byte) in fixed.iter().enumerate() {
if input.request_byte_at_offset(offset + i) != Some(byte) {
return offset;
}
}
offset + fixed.len()
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct LineColumn {
pub line: usize,
pub column: usize,
}
impl fmt::Display for LineColumn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}
#[derive(Debug)]
pub struct SyntaxError {
pub location: LineColumn,
pub msg: String,
}
impl fmt::Display for SyntaxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.location, self.msg)
}
}
impl std::error::Error for SyntaxError {}
pub struct LineReader<'a> {
pub reader: ByteReader<'a>,
pub line: usize,
pub line_start: usize,
}
impl<'a> LineReader<'a> {
pub fn new(reader: ByteReader<'a>) -> Self {
Self {
line: 1,
line_start: reader.position(),
reader,
}
}
#[inline]
pub fn reader(&mut self) -> &mut ByteReader<'a> {
&mut self.reader
}
#[inline]
pub fn line_at_offset(&mut self, offset: usize) {
self.line += 1;
self.line_start = self.reader.position() + offset;
}
#[inline]
pub fn give_up<E>(&mut self, msg: impl Into<String>) -> E
where
E: From<io::Error> + From<SyntaxError>,
{
self.give_up_at_cold(self.reader.position(), msg.into())
}
#[inline]
pub fn give_up_at<E>(&mut self, position: usize, msg: impl Into<String>) -> E
where
E: From<io::Error> + From<SyntaxError>,
{
self.give_up_at_cold(position, msg.into())
}
#[cold]
#[inline(never)]
fn give_up_at_cold<E>(&mut self, position: usize, msg: String) -> E
where
E: From<io::Error> + From<SyntaxError>,
{
if let Err(err) = self.reader.check_io_error() {
return err.into();
}
(SyntaxError {
location: LineColumn {
line: self.line,
column: position - self.line_start + 1,
},
msg,
})
.into()
}
}