diff --git a/mavlink-core/src/peek_reader.rs b/mavlink-core/src/peek_reader.rs index 7ed14ff73..854188176 100644 --- a/mavlink-core/src/peek_reader.rs +++ b/mavlink-core/src/peek_reader.rs @@ -1,7 +1,29 @@ +//! This module implements a buffered/peekable reader. +//! +//! The purpose of the buffered/peekable reader is to allow for backtracking parsers. +//! +//! A reader implementing the standard librairy's [`std::io::BufRead`] trait seems like a good fit, but +//! it does not allow for peeking a specific number of bytes, so it provides no way to request +//! more data from the underlying reader without consuming the existing data. +//! +//! This API still tries to adhere to the [`std::io::BufRead`]'s trait philosophy. +//! +//! The main type `PeekReader`does not implement [`std::io::Read`] itself, as there is no added benefit +//! in doing so. +//! + use std::io::{self, ErrorKind, Read}; +// The default chunk size to read from the underlying reader const DEFAULT_CHUNK_SIZE: usize = 1024; +/// A buffered/peekable reader +/// +/// This reader wraps a type implementing [`std::io::Read`] and adds buffering via an internal buffer. +/// +/// It allows the user to `peek` a specified number of bytes (without consuming them), +/// to `read` bytes (consuming them), or to `consume` them after `peek`ing. +/// pub struct PeekReader { // Internal buffer buffer: Vec, @@ -13,15 +35,17 @@ pub struct PeekReader { reader: R, // Stashed error, if any. error: Option, - /// Whether we hit EOF on the underlying reader. + // Whether we hit EOF on the underlying reader. eof: bool, } impl PeekReader { + /// Instanciates a new [`PeekReader`], wrapping the provided [`std::io::Read`]er and using the default chunk size pub fn new(reader: R) -> Self { Self::with_chunk_size(reader, DEFAULT_CHUNK_SIZE) } + /// Instanciates a new [`PeekReader`], wrapping the provided [`std::io::Read`]er and using the supplied chunk size pub fn with_chunk_size(reader: R, preferred_chunk_size: usize) -> Self { Self { buffer: Vec::with_capacity(preferred_chunk_size), @@ -33,41 +57,95 @@ impl PeekReader { } } + /// Peeks a specified amount of bytes from the internal buffer + /// + /// If the internal buffer does not contain enough data, this function will read + /// from the underlying [`std::io::Read`]er until it does, an error occurs or no more data can be read (EOF). + /// + /// This function does not consume data from the buffer, so subsequent calls to `peek` or `read` functions + /// will still return the peeked data. + /// pub fn peek(&mut self, amount: usize) -> io::Result<&[u8]> { self.fetch(amount, false, false) } + /// Peeks an exact amount of bytes from the internal buffer + /// + /// If the internal buffer does not contain enough data, this function will read + /// from the underlying [`std::io::Read`]er until it does, an error occurs or no more data can be read (EOF). + /// + /// If an EOF occurs and the specified amount could not be read, this function will return an [`ErrorKind::UnexpectedEof`]. + /// + /// This function does not consume data from the buffer, so subsequent calls to `peek` or `read` functions + /// will still return the peeked data. + /// pub fn peek_exact(&mut self, amount: usize) -> io::Result<&[u8]> { self.fetch(amount, true, false) } + /// Consumes a specified amount of bytes from the buffer + /// + /// If the internal buffer does not contain enough data, this function will consume as much data as is buffered. + /// pub fn consume(&mut self, amount: usize) -> usize { let amount = amount.min(self.buffer.len() - self.cursor); self.cursor += amount; amount } + /// Reads a specified amount of bytes from the internal buffer + /// + /// If the internal buffer does not contain enough data, this function will read + /// from the underlying [`std::io::Read`]er until it does, an error occurs or no more data can be read (EOF). + /// + /// This function consumes the data from the buffer, unless an error occurs, in which case no data is consumed. + /// pub fn read(&mut self, amount: usize) -> io::Result<&[u8]> { self.fetch(amount, false, true) } + /// Reads a specified amount of bytes from the internal buffer + /// + /// If the internal buffer does not contain enough data, this function will read + /// from the underlying [`std::io::Read`]er until it does, an error occurs or no more data can be read (EOF). + /// + /// If an EOF occurs and the specified amount could not be read, this function will return an [`ErrorKind::UnexpectedEof`]. + /// + /// This function consumes the data from the buffer, unless an error occurs, in which case no data is consumed. + /// pub fn read_exact(&mut self, amount: usize) -> io::Result<&[u8]> { self.fetch(amount, true, true) } + /// Reads a byte from the internal buffer + /// + /// If the internal buffer does not contain enough data, this function will read + /// from the underlying [`std::io::Read`]er until it does, an error occurs or no more data can be read (EOF). + /// + /// If an EOF occurs and the specified amount could not be read, this function will return an [`ErrorKind::UnexpectedEof`]. + /// + /// This function consumes the data from the buffer, unless an error occurs, in which case no data is consumed. + /// pub fn read_u8(&mut self) -> io::Result { let buf = self.read_exact(1)?; Ok(buf[0]) } + /// Returns an immutable reference to the underlying [`std::io::Read`]er + /// + /// Reading directly from the underlying stream will cause data loss pub fn reader_ref(&mut self) -> &R { &self.reader } + /// Returns a mutable reference to the underlying [`std::io::Read`]er + /// + /// Reading directly from the underlying stream will cause data loss pub fn reader_mut(&mut self) -> &mut R { &mut self.reader } + /// Internal function to fetch data from the internal buffer and/or reader fn fetch(&mut self, amount: usize, exact: bool, consume: bool) -> io::Result<&[u8]> { let previous_len = self.buffer.len(); let mut buffered = previous_len - self.cursor; @@ -109,6 +187,7 @@ impl PeekReader { } } } + // if some bytes were read, add them to the buffer if read > 0 { if self.buffer.capacity() - previous_len < read { // reallocate