use super::{
lexer::{read_id, read_string, read_token},
LexError, LexemeId, LexerError, Token,
};
use crate::buffer::{BufferError, BufferWindow, BufferWindowBuilder};
use std::{fmt, io::Read};
#[derive(Debug)]
pub struct TokenReader<R> {
reader: R,
buf: BufferWindow,
}
impl TokenReader<()> {
#[inline]
pub fn from_slice(data: &[u8]) -> TokenReader<&'_ [u8]> {
TokenReader {
reader: data,
buf: BufferWindow::from_slice(data),
}
}
}
impl<R> TokenReader<R>
where
R: Read,
{
#[inline]
pub fn new(reader: R) -> Self {
TokenReader::builder().build(reader)
}
#[inline]
pub fn position(&self) -> usize {
self.buf.position()
}
#[inline]
pub fn read_bytes(&mut self, bytes: usize) -> Result<&[u8], ReaderError> {
while self.buf.window_len() < bytes {
match self.buf.fill_buf(&mut self.reader) {
Ok(0) => return Err(self.lex_error(LexError::Eof)),
Ok(_) => {}
Err(e) => return Err(self.buffer_error(e)),
}
}
let input = unsafe { std::slice::from_raw_parts(self.buf.start, bytes) };
self.buf.advance(bytes);
Ok(input)
}
#[inline]
pub fn skip_container(&mut self) -> Result<(), ReaderError> {
let mut depth = 1;
loop {
let mut window =
unsafe { std::slice::from_raw_parts(self.buf.start, self.buf.window_len()) };
while let Ok((id, data)) = read_id(window) {
match id {
LexemeId::CLOSE => {
depth -= 1;
if depth == 0 {
self.buf.advance_to(data.as_ptr());
return Ok(());
}
window = data;
}
LexemeId::OPEN => {
window = data;
depth += 1
}
LexemeId::BOOL => match data.get(1..) {
Some(d) => window = d,
None => break,
},
LexemeId::F32 | LexemeId::U32 | LexemeId::I32 => match data.get(4..) {
Some(d) => window = d,
None => break,
},
LexemeId::F64 | LexemeId::I64 | LexemeId::U64 => match data.get(8..) {
Some(d) => window = d,
None => break,
},
LexemeId::QUOTED | LexemeId::UNQUOTED => match read_string(data) {
Ok((_, d)) => window = d,
Err(_) => break,
},
_ => window = data,
}
}
self.buf.advance_to(window.as_ptr());
match self.buf.fill_buf(&mut self.reader) {
Ok(0) => return Err(self.lex_error(LexError::Eof)),
Ok(_) => {}
Err(e) => return Err(self.buffer_error(e)),
}
}
}
#[inline]
pub fn into_parts(self) -> (Box<[u8]>, R) {
(self.buf.buf, self.reader)
}
#[inline]
pub fn read(&mut self) -> Result<Token, ReaderError> {
let s = std::ptr::addr_of!(self);
self.next()?
.ok_or_else(|| unsafe { s.read().lex_error(LexError::Eof) })
}
fn refill_next(&mut self) -> Result<Option<Token>, ReaderError> {
match self.buf.fill_buf(&mut self.reader) {
Ok(0) if self.buf.window_len() == 0 => Ok(None),
Ok(0) => Err(self.lex_error(LexError::Eof)),
Ok(_) => self.next(),
Err(e) => Err(self.buffer_error(e)),
}
}
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<Token>, ReaderError> {
let window = unsafe { std::slice::from_raw_parts(self.buf.start, self.buf.window_len()) };
match read_token(window) {
Ok((tok, new_data)) => {
self.buf.advance_to(new_data.as_ptr());
Ok(Some(tok))
}
Err(LexError::Eof) => self.refill_next(),
Err(e) => Err(self.lex_error(e)),
}
}
#[cold]
#[inline(never)]
fn buffer_error(&self, e: BufferError) -> ReaderError {
ReaderError {
position: self.position(),
kind: ReaderErrorKind::from(e),
}
}
#[cold]
#[inline(never)]
fn lex_error(&self, e: LexError) -> ReaderError {
ReaderError::from(e.at(self.position()))
}
}
impl TokenReader<()> {
pub fn builder() -> TokenReaderBuilder {
TokenReaderBuilder::default()
}
}
#[derive(Debug, Default)]
pub struct TokenReaderBuilder {
buffer: BufferWindowBuilder,
}
impl TokenReaderBuilder {
#[inline]
pub fn buffer(mut self, val: Box<[u8]>) -> TokenReaderBuilder {
self.buffer = self.buffer.buffer(val);
self
}
#[inline]
pub fn buffer_len(mut self, val: usize) -> TokenReaderBuilder {
self.buffer = self.buffer.buffer_len(val);
self
}
#[inline]
pub fn build<R>(self, reader: R) -> TokenReader<R> {
let buf = self.buffer.build();
TokenReader { reader, buf }
}
}
#[derive(Debug)]
pub enum ReaderErrorKind {
Read(std::io::Error),
BufferFull,
Lexer(LexError),
}
#[derive(Debug)]
pub struct ReaderError {
position: usize,
kind: ReaderErrorKind,
}
impl ReaderError {
pub fn position(&self) -> usize {
self.position
}
pub fn kind(&self) -> &ReaderErrorKind {
&self.kind
}
#[must_use]
pub fn into_kind(self) -> ReaderErrorKind {
self.kind
}
}
impl std::error::Error for ReaderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.kind {
ReaderErrorKind::Read(cause) => Some(cause),
_ => None,
}
}
}
impl std::fmt::Display for ReaderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind {
ReaderErrorKind::Read { .. } => {
write!(f, "failed to read past position: {}", self.position)
}
ReaderErrorKind::BufferFull => {
write!(f, "max buffer size exceeded at position: {}", self.position)
}
ReaderErrorKind::Lexer(cause) => {
write!(f, "{} at position: {}", cause, self.position)
}
}
}
}
impl From<LexerError> for ReaderError {
fn from(value: LexerError) -> Self {
ReaderError {
position: value.position(),
kind: ReaderErrorKind::Lexer(value.into_kind()),
}
}
}
impl From<BufferError> for ReaderErrorKind {
fn from(value: BufferError) -> Self {
match value {
BufferError::Io(x) => ReaderErrorKind::Read(x),
BufferError::BufferFull => ReaderErrorKind::BufferFull,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{binary::Rgb, Scalar};
use rstest::*;
#[rstest]
#[case(&[
Token::Id(0x2838),
Token::Equal,
Token::Open,
Token::Id(0x2863),
Token::Equal,
Token::Unquoted(Scalar::new(b"western")),
Token::Quoted(Scalar::new(b"1446.5.31")),
Token::Equal,
Token::Id(0x2838),
Token::Close,
])]
#[case(&[
Token::Id(0x2ec9),
Token::Equal,
Token::Open,
Token::Id(0x28e2),
Token::Equal,
Token::I32(1),
Token::Id(0x28e3),
Token::Equal,
Token::I32(11),
Token::Id(0x2ec7),
Token::Equal,
Token::I32(4),
Token::Id(0x2ec8),
Token::Equal,
Token::I32(0),
Token::Close,
])]
#[case(&[
Token::Id(0x053a),
Token::Equal,
Token::Rgb(Rgb {
r: 110,
g: 28,
b: 27,
a: None
})
])]
#[case(&[
Token::Id(0x053a),
Token::Equal,
Token::Rgb(Rgb {
r: 110,
g: 28,
b: 27,
a: Some(128),
})
])]
#[case(&[
Token::Id(0x326b), Token::Equal, Token::U64(128),
Token::Id(0x326b), Token::Equal, Token::I64(-1),
Token::Id(0x2d82), Token::Equal, Token::F64([0xc7, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
Token::Id(0x2d82), Token::Equal, Token::F32([0x8f, 0xc2, 0x75, 0x3e]),
Token::Id(0x2d82), Token::Equal, Token::U32(89)
])]
fn test_roundtrip(#[case] input: &[Token]) {
let data = Vec::new();
let mut writer = std::io::Cursor::new(data);
for tok in input {
tok.write(&mut writer).unwrap();
}
let data = writer.into_inner();
let mut reader = TokenReader::new(data.as_slice());
for (i, e) in input.iter().enumerate() {
assert_eq!(*e, reader.read().unwrap(), "failure at token idx: {}", i);
}
reader.read().unwrap_err();
assert_eq!(reader.position(), data.len());
let mut reader = TokenReader::from_slice(data.as_slice());
for (i, e) in input.iter().enumerate() {
assert_eq!(*e, reader.read().unwrap(), "failure at token idx: {}", i);
}
reader.read().unwrap_err();
assert_eq!(reader.position(), data.len());
for i in 30..40 {
let mut reader = TokenReader::builder().buffer_len(i).build(data.as_slice());
for e in input {
assert_eq!(*e, reader.read().unwrap(), "failure at token idx: {}", i);
}
reader.read().unwrap_err();
assert_eq!(reader.position(), data.len());
}
}
#[test]
fn test_not_enough_data() {
let mut reader = TokenReader::new(&[0x43][..]);
assert!(matches!(
reader.read().unwrap_err().kind(),
&ReaderErrorKind::Lexer(LexError::Eof)
));
}
}