use std::io::{self, Read};
use std::marker::PhantomData;
use std::ptr;
use crate::{
Error,
binary::{LexemeId, Rgb},
};
const DEFAULT_CAPACITY: usize = 32 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParserError {
BufferTooSmall,
Io(io::ErrorKind),
}
impl ParserError {
#[inline]
pub fn eof() -> Self {
ParserError::Io(io::ErrorKind::UnexpectedEof)
}
#[inline]
pub fn is_eof(&self) -> bool {
matches!(self, ParserError::Io(io::ErrorKind::UnexpectedEof))
}
}
impl std::fmt::Display for ParserError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParserError::BufferTooSmall => write!(f, "requested read exceeds buffer capacity"),
ParserError::Io(kind) => write!(f, "io error: {}", kind),
}
}
}
impl std::error::Error for ParserError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl From<io::Error> for ParserError {
fn from(value: io::Error) -> Self {
ParserError::Io(value.kind())
}
}
enum Backing<'a> {
Stream {
buffer: Vec<u8>,
source: Box<dyn Read + 'a>,
},
Owned {
#[expect(dead_code)] owner: Box<dyn AsRef<[u8]> + 'a>,
},
}
pub struct ParserSource<'a> {
ptr: *const u8,
end: *const u8,
base_ptr: *const u8, total_bytes_parsed: usize,
streaming: Option<Box<Backing<'a>>>,
_marker: PhantomData<&'a [u8]>,
}
impl std::fmt::Debug for ParserSource<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParserSource")
.field("remaining", &self.window_len())
.field("position", &self.position())
.field("streaming", &self.streaming.is_some())
.finish()
}
}
impl<'a> ParserSource<'a> {
pub fn from_slice(data: &'a [u8]) -> Self {
let start = data.as_ptr();
Self {
ptr: start,
end: unsafe { start.add(data.len()) },
base_ptr: start,
total_bytes_parsed: 0,
streaming: None,
_marker: PhantomData,
}
}
pub fn from_owned<T: AsRef<[u8]> + 'a>(owner: T) -> Self {
let boxed: Box<dyn AsRef<[u8]> + 'a> = Box::new(owner);
let slice = (*boxed).as_ref();
let start = slice.as_ptr();
let len = slice.len();
Self {
ptr: start,
end: unsafe { start.add(len) },
base_ptr: start,
total_bytes_parsed: 0,
streaming: Some(Box::new(Backing::Owned { owner: boxed })),
_marker: PhantomData,
}
}
pub fn from_reader<R: Read + 'a>(source: R) -> Self {
Self::from_reader_with_buf(source, vec![0u8; DEFAULT_CAPACITY])
}
pub fn from_reader_with_buf<R>(source: R, buffer: Vec<u8>) -> Self
where
R: Read + 'a,
{
let vec = buffer;
let start = vec.as_ptr();
Self {
ptr: start,
end: start, base_ptr: start,
total_bytes_parsed: 0,
streaming: Some(Box::new(Backing::Stream {
buffer: vec,
source: Box::new(source),
})),
_marker: PhantomData,
}
}
#[inline(always)]
pub fn ensure_bytes(&mut self, required: usize) -> Result<(), ParserError> {
let available = unsafe { self.end.offset_from_unsigned(self.ptr) };
if available >= required {
return Ok(());
}
self.refill_slow(required)
}
#[inline(always)]
pub fn position(&self) -> usize {
let consumed_in_current_window = unsafe { self.ptr.offset_from_unsigned(self.base_ptr) };
self.total_bytes_parsed + consumed_in_current_window
}
#[inline(always)]
pub unsafe fn advance_unchecked(&mut self, bytes: usize) {
self.ptr = unsafe { self.ptr.add(bytes) };
}
#[inline(always)]
pub fn advance(&mut self, bytes: usize) -> bool {
if bytes <= self.window_len() {
unsafe {
self.advance_unchecked(bytes);
}
true
} else {
false
}
}
#[inline(always)]
pub fn window_len(&self) -> usize {
unsafe { self.end.offset_from_unsigned(self.ptr) }
}
#[inline(always)]
pub fn window(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr, self.window_len()) }
}
#[inline(always)]
pub unsafe fn get_window_unchecked(&self, len: usize) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr, len) }
}
#[inline]
pub fn refill(&mut self) -> Result<usize, ParserError> {
let (buffer, source) = match self.streaming.as_deref_mut() {
Some(Backing::Owned { .. }) | None => return Ok(0),
Some(Backing::Stream { buffer, source }) => (buffer, source),
};
let unparsed_len = unsafe { self.end.offset_from_unsigned(self.ptr) };
if unparsed_len >= buffer.len() {
return Err(ParserError::BufferTooSmall);
}
let consumed_in_window = unsafe { self.ptr.offset_from_unsigned(self.base_ptr) };
self.total_bytes_parsed += consumed_in_window;
let internal_buffer_start = buffer.as_mut_ptr();
if unparsed_len > 0 {
unsafe {
ptr::copy(self.ptr, internal_buffer_start, unparsed_len);
}
}
self.ptr = internal_buffer_start;
self.end = unsafe { internal_buffer_start.add(unparsed_len) };
let bytes_written = source.read(&mut buffer[unparsed_len..])?;
self.end = unsafe { self.end.add(bytes_written) };
Ok(bytes_written)
}
#[inline(always)]
pub fn peek<const N: usize>(&mut self) -> Result<Option<&[u8; N]>, ParserError> {
if self.window_len() >= N {
unsafe { return Ok(Some(&*self.ptr.cast::<[u8; N]>())) };
}
self.peek_slow::<N>()
}
#[inline(never)]
fn peek_slow<const N: usize>(&mut self) -> Result<Option<&[u8; N]>, ParserError> {
match self.ensure_bytes(N) {
Ok(()) => unsafe { Ok(Some(&*self.ptr.cast::<[u8; N]>())) },
Err(ParserError::Io(io::ErrorKind::UnexpectedEof)) if self.window_len() == 0 => {
Ok(None)
}
Err(e) => Err(e),
}
}
#[inline(always)]
pub fn take<const N: usize>(&mut self) -> Result<&[u8; N], ParserError> {
self.ensure_bytes(N)?;
unsafe {
let array_ref = &*self.ptr.cast::<[u8; N]>();
self.advance_unchecked(N);
Ok(array_ref)
}
}
#[inline(always)]
pub fn try_take<const N: usize>(&mut self) -> Result<Option<&[u8; N]>, ParserError> {
if self.window_len() >= N {
unsafe {
let array_ref = &*self.ptr.cast::<[u8; N]>();
self.advance_unchecked(N);
return Ok(Some(array_ref));
}
}
self.try_take_slow::<N>()
}
#[inline(never)]
fn try_take_slow<const N: usize>(&mut self) -> Result<Option<&[u8; N]>, ParserError> {
match self.ensure_bytes(N) {
Ok(()) => unsafe {
let array_ref = &*self.ptr.cast::<[u8; N]>();
self.advance_unchecked(N);
Ok(Some(array_ref))
},
Err(ParserError::Io(io::ErrorKind::UnexpectedEof)) if self.window_len() == 0 => {
Ok(None)
}
Err(e) => Err(e),
}
}
#[inline(always)]
pub fn take_bytes(&mut self, n: usize) -> Result<&[u8], ParserError> {
self.ensure_bytes(n)?;
unsafe {
let slice = std::slice::from_raw_parts(self.ptr, n);
self.advance_unchecked(n);
Ok(slice)
}
}
#[inline(always)]
pub fn try_take_bytes(&mut self, n: usize) -> Result<Option<&[u8]>, ParserError> {
if self.window_len() >= n {
unsafe {
let slice = std::slice::from_raw_parts(self.ptr, n);
self.advance_unchecked(n);
return Ok(Some(slice));
}
}
self.try_take_bytes_slow(n)
}
#[inline(never)]
fn try_take_bytes_slow(&mut self, n: usize) -> Result<Option<&[u8]>, ParserError> {
match self.ensure_bytes(n) {
Ok(()) => unsafe {
let slice = std::slice::from_raw_parts(self.ptr, n);
self.advance_unchecked(n);
Ok(Some(slice))
},
Err(ParserError::Io(io::ErrorKind::UnexpectedEof)) if self.window_len() == 0 => {
Ok(None)
}
Err(e) => Err(e),
}
}
#[inline(never)]
fn refill_slow(&mut self, required: usize) -> Result<(), ParserError> {
let (buffer, source) = match self.streaming.as_deref_mut() {
Some(Backing::Owned { .. }) | None => return Err(ParserError::eof()),
Some(Backing::Stream { buffer, source }) => (buffer, source),
};
if required > buffer.len() {
return Err(ParserError::BufferTooSmall);
}
let consumed_in_window = unsafe { self.ptr.offset_from_unsigned(self.base_ptr) };
self.total_bytes_parsed += consumed_in_window;
let unparsed_len = unsafe { self.end.offset_from_unsigned(self.ptr) };
let internal_buffer_start = buffer.as_mut_ptr();
if unparsed_len > 0 {
unsafe {
ptr::copy(self.ptr, internal_buffer_start, unparsed_len);
}
}
self.ptr = internal_buffer_start;
self.end = unsafe { internal_buffer_start.add(unparsed_len) };
loop {
let current_buffer_len =
unsafe { self.end.offset_from_unsigned(internal_buffer_start) };
if current_buffer_len >= buffer.len() {
return Ok(());
}
let target_slice = &mut buffer[current_buffer_len..];
let bytes_written = source.read(target_slice)?;
if bytes_written == 0 {
let available = unsafe { self.end.offset_from_unsigned(self.ptr) };
if available >= required {
return Ok(());
}
return Err(ParserError::eof());
}
self.end = unsafe { self.end.add(bytes_written) };
}
}
}
pub trait BinarySourceExt {
fn read_bstr(&mut self) -> Result<&[u8], ParserError>;
fn read_lexeme_id(&mut self) -> Result<LexemeId, ParserError>;
fn peek_lexeme_id(&mut self) -> Result<Option<LexemeId>, ParserError>;
fn read_rgb(&mut self) -> Result<Rgb, Error>;
}
impl BinarySourceExt for ParserSource<'_> {
#[inline]
fn read_bstr(&mut self) -> Result<&[u8], ParserError> {
self.ensure_bytes(2)?;
let len = u16::from_le_bytes(unsafe { *self.ptr.cast::<[u8; 2]>() }) as usize;
self.ensure_bytes(2 + len)?;
unsafe {
self.advance_unchecked(2);
let data = std::slice::from_raw_parts(self.ptr, len);
self.advance_unchecked(len);
Ok(data)
}
}
#[inline]
fn read_lexeme_id(&mut self) -> Result<LexemeId, ParserError> {
Ok(LexemeId::new(u16::from_le_bytes(*self.take::<2>()?)))
}
#[inline]
fn peek_lexeme_id(&mut self) -> Result<Option<LexemeId>, ParserError> {
Ok(self
.peek::<2>()?
.map(|x| LexemeId::new(u16::from_le_bytes(*x))))
}
fn read_rgb(&mut self) -> Result<Rgb, Error> {
fn channel(source: &mut ParserSource<'_>) -> Result<u32, Error> {
if source.read_lexeme_id()? != LexemeId::U32 {
return Err(Error::invalid_syntax("invalid rgb", source.position()));
}
Ok(u32::from_le_bytes(*source.take::<4>()?))
}
if self.read_lexeme_id()? != LexemeId::OPEN {
return Err(Error::invalid_syntax("invalid rgb", self.position()));
}
let r = channel(self)?;
let g = channel(self)?;
let b = channel(self)?;
match self.read_lexeme_id()? {
LexemeId::CLOSE => Ok(Rgb { r, g, b, a: None }),
LexemeId::U32 => {
let a = u32::from_le_bytes(*self.take::<4>()?);
if self.read_lexeme_id()? == LexemeId::CLOSE {
Ok(Rgb {
r,
g,
b,
a: Some(a),
})
} else {
Err(Error::invalid_syntax("invalid rgb", self.position()))
}
}
_ => Err(Error::invalid_syntax("invalid rgb", self.position())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{self, Read};
struct ChunkedReader {
data: Vec<u8>,
pos: usize,
chunk: usize,
}
impl ChunkedReader {
fn new(data: Vec<u8>, chunk: usize) -> Self {
assert!(chunk > 0);
Self {
data,
pos: 0,
chunk,
}
}
}
impl Read for ChunkedReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let remaining = self.data.len() - self.pos;
let n = remaining.min(buf.len()).min(self.chunk);
buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
self.pos += n;
Ok(n)
}
}
struct FailingReader;
impl Read for FailingReader {
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::InvalidData, "read failed"))
}
}
fn parsers(
data: &[u8],
chunk: usize,
capacity: usize,
) -> (ParserSource<'_>, ParserSource<'static>) {
let slice = ParserSource::from_slice(data);
let stream = ParserSource::from_reader_with_buf(
ChunkedReader::new(data.to_vec(), chunk),
vec![0; capacity],
);
(slice, stream)
}
#[test]
fn parser_error_classifies_io_error() {
use std::error::Error as _;
assert!(std::mem::size_of::<ParserError>() <= 2);
let err = ParserError::from(io::Error::other("boom"));
assert_eq!(err, ParserError::Io(io::ErrorKind::Other));
assert_eq!(err.to_string(), "io error: other error");
assert!(err.source().is_none());
}
#[test]
fn empty_input_is_immediately_eof() {
let mut s = ParserSource::from_slice(&[]);
assert!(s.try_take::<1>().unwrap().is_none());
let mut r = ParserSource::from_reader_with_buf(ChunkedReader::new(vec![], 1), vec![0; 16]);
assert!(r.try_take::<1>().unwrap().is_none());
}
#[test]
fn try_take_returns_some_when_window_has_remaining_bytes() {
let mut parser = ParserSource::from_slice(&[1, 2, 3]);
assert_eq!(parser.try_take::<1>().unwrap(), Some(&[1]));
}
#[test]
fn slice_ensure_bytes_past_end_returns_eof() {
let mut s = ParserSource::from_slice(&[1, 2, 3]);
let err = s.ensure_bytes(4).unwrap_err();
assert_eq!(err, ParserError::Io(io::ErrorKind::UnexpectedEof));
}
#[test]
fn read_fixed_parity_byte_at_a_time() {
let data: Vec<u8> = (0..50u8).collect();
let (mut slice, mut stream) = parsers(&data, 1, 16);
for _ in 0..10 {
let a = *slice.take::<5>().unwrap();
let b = *stream.take::<5>().unwrap();
assert_eq!(a, b);
assert_eq!(slice.position(), stream.position());
}
assert!(slice.try_take::<1>().unwrap().is_none());
assert!(stream.try_take::<1>().unwrap().is_none());
}
#[test]
fn peek_does_not_advance_and_matches_read() {
let data: Vec<u8> = (0..20u8).collect();
let (mut slice, mut stream) = parsers(&data, 1, 8);
let ps = *slice.peek::<4>().unwrap().unwrap();
let pr = *stream.peek::<4>().unwrap().unwrap();
assert_eq!(ps, pr);
assert_eq!(slice.position(), 0);
assert_eq!(stream.position(), 0);
let rs = *slice.take::<4>().unwrap();
let rr = *stream.take::<4>().unwrap();
assert_eq!(ps, rs);
assert_eq!(pr, rr);
}
#[test]
fn peek_propagates_non_eof_read_errors() {
let mut parser = ParserSource::from_reader_with_buf(FailingReader, vec![0; 8]);
let err = parser.peek::<1>().unwrap_err();
assert_ne!(err, ParserError::Io(io::ErrorKind::UnexpectedEof));
assert_eq!(err, ParserError::Io(io::ErrorKind::InvalidData));
}
#[test]
fn peek_at_eof_returns_none() {
let data = [1u8, 2, 3];
let (mut slice, mut stream) = parsers(&data, 1, 8);
slice.take::<3>().unwrap();
stream.take::<3>().unwrap();
assert!(slice.peek::<4>().unwrap().is_none());
assert!(stream.peek::<4>().unwrap().is_none());
}
#[test]
fn peek_errors_on_partial_item() {
let data = [1u8, 2, 3];
let (mut slice, mut stream) = parsers(&data, 1, 8);
let slice_err = slice.peek::<4>().unwrap_err();
let stream_err = stream.peek::<4>().unwrap_err();
assert_eq!(slice_err, ParserError::Io(io::ErrorKind::UnexpectedEof));
assert_eq!(stream_err, ParserError::Io(io::ErrorKind::UnexpectedEof));
assert_eq!(slice.window(), &[1, 2, 3]);
assert_eq!(stream.window(), &[1, 2, 3]);
}
#[test]
fn try_take_returns_none_only_at_clean_eof() {
let data = [1u8, 2, 3, 4];
let (mut slice, mut stream) = parsers(&data, 1, 8);
assert_eq!(slice.try_take::<2>().unwrap(), Some(&[1, 2]));
assert_eq!(stream.try_take::<2>().unwrap(), Some(&[1, 2]));
assert_eq!(slice.try_take_bytes(2).unwrap(), Some(&[3, 4][..]));
assert_eq!(stream.try_take_bytes(2).unwrap(), Some(&[3, 4][..]));
assert!(slice.try_take::<1>().unwrap().is_none());
assert!(stream.try_take::<1>().unwrap().is_none());
}
#[test]
fn try_take_errors_on_partial_item() {
let data = [1u8, 2, 3];
let (mut slice, mut stream) = parsers(&data, 1, 8);
let slice_err = slice.try_take::<4>().unwrap_err();
let stream_err = stream.try_take::<4>().unwrap_err();
assert_eq!(slice_err, ParserError::Io(io::ErrorKind::UnexpectedEof));
assert_eq!(stream_err, ParserError::Io(io::ErrorKind::UnexpectedEof));
assert_eq!(slice.window(), &[1, 2, 3]);
assert_eq!(stream.window(), &[1, 2, 3]);
}
#[test]
fn length_prefixed_slice_parity_across_refills() {
let mut data = Vec::new();
for payload in [b"abcd".as_slice(), b"hi", b"streaming!!"] {
data.extend_from_slice(&(payload.len() as u16).to_le_bytes());
data.extend_from_slice(payload);
}
let (mut slice, mut stream) = parsers(&data, 1, 16);
for expected in [b"abcd".as_slice(), b"hi", b"streaming!!"] {
let a = slice.read_bstr().unwrap().to_vec();
let b = stream.read_bstr().unwrap().to_vec();
assert_eq!(a, expected);
assert_eq!(b, expected);
}
assert!(slice.try_take::<1>().unwrap().is_none());
assert!(stream.try_take::<1>().unwrap().is_none());
}
#[test]
fn length_prefix_preserved_when_body_unavailable() {
let mut data = Vec::new();
data.extend_from_slice(&10u16.to_le_bytes());
data.extend_from_slice(b"abcd");
let mut stream =
ParserSource::from_reader_with_buf(ChunkedReader::new(data.clone(), 1), vec![0; 32]);
let err = stream.read_bstr().unwrap_err();
assert_eq!(err, ParserError::Io(io::ErrorKind::UnexpectedEof));
let prefix = stream.peek::<2>().unwrap().unwrap();
assert_eq!(u16::from_le_bytes(*prefix), 10);
}
#[test]
fn position_is_cumulative_across_refills() {
let data: Vec<u8> = (0..100u8).collect();
let mut stream =
ParserSource::from_reader_with_buf(ChunkedReader::new(data.clone(), 1), vec![0; 8]);
let mut consumed = 0usize;
while stream.try_take::<1>().unwrap().is_some() {
consumed += 1;
assert_eq!(stream.position(), consumed);
}
assert_eq!(consumed, data.len());
}
#[test]
fn window_and_window_len_reflect_consumed_bytes() {
let data = [1u8, 2, 3, 4, 5];
let mut slice = ParserSource::from_slice(&data);
assert_eq!(slice.window_len(), 5);
assert_eq!(slice.window(), &data);
slice.take::<2>().unwrap();
assert_eq!(slice.window_len(), 3);
assert_eq!(slice.window(), &[3, 4, 5]);
}
#[test]
fn advance_within_window_moves_cursor_and_subsequent_reads_follow() {
let mut parser = ParserSource::from_slice(&[10, 20, 30, 40, 50]);
assert!(parser.advance(2));
assert_eq!(parser.window_len(), 3);
assert_eq!(parser.position(), 2);
assert_eq!(*parser.take::<2>().unwrap(), [30, 40]);
assert_eq!(parser.window(), &[50]);
}
#[test]
fn refill_preserves_unread_bytes_and_reads_once() {
let data: Vec<u8> = (0..10u8).collect();
let mut parser =
ParserSource::from_reader_with_buf(ChunkedReader::new(data, 3), vec![0; 8]);
assert_eq!(parser.refill().unwrap(), 3);
assert_eq!(parser.window(), &[0, 1, 2]);
assert!(parser.advance(2));
assert_eq!(parser.position(), 2);
assert_eq!(parser.refill().unwrap(), 3);
assert_eq!(parser.position(), 2);
assert_eq!(parser.window(), &[2, 3, 4, 5]);
}
#[test]
fn refill_returns_zero_at_stream_eof() {
let mut parser =
ParserSource::from_reader_with_buf(ChunkedReader::new(vec![1, 2], 8), vec![0; 8]);
assert_eq!(parser.refill().unwrap(), 2);
assert_eq!(parser.take::<2>().unwrap(), &[1, 2]);
assert_eq!(parser.refill().unwrap(), 0);
assert_eq!(parser.position(), 2);
assert_eq!(parser.window_len(), 0);
}
#[test]
fn refill_on_slice_and_owned_sources_returns_zero() {
let mut slice = ParserSource::from_slice(&[1, 2, 3]);
let mut owned = ParserSource::from_owned(vec![1u8, 2, 3]);
assert_eq!(slice.refill().unwrap(), 0);
assert_eq!(slice.window(), &[1, 2, 3]);
assert_eq!(owned.refill().unwrap(), 0);
assert_eq!(owned.window(), &[1, 2, 3]);
}
#[test]
fn refill_errors_when_unread_window_fills_stream_buffer() {
let mut parser =
ParserSource::from_reader_with_buf(ChunkedReader::new(vec![1, 2, 3, 4], 8), vec![0; 4]);
assert_eq!(parser.refill().unwrap(), 4);
let err = parser.refill().unwrap_err();
assert_eq!(err, ParserError::BufferTooSmall);
assert_eq!(parser.window(), &[1, 2, 3, 4]);
assert_eq!(parser.position(), 0);
}
#[test]
fn advance_past_window_returns_false_and_does_not_move() {
let mut parser = ParserSource::from_slice(&[1, 2, 3]);
assert!(!parser.advance(4));
assert_eq!(parser.window_len(), 3);
assert_eq!(parser.window(), &[1, 2, 3]);
assert_eq!(parser.position(), 0);
}
#[test]
fn advance_does_not_refill_streaming_window() {
let data: Vec<u8> = (0..10u8).collect();
let mut stream =
ParserSource::from_reader_with_buf(ChunkedReader::new(data, 1), vec![0; 8]);
assert_eq!(stream.window_len(), 0);
assert!(!stream.advance(1));
stream.ensure_bytes(4).unwrap();
let window = stream.window_len();
assert!(!stream.advance(window + 1));
assert!(stream.advance(window));
assert_eq!(stream.position(), window);
}
#[test]
fn get_slice_unchecked_returns_current_window_prefix() {
let mut parser = ParserSource::from_slice(&[1, 2, 3, 4]);
parser.ensure_bytes(3).unwrap();
let slice = unsafe { parser.get_window_unchecked(3) };
assert_eq!(slice, &[1, 2, 3]);
assert_eq!(parser.position(), 0);
}
#[test]
fn ensure_bytes_errors_when_request_exceeds_capacity() {
let mut stream =
ParserSource::from_reader_with_buf(ChunkedReader::new(vec![0u8; 32], 1), vec![0; 8]);
let err = stream.ensure_bytes(16).unwrap_err();
assert_eq!(err, ParserError::BufferTooSmall);
}
#[test]
fn from_owned_vec_parity_with_slice() {
let data: Vec<u8> = (0..50u8).collect();
let mut slice = ParserSource::from_slice(&data);
let mut owned = ParserSource::from_owned(data.clone());
for _ in 0..10 {
let a = *slice.take::<5>().unwrap();
let b = *owned.take::<5>().unwrap();
assert_eq!(a, b);
assert_eq!(slice.position(), owned.position());
}
assert!(slice.try_take::<1>().unwrap().is_none());
assert!(owned.try_take::<1>().unwrap().is_none());
}
#[test]
fn from_owned_array_is_static() {
fn requires_static(_: &ParserSource<'static>) {}
let owned = ParserSource::from_owned([1u8, 2, 3, 4]);
requires_static(&owned);
let mut owned = owned;
assert_eq!(*owned.take::<4>().unwrap(), [1, 2, 3, 4]);
assert!(owned.try_take::<1>().unwrap().is_none());
}
#[test]
fn from_owned_arc_keeps_data_alive() {
use std::sync::Arc;
let arc: Arc<[u8]> = Arc::from(vec![10u8, 20, 30, 40, 50].into_boxed_slice());
let mut owned = ParserSource::from_owned(Arc::clone(&arc));
drop(arc); assert_eq!(*owned.take::<5>().unwrap(), [10, 20, 30, 40, 50]);
assert!(owned.try_take::<1>().unwrap().is_none());
}
#[test]
fn from_owned_ensure_bytes_past_end_returns_eof_without_slide() {
let mut owned = ParserSource::from_owned(vec![1u8, 2, 3, 4, 5]);
owned.take::<2>().unwrap();
let tail_ptr_before = owned.window().as_ptr();
let err = owned.ensure_bytes(10).unwrap_err();
assert_eq!(err, ParserError::Io(io::ErrorKind::UnexpectedEof));
assert_eq!(owned.window(), &[3, 4, 5]);
assert_eq!(owned.window().as_ptr(), tail_ptr_before);
}
#[test]
fn from_owned_static_bytes() {
static DATA: &[u8] = b"hello world";
let mut owned = ParserSource::from_owned(DATA);
assert_eq!(*owned.take::<5>().unwrap(), *b"hello");
owned.take::<1>().unwrap();
assert_eq!(*owned.take::<5>().unwrap(), *b"world");
assert!(owned.try_take::<1>().unwrap().is_none());
}
}