aboutsummaryrefslogtreecommitdiff
path: root/src/read.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/read.rs')
-rw-r--r--src/read.rs637
1 files changed, 637 insertions, 0 deletions
diff --git a/src/read.rs b/src/read.rs
new file mode 100644
index 0000000..1b53018
--- /dev/null
+++ b/src/read.rs
@@ -0,0 +1,637 @@
+#[cfg(feature = "alloc")]
+use alloc::{vec, vec::Vec};
+#[cfg(feature = "std")]
+use core::cmp;
+use core::mem;
+
+#[cfg(feature = "std")]
+use std::io::{self, Read as StdRead};
+
+use crate::error::{Error, ErrorCode, Result};
+
+#[cfg(not(feature = "unsealed_read_write"))]
+/// Trait used by the deserializer for iterating over input.
+///
+/// This trait is sealed by default, enabling the `unsealed_read_write` feature removes this bound
+/// to allow objects outside of this crate to implement this trait.
+pub trait Read<'de>: private::Sealed {
+ #[doc(hidden)]
+ /// Read n bytes from the input.
+ ///
+ /// Implementations that can are asked to return a slice with a Long lifetime that outlives the
+ /// decoder, but others (eg. ones that need to allocate the data into a temporary buffer) can
+ /// return it with a Short lifetime that just lives for the time of read's mutable borrow of
+ /// the reader.
+ ///
+ /// This may, as a side effect, clear the reader's scratch buffer (as the provided
+ /// implementation does).
+
+ // A more appropriate lifetime setup for this (that would allow the Deserializer::convert_str
+ // to stay a function) would be something like `fn read<'a, 'r: 'a>(&'a mut 'r immut self, ...) -> ...
+ // EitherLifetime<'r, 'de>>`, which borrows self mutably for the duration of the function and
+ // downgrates that reference to an immutable one that outlives the result (protecting the
+ // scratch buffer from changes), but alas, that can't be expressed (yet?).
+ fn read<'a>(&'a mut self, n: usize) -> Result<EitherLifetime<'a, 'de>> {
+ self.clear_buffer();
+ self.read_to_buffer(n)?;
+
+ Ok(self.take_buffer())
+ }
+
+ #[doc(hidden)]
+ fn next(&mut self) -> Result<Option<u8>>;
+
+ #[doc(hidden)]
+ fn peek(&mut self) -> Result<Option<u8>>;
+
+ #[doc(hidden)]
+ fn clear_buffer(&mut self);
+
+ #[doc(hidden)]
+ fn read_to_buffer(&mut self, n: usize) -> Result<()>;
+
+ #[doc(hidden)]
+ fn take_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de>;
+
+ #[doc(hidden)]
+ fn read_into(&mut self, buf: &mut [u8]) -> Result<()>;
+
+ #[doc(hidden)]
+ fn discard(&mut self);
+
+ #[doc(hidden)]
+ fn offset(&self) -> u64;
+}
+
+#[cfg(feature = "unsealed_read_write")]
+/// Trait used by the deserializer for iterating over input.
+pub trait Read<'de> {
+ /// Read n bytes from the input.
+ ///
+ /// Implementations that can are asked to return a slice with a Long lifetime that outlives the
+ /// decoder, but others (eg. ones that need to allocate the data into a temporary buffer) can
+ /// return it with a Short lifetime that just lives for the time of read's mutable borrow of
+ /// the reader.
+ ///
+ /// This may, as a side effect, clear the reader's scratch buffer (as the provided
+ /// implementation does).
+
+ // A more appropriate lifetime setup for this (that would allow the Deserializer::convert_str
+ // to stay a function) would be something like `fn read<'a, 'r: 'a>(&'a mut 'r immut self, ...) -> ...
+ // EitherLifetime<'r, 'de>>`, which borrows self mutably for the duration of the function and
+ // downgrates that reference to an immutable one that outlives the result (protecting the
+ // scratch buffer from changes), but alas, that can't be expressed (yet?).
+ fn read<'a>(&'a mut self, n: usize) -> Result<EitherLifetime<'a, 'de>> {
+ self.clear_buffer();
+ self.read_to_buffer(n)?;
+
+ Ok(self.take_buffer())
+ }
+
+ /// Read the next byte from the input, if any.
+ fn next(&mut self) -> Result<Option<u8>>;
+
+ /// Peek at the next byte of the input, if any. This does not advance the reader, so the result
+ /// of this function will remain the same until a read or clear occurs.
+ fn peek(&mut self) -> Result<Option<u8>>;
+
+ /// Clear the underlying scratch buffer
+ fn clear_buffer(&mut self);
+
+ /// Append n bytes from the reader to the reader's scratch buffer (without clearing it)
+ fn read_to_buffer(&mut self, n: usize) -> Result<()>;
+
+ /// Read out everything accumulated in the reader's scratch buffer. This may, as a side effect,
+ /// clear it.
+ fn take_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de>;
+
+ /// Read from the input until `buf` is full or end of input is encountered.
+ fn read_into(&mut self, buf: &mut [u8]) -> Result<()>;
+
+ /// Discard any data read by `peek`.
+ fn discard(&mut self);
+
+ /// Returns the offset from the start of the reader.
+ fn offset(&self) -> u64;
+}
+
+/// Represents a reader that can return its current position
+pub trait Offset {
+ fn byte_offset(&self) -> usize;
+}
+
+/// Represents a buffer with one of two lifetimes.
+pub enum EitherLifetime<'short, 'long> {
+ /// The short lifetime
+ Short(&'short [u8]),
+ /// The long lifetime
+ Long(&'long [u8]),
+}
+
+#[cfg(not(feature = "unsealed_read_write"))]
+mod private {
+ pub trait Sealed {}
+}
+
+/// CBOR input source that reads from a std::io input stream.
+#[cfg(feature = "std")]
+#[derive(Debug)]
+pub struct IoRead<R>
+where
+ R: io::Read,
+{
+ reader: OffsetReader<R>,
+ scratch: Vec<u8>,
+ ch: Option<u8>,
+}
+
+#[cfg(feature = "std")]
+impl<R> IoRead<R>
+where
+ R: io::Read,
+{
+ /// Creates a new CBOR input source to read from a std::io input stream.
+ pub fn new(reader: R) -> IoRead<R> {
+ IoRead {
+ reader: OffsetReader { reader, offset: 0 },
+ scratch: vec![],
+ ch: None,
+ }
+ }
+
+ #[inline]
+ fn next_inner(&mut self) -> Result<Option<u8>> {
+ let mut buf = [0; 1];
+ loop {
+ match self.reader.read(&mut buf) {
+ Ok(0) => return Ok(None),
+ Ok(_) => return Ok(Some(buf[0])),
+ Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+ Err(e) => return Err(Error::io(e)),
+ }
+ }
+ }
+}
+
+#[cfg(all(feature = "std", not(feature = "unsealed_read_write")))]
+impl<R> private::Sealed for IoRead<R> where R: io::Read {}
+
+#[cfg(feature = "std")]
+impl<'de, R> Read<'de> for IoRead<R>
+where
+ R: io::Read,
+{
+ #[inline]
+ fn next(&mut self) -> Result<Option<u8>> {
+ match self.ch.take() {
+ Some(ch) => Ok(Some(ch)),
+ None => self.next_inner(),
+ }
+ }
+
+ #[inline]
+ fn peek(&mut self) -> Result<Option<u8>> {
+ match self.ch {
+ Some(ch) => Ok(Some(ch)),
+ None => {
+ self.ch = self.next_inner()?;
+ Ok(self.ch)
+ }
+ }
+ }
+
+ fn read_to_buffer(&mut self, mut n: usize) -> Result<()> {
+ // defend against malicious input pretending to be huge strings by limiting growth
+ self.scratch.reserve(cmp::min(n, 16 * 1024));
+
+ if n == 0 {
+ return Ok(());
+ }
+
+ if let Some(ch) = self.ch.take() {
+ self.scratch.push(ch);
+ n -= 1;
+ }
+
+ // n == 0 is OK here and needs no further special treatment
+
+ let transfer_result = {
+ // Prepare for take() (which consumes its reader) by creating a reference adaptor
+ // that'll only live in this block
+ let reference = self.reader.by_ref();
+ // Append the first n bytes of the reader to the scratch vector (or up to
+ // an error or EOF indicated by a shorter read)
+ let mut taken = reference.take(n as u64);
+ taken.read_to_end(&mut self.scratch)
+ };
+
+ match transfer_result {
+ Ok(r) if r == n => Ok(()),
+ Ok(_) => Err(Error::syntax(
+ ErrorCode::EofWhileParsingValue,
+ self.offset(),
+ )),
+ Err(e) => Err(Error::io(e)),
+ }
+ }
+
+ fn clear_buffer(&mut self) {
+ self.scratch.clear();
+ }
+
+ fn take_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de> {
+ EitherLifetime::Short(&self.scratch)
+ }
+
+ fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
+ self.reader.read_exact(buf).map_err(|e| {
+ if e.kind() == io::ErrorKind::UnexpectedEof {
+ Error::syntax(ErrorCode::EofWhileParsingValue, self.offset())
+ } else {
+ Error::io(e)
+ }
+ })
+ }
+
+ #[inline]
+ fn discard(&mut self) {
+ self.ch = None;
+ }
+
+ fn offset(&self) -> u64 {
+ self.reader.offset
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R> Offset for IoRead<R>
+where
+ R: std::io::Read,
+{
+ fn byte_offset(&self) -> usize {
+ self.offset() as usize
+ }
+}
+
+#[cfg(feature = "std")]
+#[derive(Debug)]
+struct OffsetReader<R> {
+ reader: R,
+ offset: u64,
+}
+
+#[cfg(feature = "std")]
+impl<R> io::Read for OffsetReader<R>
+where
+ R: io::Read,
+{
+ #[inline]
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let r = self.reader.read(buf);
+ if let Ok(count) = r {
+ self.offset += count as u64;
+ }
+ r
+ }
+}
+
+/// A CBOR input source that reads from a slice of bytes.
+#[cfg(any(feature = "std", feature = "alloc"))]
+#[derive(Debug)]
+pub struct SliceRead<'a> {
+ slice: &'a [u8],
+ scratch: Vec<u8>,
+ index: usize,
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'a> SliceRead<'a> {
+ /// Creates a CBOR input source to read from a slice of bytes.
+ pub fn new(slice: &'a [u8]) -> SliceRead<'a> {
+ SliceRead {
+ slice,
+ scratch: vec![],
+ index: 0,
+ }
+ }
+
+ fn end(&self, n: usize) -> Result<usize> {
+ match self.index.checked_add(n) {
+ Some(end) if end <= self.slice.len() => Ok(end),
+ _ => Err(Error::syntax(
+ ErrorCode::EofWhileParsingValue,
+ self.slice.len() as u64,
+ )),
+ }
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'a> Offset for SliceRead<'a> {
+ #[inline]
+ fn byte_offset(&self) -> usize {
+ self.index
+ }
+}
+
+#[cfg(all(
+ any(feature = "std", feature = "alloc"),
+ not(feature = "unsealed_read_write")
+))]
+impl<'a> private::Sealed for SliceRead<'a> {}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'a> Read<'a> for SliceRead<'a> {
+ #[inline]
+ fn next(&mut self) -> Result<Option<u8>> {
+ Ok(if self.index < self.slice.len() {
+ let ch = self.slice[self.index];
+ self.index += 1;
+ Some(ch)
+ } else {
+ None
+ })
+ }
+
+ #[inline]
+ fn peek(&mut self) -> Result<Option<u8>> {
+ Ok(if self.index < self.slice.len() {
+ Some(self.slice[self.index])
+ } else {
+ None
+ })
+ }
+
+ fn clear_buffer(&mut self) {
+ self.scratch.clear();
+ }
+
+ fn read_to_buffer(&mut self, n: usize) -> Result<()> {
+ let end = self.end(n)?;
+ let slice = &self.slice[self.index..end];
+ self.scratch.extend_from_slice(slice);
+ self.index = end;
+
+ Ok(())
+ }
+
+ #[inline]
+ fn read<'b>(&'b mut self, n: usize) -> Result<EitherLifetime<'b, 'a>> {
+ let end = self.end(n)?;
+ let slice = &self.slice[self.index..end];
+ self.index = end;
+ Ok(EitherLifetime::Long(slice))
+ }
+
+ fn take_buffer<'b>(&'b mut self) -> EitherLifetime<'b, 'a> {
+ EitherLifetime::Short(&self.scratch)
+ }
+
+ #[inline]
+ fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
+ let end = self.end(buf.len())?;
+ buf.copy_from_slice(&self.slice[self.index..end]);
+ self.index = end;
+ Ok(())
+ }
+
+ #[inline]
+ fn discard(&mut self) {
+ self.index += 1;
+ }
+
+ fn offset(&self) -> u64 {
+ self.index as u64
+ }
+}
+
+/// A CBOR input source that reads from a slice of bytes using a fixed size scratch buffer.
+///
+/// [`SliceRead`](struct.SliceRead.html) and [`MutSliceRead`](struct.MutSliceRead.html) are usually
+/// preferred over this, as they can handle indefinite length items.
+#[derive(Debug)]
+pub struct SliceReadFixed<'a, 'b> {
+ slice: &'a [u8],
+ scratch: &'b mut [u8],
+ index: usize,
+ scratch_index: usize,
+}
+
+impl<'a, 'b> SliceReadFixed<'a, 'b> {
+ /// Creates a CBOR input source to read from a slice of bytes, backed by a scratch buffer.
+ pub fn new(slice: &'a [u8], scratch: &'b mut [u8]) -> SliceReadFixed<'a, 'b> {
+ SliceReadFixed {
+ slice,
+ scratch,
+ index: 0,
+ scratch_index: 0,
+ }
+ }
+
+ fn end(&self, n: usize) -> Result<usize> {
+ match self.index.checked_add(n) {
+ Some(end) if end <= self.slice.len() => Ok(end),
+ _ => Err(Error::syntax(
+ ErrorCode::EofWhileParsingValue,
+ self.slice.len() as u64,
+ )),
+ }
+ }
+
+ fn scratch_end(&self, n: usize) -> Result<usize> {
+ match self.scratch_index.checked_add(n) {
+ Some(end) if end <= self.scratch.len() => Ok(end),
+ _ => Err(Error::scratch_too_small(self.index as u64)),
+ }
+ }
+}
+
+#[cfg(not(feature = "unsealed_read_write"))]
+impl<'a, 'b> private::Sealed for SliceReadFixed<'a, 'b> {}
+
+impl<'a, 'b> Read<'a> for SliceReadFixed<'a, 'b> {
+ #[inline]
+ fn next(&mut self) -> Result<Option<u8>> {
+ Ok(if self.index < self.slice.len() {
+ let ch = self.slice[self.index];
+ self.index += 1;
+ Some(ch)
+ } else {
+ None
+ })
+ }
+
+ #[inline]
+ fn peek(&mut self) -> Result<Option<u8>> {
+ Ok(if self.index < self.slice.len() {
+ Some(self.slice[self.index])
+ } else {
+ None
+ })
+ }
+
+ fn clear_buffer(&mut self) {
+ self.scratch_index = 0;
+ }
+
+ fn read_to_buffer(&mut self, n: usize) -> Result<()> {
+ let end = self.end(n)?;
+ let scratch_end = self.scratch_end(n)?;
+ let slice = &self.slice[self.index..end];
+ self.scratch[self.scratch_index..scratch_end].copy_from_slice(&slice);
+ self.index = end;
+ self.scratch_index = scratch_end;
+
+ Ok(())
+ }
+
+ fn read<'c>(&'c mut self, n: usize) -> Result<EitherLifetime<'c, 'a>> {
+ let end = self.end(n)?;
+ let slice = &self.slice[self.index..end];
+ self.index = end;
+ Ok(EitherLifetime::Long(slice))
+ }
+
+ fn take_buffer<'c>(&'c mut self) -> EitherLifetime<'c, 'a> {
+ EitherLifetime::Short(&self.scratch[0..self.scratch_index])
+ }
+
+ #[inline]
+ fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
+ let end = self.end(buf.len())?;
+ buf.copy_from_slice(&self.slice[self.index..end]);
+ self.index = end;
+ Ok(())
+ }
+
+ #[inline]
+ fn discard(&mut self) {
+ self.index += 1;
+ }
+
+ fn offset(&self) -> u64 {
+ self.index as u64
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'a, 'b> Offset for SliceReadFixed<'a, 'b> {
+ #[inline]
+ fn byte_offset(&self) -> usize {
+ self.index
+ }
+}
+
+/// A CBOR input source that reads from a slice of bytes, and can move data around internally to
+/// reassemble indefinite strings without the need of an allocated scratch buffer.
+#[derive(Debug)]
+pub struct MutSliceRead<'a> {
+ /// A complete view of the reader's data. It is promised that bytes before buffer_end are not
+ /// mutated any more.
+ slice: &'a mut [u8],
+ /// Read cursor position in slice
+ index: usize,
+ /// Number of bytes already discarded from the slice
+ before: usize,
+ /// End of the buffer area that contains all bytes read_into_buffer. This is always <= index.
+ buffer_end: usize,
+}
+
+impl<'a> MutSliceRead<'a> {
+ /// Creates a CBOR input source to read from a slice of bytes.
+ pub fn new(slice: &'a mut [u8]) -> MutSliceRead<'a> {
+ MutSliceRead {
+ slice,
+ index: 0,
+ before: 0,
+ buffer_end: 0,
+ }
+ }
+
+ fn end(&self, n: usize) -> Result<usize> {
+ match self.index.checked_add(n) {
+ Some(end) if end <= self.slice.len() => Ok(end),
+ _ => Err(Error::syntax(
+ ErrorCode::EofWhileParsingValue,
+ self.slice.len() as u64,
+ )),
+ }
+ }
+}
+
+#[cfg(not(feature = "unsealed_read_write"))]
+impl<'a> private::Sealed for MutSliceRead<'a> {}
+
+impl<'a> Read<'a> for MutSliceRead<'a> {
+ #[inline]
+ fn next(&mut self) -> Result<Option<u8>> {
+ // This is duplicated from SliceRead, can that be eased?
+ Ok(if self.index < self.slice.len() {
+ let ch = self.slice[self.index];
+ self.index += 1;
+ Some(ch)
+ } else {
+ None
+ })
+ }
+
+ #[inline]
+ fn peek(&mut self) -> Result<Option<u8>> {
+ // This is duplicated from SliceRead, can that be eased?
+ Ok(if self.index < self.slice.len() {
+ Some(self.slice[self.index])
+ } else {
+ None
+ })
+ }
+
+ fn clear_buffer(&mut self) {
+ self.slice = &mut mem::replace(&mut self.slice, &mut [])[self.index..];
+ self.before += self.index;
+ self.index = 0;
+ self.buffer_end = 0;
+ }
+
+ fn read_to_buffer(&mut self, n: usize) -> Result<()> {
+ let end = self.end(n)?;
+ debug_assert!(
+ self.buffer_end <= self.index,
+ "MutSliceRead invariant violated: scratch buffer exceeds index"
+ );
+ self.slice[self.buffer_end..end].rotate_left(self.index - self.buffer_end);
+ self.buffer_end += n;
+ self.index = end;
+
+ Ok(())
+ }
+
+ fn take_buffer<'b>(&'b mut self) -> EitherLifetime<'b, 'a> {
+ let (left, right) = mem::replace(&mut self.slice, &mut []).split_at_mut(self.index);
+ self.slice = right;
+ self.before += self.index;
+ self.index = 0;
+
+ let left = &left[..self.buffer_end];
+ self.buffer_end = 0;
+
+ EitherLifetime::Long(left)
+ }
+
+ #[inline]
+ fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
+ // This is duplicated from SliceRead, can that be eased?
+ let end = self.end(buf.len())?;
+ buf.copy_from_slice(&self.slice[self.index..end]);
+ self.index = end;
+ Ok(())
+ }
+
+ #[inline]
+ fn discard(&mut self) {
+ self.index += 1;
+ }
+
+ fn offset(&self) -> u64 {
+ (self.before + self.index) as u64
+ }
+}