use std::io;
use crate::error::{Error, ErrorCode, PResult, StreamError};
pub(crate) const DEFAULT_BUFFER: usize = 64 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Split {
Item { start: usize, end: usize },
Need,
End,
}
pub(crate) trait Framer {
fn next(&mut self, buf: &[u8], eof: bool) -> PResult<Split>;
fn consumed(&self) -> usize;
fn rebase(&mut self);
fn position(&self) -> usize;
}
pub(crate) struct Window<F> {
buf: Vec<u8>,
split: F,
eof: bool,
limit: usize,
origin: usize,
failed: bool,
inited: usize,
}
impl<F: Framer> Window<F> {
pub(crate) fn new(split: F, capacity: usize) -> Self {
Window {
buf: Vec::with_capacity(capacity),
split,
eof: false,
limit: usize::MAX,
origin: 0,
failed: false,
inited: 0,
}
}
pub(crate) fn set_limit(&mut self, bytes: usize) {
self.limit = bytes;
}
pub(crate) fn framer(&self) -> &F {
&self.split
}
pub(crate) fn framer_mut(&mut self) -> &mut F {
&mut self.split
}
pub(crate) fn bytes(&self) -> &[u8] {
&self.buf
}
pub(crate) fn buffered(&self) -> usize {
if self.failed {
return 0;
}
self.buf.len() - self.split.consumed()
}
pub(crate) fn into_unread(self) -> Vec<u8> {
if self.failed {
return Vec::new();
}
let mut buf = self.buf;
buf.drain(..self.split.consumed());
buf
}
pub(crate) fn offset(&self) -> usize {
self.origin + self.split.consumed()
}
pub(crate) fn is_eof(&self) -> bool {
self.eof
}
pub(crate) fn set_eof(&mut self) {
self.eof = true;
}
pub(crate) fn extend(&mut self, bytes: &[u8]) {
if self.failed {
return;
}
self.buf.extend_from_slice(bytes);
self.inited = self.buf.len();
}
fn compact(&mut self) {
let dead = self.split.consumed();
if dead == 0 || dead < self.buf.len() - dead {
return;
}
self.buf.copy_within(dead.., 0);
self.buf.truncate(self.buf.len() - dead);
self.split.rebase();
self.origin += dead;
}
pub(crate) fn try_next(&mut self) -> Result<Split, StreamError> {
if self.failed {
return Ok(Split::End);
}
match self.locate() {
Ok(split) => Ok(split),
Err(e) => {
self.failed = true;
self.buf = Vec::new();
self.inited = 0;
Err(e)
}
}
}
fn locate(&mut self) -> Result<Split, StreamError> {
self.compact();
let split = self
.split
.next(&self.buf, self.eof)
.map_err(|code| self.error_at(code, self.split.position(), None))?;
let (extent, at) = match split {
Split::Need => (self.buffered(), self.split.consumed()),
Split::Item { start, end } => (end - start, start),
Split::End => (0, self.split.consumed()),
};
if extent > self.limit {
return Err(self.error_at(ErrorCode::DocumentTooLarge, at, None));
}
Ok(split)
}
pub(crate) fn fill<R: io::Read>(&mut self, reader: &mut R, chunk: usize) -> io::Result<()> {
let len = self.buf.len();
let room = self.limit.saturating_sub(self.buffered()).saturating_add(1);
let want = len + chunk.clamp(1, room);
if want > self.inited {
self.buf.resize(want, 0);
self.inited = want;
} else {
unsafe { self.buf.set_len(want) };
}
let read = reader.read(&mut self.buf[len..]);
match read {
Ok(0) => {
self.buf.truncate(len);
self.eof = true;
Ok(())
}
Ok(n) => {
self.buf.truncate(len + n);
Ok(())
}
Err(e) => {
self.buf.truncate(len);
if e.kind() == io::ErrorKind::Interrupted {
Ok(())
} else {
Err(e)
}
}
}
}
pub(crate) fn error_at(
&self,
code: ErrorCode,
at: usize,
key: Option<&'static str>,
) -> StreamError {
StreamError::Parse(Error::with_key(code, self.origin + at, key))
}
}