Source code
Revision control
Copy as Markdown
Other Tools
//! Implementation of the [Bytes] type
use super::RmpRead;
use crate::decode::RmpReadErr;
use core::fmt::{Display, Formatter};
/// Indicates that an error occurred reading from [Bytes]
#[derive(Debug)]
#[non_exhaustive]
// NOTE: We can't use thiserror because of no_std :(
pub enum BytesReadError {
/// Indicates that there were not enough bytes.
InsufficientBytes {
expected: usize,
actual: usize,
position: u64,
},
}
impl Display for BytesReadError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match *self {
BytesReadError::InsufficientBytes { expected, actual, position } => {
write!(f, "Expected at least bytes {expected}, but only got {actual} (pos {position})")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for BytesReadError {}
impl RmpReadErr for BytesReadError {}
/// A wrapper around `&[u8]` to read more efficiently.
///
/// This has a specialized implementation of `RmpWrite`
/// and has error type [Infallible](core::convert::Infallible).
///
/// This has the additional benefit of working on `#[no_std]` (unlike the builtin Read trait)
///
///
/// Unlike a plain `&[u8]` this also tracks an internal offset in the input (See [`Self::position`]).
///
/// This is used for (limited) compatibility with [`std::io::Cursor`]. Unlike a [Cursor](std::io::Cursor) it does
/// not support mark/reset.
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Bytes<'a> {
/// The internal position of the input buffer.
///
/// This is not required for correctness.
/// It is only used for error reporting (and to implement [`Self::position`])
current_position: u64,
bytes: &'a [u8],
}
impl<'a> Bytes<'a> {
/// Wrap an existing bytes slice.
///
/// This sets the internal position to zero.
#[inline]
#[must_use]
pub fn new(bytes: &'a [u8]) -> Self {
Bytes { bytes, current_position: 0 }
}
/// Get a reference to the remaining bytes in the buffer.
#[inline]
#[must_use]
pub fn remaining_slice(&self) -> &'a [u8] {
self.bytes
}
/// Return the position of the input buffer.
///
/// This is not required for correctness, it only exists to help mimic
/// [`Cursor::position`](std::io::Cursor::position)
#[inline]
#[must_use]
pub fn position(&self) -> u64 {
self.current_position
}
}
impl<'a> From<&'a [u8]> for Bytes<'a> {
#[inline]
fn from(bytes: &'a [u8]) -> Self {
Bytes { bytes, current_position: 0 }
}
}
impl RmpRead for Bytes<'_> {
type Error = BytesReadError;
#[inline]
fn read_u8(&mut self) -> Result<u8, Self::Error> {
if let Some((&first, newly_remaining)) = self.bytes.split_first() {
self.bytes = newly_remaining;
self.current_position += 1;
Ok(first)
} else {
Err(BytesReadError::InsufficientBytes {
expected: 1,
actual: 0,
position: self.current_position,
})
}
}
#[inline]
fn read_exact_buf(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
let to_read = buf.len();
if to_read <= self.bytes.len() {
let (src, newly_remaining) = self.bytes.split_at(to_read);
self.bytes = newly_remaining;
self.current_position += to_read as u64;
buf.copy_from_slice(src);
Ok(())
} else {
Err(BytesReadError::InsufficientBytes {
expected: to_read,
actual: self.bytes.len(),
position: self.current_position,
})
}
}
}
#[cfg(not(feature = "std"))]
impl<'a> RmpRead for &'a [u8] {
type Error = BytesReadError;
fn read_u8(&mut self) -> Result<u8, Self::Error> {
if let Some((&first, newly_remaining)) = self.split_first() {
*self = newly_remaining;
Ok(first)
} else {
Err(BytesReadError::InsufficientBytes {
expected: 1,
actual: 0,
position: 0,
})
}
}
fn read_exact_buf(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
let to_read = buf.len();
if to_read <= self.len() {
let (src, newly_remaining) = self.split_at(to_read);
*self = newly_remaining;
buf.copy_from_slice(src);
Ok(())
} else {
Err(BytesReadError::InsufficientBytes {
expected: to_read,
actual: self.len(),
position: 0,
})
}
}
}