use std::cmp::min;
use std::convert::Infallible;
use std::fs::File;
use std::io::{self, Read};
pub trait Reader {
type Error: std::error::Error;
fn read_byte(&mut self) -> Result<Option<u8>, Self::Error>;
fn try_read_string(&mut self, s: &[u8], case_sensitive: bool) -> Result<bool, Self::Error>;
fn read_until<'b>(
&'b mut self,
needle: &[u8],
char_buf: &'b mut [u8; 4],
) -> Result<Option<&'b [u8]>, Self::Error> {
let _ = needle;
match self.read_byte()? {
Some(x) => {
char_buf[0] = x;
Ok(Some(&char_buf[..1]))
}
None => Ok(None),
}
}
}
pub trait Readable<'a> {
type Reader: Reader + 'a;
fn to_reader(self) -> Self::Reader;
}
impl<'a, R: 'a + Reader> Readable<'a> for R {
type Reader = Self;
fn to_reader(self) -> Self::Reader {
self
}
}
#[derive(Debug)]
pub struct StringReader<'a> {
input: &'a [u8],
}
impl<'a> StringReader<'a> {
fn new(input: &'a [u8]) -> Self {
StringReader { input }
}
}
impl<'a> Reader for StringReader<'a> {
type Error = Infallible;
fn read_byte(&mut self) -> Result<Option<u8>, Self::Error> {
if self.input.is_empty() {
Ok(None)
} else {
let rv = self.input[0];
self.input = &self.input[1..];
Ok(Some(rv))
}
}
fn read_until<'b>(
&'b mut self,
needle: &[u8],
_: &'b mut [u8; 4],
) -> Result<Option<&'b [u8]>, Self::Error> {
if self.input.is_empty() {
return Ok(None);
}
if let Some(needle_pos) = fast_find(needle, self.input) {
if needle_pos == 0 {
let (rv, new_input) = self.input.split_at(1);
self.input = new_input;
Ok(Some(rv))
} else {
let (rv, new_input) = self.input.split_at(needle_pos);
self.input = new_input;
Ok(Some(rv))
}
} else {
let rv = self.input;
self.input = b"";
Ok(Some(rv))
}
}
fn try_read_string(&mut self, s1: &[u8], case_sensitive: bool) -> Result<bool, Self::Error> {
if let Some(s2) = self.input.get(..s1.len()) {
if s1 == s2 || (!case_sensitive && s1.eq_ignore_ascii_case(s2)) {
self.input = &self.input[s1.len()..];
return Ok(true);
}
}
Ok(false)
}
}
impl<'a> Readable<'a> for &'a str {
type Reader = StringReader<'a>;
fn to_reader(self) -> Self::Reader {
StringReader::new(self.as_bytes())
}
}
impl<'a> Readable<'a> for &'a String {
type Reader = StringReader<'a>;
fn to_reader(self) -> Self::Reader {
StringReader::new(self.as_bytes())
}
}
impl<'a> Readable<'a> for &'a Vec<u8> {
type Reader = StringReader<'a>;
fn to_reader(self) -> Self::Reader {
StringReader::new(self.as_slice())
}
}
impl<'a> Readable<'a> for &'a [u8] {
type Reader = StringReader<'a>;
fn to_reader(self) -> Self::Reader {
StringReader::new(self)
}
}
#[derive(Debug)]
pub struct IoReader<R: Read> {
buf: Box<[u8; BUF_SIZE]>,
buf_offset: usize,
buf_len: usize,
reader: R,
}
const BUF_SIZE: usize = 16 * 1024;
impl<R: Read> IoReader<R> {
pub fn new(reader: R) -> Self {
IoReader {
buf: Box::new([0; BUF_SIZE]),
buf_offset: 0,
buf_len: 0,
reader,
}
}
fn prepare_buf(&mut self, min_len: usize) -> Result<(), io::Error> {
let mut len = self.buf_len - self.buf_offset;
debug_assert!(min_len < self.buf.len());
debug_assert!(len < self.buf.len());
if len < min_len {
let mut raw_buf = &mut self.buf[..];
raw_buf.copy_within(self.buf_offset..self.buf_len, 0);
raw_buf = &mut raw_buf[len..];
while len < min_len {
let n = self.reader.read(raw_buf)?;
if n == 0 {
break;
}
len += n;
raw_buf = &mut raw_buf[n..];
}
self.buf_len = len;
self.buf_offset = 0;
}
Ok(())
}
}
impl<R: Read> Reader for IoReader<R> {
type Error = io::Error;
fn read_byte(&mut self) -> Result<Option<u8>, Self::Error> {
self.prepare_buf(1)?;
if self.buf_offset == self.buf_len {
return Ok(None);
}
let rv = self.buf.get(self.buf_offset).copied();
if rv.is_some() {
self.buf_offset += 1;
}
Ok(rv)
}
fn try_read_string(&mut self, s1: &[u8], case_sensitive: bool) -> Result<bool, Self::Error> {
debug_assert!(!s1.contains(&b'\r'));
debug_assert!(!s1.contains(&b'\n'));
self.prepare_buf(s1.len())?;
let s2 = &self.buf[self.buf_offset..min(self.buf_offset + s1.len(), self.buf_len)];
if s1 == s2 || (!case_sensitive && s1.eq_ignore_ascii_case(s2)) {
self.buf_offset += s1.len();
Ok(true)
} else {
Ok(false)
}
}
fn read_until<'b>(
&'b mut self,
needle: &[u8],
_: &'b mut [u8; 4],
) -> Result<Option<&'b [u8]>, Self::Error> {
self.prepare_buf(4)?;
let buf = &self.buf[self.buf_offset..self.buf_len];
if buf.is_empty() {
Ok(None)
} else if let Some(needle_pos) = fast_find(needle, buf) {
if needle_pos == 0 {
self.buf_offset += 1;
Ok(Some(&buf[..1]))
} else {
self.buf_offset += needle_pos;
Ok(Some(&buf[..needle_pos]))
}
} else {
self.buf_offset += buf.len();
Ok(Some(buf))
}
}
}
impl<'a> Readable<'a> for File {
type Reader = IoReader<File>;
fn to_reader(self) -> Self::Reader {
IoReader::new(self)
}
}
#[inline]
fn fast_find(needle: &[u8], haystack: &[u8]) -> Option<usize> {
#[cfg(feature = "jetscii")]
{
debug_assert!(needle.len() <= 16);
let mut needle_arr = [0; 16];
needle_arr[..needle.len()].copy_from_slice(needle);
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
jetscii::Bytes::new(needle_arr, needle.len() as i32, |b| needle.contains(&b)).find(haystack)
}
#[cfg(not(feature = "jetscii"))]
haystack.iter().position(|b| needle.contains(b))
}