| //! Provides various functions and structs for MessagePack decoding. |
| //! |
| //! Most of the function defined in this module will silently handle interruption error (EINTR) |
| //! received from the given `Read` to be in consistent state with the `Write::write_all` method in |
| //! the standard library. |
| //! |
| //! Any other error would immediately interrupt the parsing process. If your reader can results in |
| //! I/O error and simultaneously be a recoverable state (for example, when reading from |
| //! non-blocking socket and it returns EWOULDBLOCK) be sure that you buffer the data externally |
| //! to avoid data loss (using `BufRead` readers with manual consuming or some other way). |
| |
| mod dec; |
| mod ext; |
| mod sint; |
| mod str; |
| mod uint; |
| |
| pub use self::dec::{read_f32, read_f64}; |
| pub use self::ext::{ |
| read_ext_meta, read_fixext1, read_fixext16, read_fixext2, read_fixext4, read_fixext8, ExtMeta, |
| }; |
| pub use self::sint::{read_i16, read_i32, read_i64, read_i8, read_nfix}; |
| #[allow(deprecated)] |
| // While we re-export deprecated items, we don't want to trigger warnings while compiling this crate |
| pub use self::str::{read_str, read_str_from_slice, read_str_len, read_str_ref, DecodeStringError}; |
| pub use self::uint::{read_pfix, read_u16, read_u32, read_u64, read_u8}; |
| |
| #[cfg(feature = "std")] |
| use std::error; |
| use core::fmt::{self, Display, Debug, Formatter}; |
| |
| use num_traits::cast::FromPrimitive; |
| |
| use crate::Marker; |
| |
| pub mod bytes; |
| pub use bytes::Bytes; |
| |
| #[doc(inline)] |
| #[allow(deprecated)] |
| pub use crate::errors::Error; |
| |
| |
| /// The error type for I/O operations on `RmpRead` and associated traits. |
| /// |
| /// For [std::io::Read], this is [std::io::Error] |
| pub trait RmpReadErr: Display + Debug + crate::errors::MaybeErrBound + 'static {} |
| #[cfg(feature = "std")] |
| impl RmpReadErr for std::io::Error {} |
| impl RmpReadErr for core::convert::Infallible {} |
| |
| macro_rules! read_byteorder_utils { |
| ($($name:ident => $tp:ident),* $(,)?) => { |
| $( |
| #[inline] |
| #[doc(hidden)] |
| fn $name(&mut self) -> Result<$tp, ValueReadError<Self::Error>> where Self: Sized { |
| const SIZE: usize = core::mem::size_of::<$tp>(); |
| let mut buf: [u8; SIZE] = [0u8; SIZE]; |
| self.read_exact_buf(&mut buf).map_err(ValueReadError::InvalidDataRead)?; |
| Ok(paste::paste! { |
| <byteorder::BigEndian as byteorder::ByteOrder>::[<read_ $tp>](&mut buf) |
| }) |
| } |
| )* |
| }; |
| } |
| mod sealed{ |
| pub trait Sealed {} |
| #[cfg(feature = "std")] |
| impl<T: ?Sized + std::io::Read> Sealed for T {} |
| #[cfg(not(feature = "std"))] |
| impl<'a> Sealed for &'a [u8] {} |
| impl Sealed for super::Bytes<'_> {} |
| } |
| |
| |
| /// A type that `rmp` supports reading from. |
| /// |
| /// The methods of this trait should be considered an implementation detail (for now). |
| /// It is currently sealed (can not be implemented by the user). |
| /// |
| /// See also [std::io::Read] and [byteorder::ReadBytesExt] |
| /// |
| /// Its primary implementations are [std::io::Read] and [Bytes]. |
| pub trait RmpRead: sealed::Sealed { |
| type Error: RmpReadErr; |
| /// Read a single (unsigned) byte from this stream |
| #[inline] |
| fn read_u8(&mut self) -> Result<u8, Self::Error> { |
| let mut buf = [0; 1]; |
| self.read_exact_buf(&mut buf)?; |
| Ok(buf[0]) |
| } |
| |
| /// Read the exact number of bytes needed to fill the specified buffer. |
| /// |
| /// If there are not enough bytes, this will return an error. |
| /// |
| /// See also [std::io::Read::read_exact] |
| fn read_exact_buf(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>; |
| |
| // Internal helper functions to map I/O error into the `InvalidDataRead` error. |
| |
| /// Read a single (unsigned) byte from this stream. |
| #[inline] |
| #[doc(hidden)] |
| fn read_data_u8(&mut self) -> Result<u8, ValueReadError<Self::Error>> { |
| self.read_u8().map_err(ValueReadError::InvalidDataRead) |
| } |
| /// Read a single (signed) byte from this stream. |
| #[inline] |
| #[doc(hidden)] |
| fn read_data_i8(&mut self) -> Result<i8, ValueReadError<Self::Error>> { |
| self.read_data_u8().map(|b| b as i8) |
| } |
| |
| read_byteorder_utils!( |
| read_data_u16 => u16, |
| read_data_u32 => u32, |
| read_data_u64 => u64, |
| read_data_i16 => i16, |
| read_data_i32 => i32, |
| read_data_i64 => i64, |
| read_data_f32 => f32, |
| read_data_f64 => f64 |
| ); |
| } |
| |
| /* |
| * HACK: rmpv & rmp-erde used the internal read_data_* functions. |
| * |
| * Since adding no_std support moved these functions to the RmpRead trait, |
| * this broke compatiblity (despite changing no public APIs). |
| * |
| * In theory, we could update rmpv and rmp-serde to use the new APIS, |
| * but that would be needless churn (and might surprise users who just want to update rmp proper). |
| * |
| * Instead, we emulate these internal APIs for now, |
| * so that rmpv and rmp-serde continue to compile without issue. |
| * |
| * |
| * TODO: Remove this hack once we release a new version of rmp proper |
| */ |
| |
| macro_rules! wrap_data_funcs_for_compatibility { |
| ($($tp:ident),* $(,)?) => { |
| $(paste::paste! { |
| #[cfg(feature = "std")] |
| #[doc(hidden)] |
| #[deprecated(note = "internal function. rmpv & rmp-serde need to switch to RmpRead")] |
| pub fn [<read_data_ $tp>] <R: std::io::Read>(buf: &mut R) -> Result<$tp, ValueReadError> { |
| buf.[<read_data_ $tp>]() |
| } |
| })* |
| }; |
| } |
| wrap_data_funcs_for_compatibility!( |
| u8, u16, u32, u64, |
| i8, i16, i32, i64, |
| f32, f64 |
| ); |
| |
| #[cfg(feature = "std")] |
| impl<T: std::io::Read> RmpRead for T { |
| type Error = std::io::Error; |
| |
| #[inline] |
| fn read_exact_buf(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { |
| std::io::Read::read_exact(self, buf) |
| } |
| } |
| |
| // An error returned from the `write_marker` and `write_fixval` functions. |
| struct MarkerWriteError<E: RmpReadErr>(E); |
| |
| impl<E: RmpReadErr> From<E> for MarkerWriteError<E> { |
| #[cold] |
| fn from(err: E) -> Self { |
| MarkerWriteError(err) |
| } |
| } |
| |
| |
| /// An error that can occur when attempting to read a MessagePack marker from the reader. |
| #[derive(Debug)] |
| #[allow(deprecated)] // Needed for backwards compat |
| pub struct MarkerReadError<E: RmpReadErr = Error>(pub E); |
| |
| /// An error which can occur when attempting to read a MessagePack value from the reader. |
| #[derive(Debug)] |
| #[allow(deprecated)] // Needed for backwards compat |
| pub enum ValueReadError<E: RmpReadErr = Error> { |
| /// Failed to read the marker. |
| InvalidMarkerRead(E), |
| /// Failed to read the data. |
| InvalidDataRead(E), |
| /// The type decoded isn't match with the expected one. |
| TypeMismatch(Marker), |
| } |
| |
| #[cfg(feature = "std")] |
| impl error::Error for ValueReadError { |
| #[cold] |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match *self { |
| ValueReadError::InvalidMarkerRead(ref err) | |
| ValueReadError::InvalidDataRead(ref err) => Some(err), |
| ValueReadError::TypeMismatch(..) => None, |
| } |
| } |
| } |
| |
| impl Display for ValueReadError { |
| #[cold] |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { |
| // TODO: This should probably use formatting |
| f.write_str(match *self { |
| ValueReadError::InvalidMarkerRead(..) => "failed to read MessagePack marker", |
| ValueReadError::InvalidDataRead(..) => "failed to read MessagePack data", |
| ValueReadError::TypeMismatch(..) => { |
| "the type decoded isn't match with the expected one" |
| } |
| }) |
| } |
| } |
| |
| impl<E: RmpReadErr> From<MarkerReadError<E>> for ValueReadError<E> { |
| #[cold] |
| fn from(err: MarkerReadError<E>) -> ValueReadError<E> { |
| match err { |
| MarkerReadError(err) => ValueReadError::InvalidMarkerRead(err), |
| } |
| } |
| } |
| |
| impl<E: RmpReadErr> From<E> for MarkerReadError<E> { |
| #[cold] |
| fn from(err: E) -> MarkerReadError<E> { |
| MarkerReadError(err) |
| } |
| } |
| |
| /// Attempts to read a single byte from the given reader and to decode it as a MessagePack marker. |
| #[inline] |
| pub fn read_marker<R: RmpRead>(rd: &mut R) -> Result<Marker, MarkerReadError<R::Error>> { |
| Ok(Marker::from_u8(rd.read_u8()?)) |
| } |
| |
| /// Attempts to read a single byte from the given reader and to decode it as a nil value. |
| /// |
| /// According to the MessagePack specification, a nil value is represented as a single `0xc0` byte. |
| /// |
| /// # Errors |
| /// |
| /// This function will return `ValueReadError` on any I/O error while reading the nil marker, |
| /// except the EINTR, which is handled internally. |
| /// |
| /// It also returns `ValueReadError::TypeMismatch` if the actual type is not equal with the |
| /// expected one, indicating you with the actual type. |
| /// |
| /// # Note |
| /// |
| /// This function will silently retry on every EINTR received from the underlying `Read` until |
| /// successful read. |
| pub fn read_nil<R: RmpRead>(rd: &mut R) -> Result<(), ValueReadError<R::Error>> { |
| match read_marker(rd)? { |
| Marker::Null => Ok(()), |
| marker => Err(ValueReadError::TypeMismatch(marker)), |
| } |
| } |
| |
| /// Attempts to read a single byte from the given reader and to decode it as a boolean value. |
| /// |
| /// According to the MessagePack specification, an encoded boolean value is represented as a single |
| /// byte. |
| /// |
| /// # Errors |
| /// |
| /// This function will return `ValueReadError` on any I/O error while reading the bool marker, |
| /// except the EINTR, which is handled internally. |
| /// |
| /// It also returns `ValueReadError::TypeMismatch` if the actual type is not equal with the |
| /// expected one, indicating you with the actual type. |
| /// |
| /// # Note |
| /// |
| /// This function will silently retry on every EINTR received from the underlying `Read` until |
| /// successful read. |
| pub fn read_bool<R: RmpRead>(rd: &mut R) -> Result<bool, ValueReadError<R::Error>> { |
| match read_marker(rd)? { |
| Marker::True => Ok(true), |
| Marker::False => Ok(false), |
| marker => Err(ValueReadError::TypeMismatch(marker)), |
| } |
| } |
| |
| /// An error which can occur when attempting to read a MessagePack numeric value from the reader. |
| #[derive(Debug)] |
| #[allow(deprecated)] // Used for compatibility |
| pub enum NumValueReadError<E: RmpReadErr = Error> { |
| /// Failed to read the marker. |
| InvalidMarkerRead(E), |
| /// Failed to read the data. |
| InvalidDataRead(E), |
| /// The type decoded isn't match with the expected one. |
| TypeMismatch(Marker), |
| /// Out of range integral type conversion attempted. |
| OutOfRange, |
| } |
| |
| #[cfg(feature = "std")] |
| impl error::Error for NumValueReadError { |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match *self { |
| NumValueReadError::InvalidMarkerRead(ref err) | |
| NumValueReadError::InvalidDataRead(ref err) => Some(err), |
| NumValueReadError::TypeMismatch(..) | |
| NumValueReadError::OutOfRange => None, |
| } |
| } |
| } |
| |
| impl<E: RmpReadErr> Display for NumValueReadError<E> { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { |
| f.write_str(match *self { |
| NumValueReadError::InvalidMarkerRead(..) => "failed to read MessagePack marker", |
| NumValueReadError::InvalidDataRead(..) => "failed to read MessagePack data", |
| NumValueReadError::TypeMismatch(..) => { |
| "the type decoded isn't match with the expected one" |
| } |
| NumValueReadError::OutOfRange => "out of range integral type conversion attempted", |
| }) |
| } |
| } |
| |
| impl<E: RmpReadErr> From<MarkerReadError<E>> for NumValueReadError<E> { |
| #[cold] |
| fn from(err: MarkerReadError<E>) -> NumValueReadError<E> { |
| match err { |
| MarkerReadError(err) => NumValueReadError::InvalidMarkerRead(err), |
| } |
| } |
| } |
| |
| impl<E: RmpReadErr> From<ValueReadError<E>> for NumValueReadError<E> { |
| #[cold] |
| fn from(err: ValueReadError<E>) -> NumValueReadError<E> { |
| match err { |
| ValueReadError::InvalidMarkerRead(err) => NumValueReadError::InvalidMarkerRead(err), |
| ValueReadError::InvalidDataRead(err) => NumValueReadError::InvalidDataRead(err), |
| ValueReadError::TypeMismatch(err) => NumValueReadError::TypeMismatch(err), |
| } |
| } |
| } |
| |
| /// Attempts to read up to 9 bytes from the given reader and to decode them as integral `T` value. |
| /// |
| /// This function will try to read up to 9 bytes from the reader (1 for marker and up to 8 for data) |
| /// and interpret them as a big-endian `T`. |
| /// |
| /// Unlike `read_*`, this function weakens type restrictions, allowing you to safely decode packed |
| /// values even if you aren't sure about the actual integral type. |
| /// |
| /// # Errors |
| /// |
| /// This function will return `NumValueReadError` on any I/O error while reading either the marker |
| /// or the data. |
| /// |
| /// It also returns `NumValueReadError::OutOfRange` if the actual type is not an integer or it does |
| /// not fit in the given numeric range. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// let buf = [0xcd, 0x1, 0x2c]; |
| /// |
| /// assert_eq!(300u16, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// assert_eq!(300i16, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// assert_eq!(300u32, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// assert_eq!(300i32, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// assert_eq!(300u64, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// assert_eq!(300i64, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// assert_eq!(300usize, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// assert_eq!(300isize, rmp::decode::read_int(&mut &buf[..]).unwrap()); |
| /// ``` |
| pub fn read_int<T: FromPrimitive, R: RmpRead>(rd: &mut R) -> Result<T, NumValueReadError<R::Error>> { |
| let val = match read_marker(rd)? { |
| Marker::FixPos(val) => T::from_u8(val), |
| Marker::FixNeg(val) => T::from_i8(val), |
| Marker::U8 => T::from_u8(rd.read_data_u8()?), |
| Marker::U16 => T::from_u16(rd.read_data_u16()?), |
| Marker::U32 => T::from_u32(rd.read_data_u32()?), |
| Marker::U64 => T::from_u64(rd.read_data_u64()?), |
| Marker::I8 => T::from_i8(rd.read_data_i8()?), |
| Marker::I16 => T::from_i16(rd.read_data_i16()?), |
| Marker::I32 => T::from_i32(rd.read_data_i32()?), |
| Marker::I64 => T::from_i64(rd.read_data_i64()?), |
| marker => return Err(NumValueReadError::TypeMismatch(marker)), |
| }; |
| |
| val.ok_or(NumValueReadError::OutOfRange) |
| } |
| |
| /// Attempts to read up to 5 bytes from the given reader and to decode them as a big-endian u32 |
| /// array size. |
| /// |
| /// Array format family stores a sequence of elements in 1, 3, or 5 bytes of extra bytes in addition |
| /// to the elements. |
| /// |
| /// # Note |
| /// |
| /// This function will silently retry on every EINTR received from the underlying `Read` until |
| /// successful read. |
| // TODO: Docs. |
| // NOTE: EINTR is managed internally. |
| pub fn read_array_len<R>(rd: &mut R) -> Result<u32, ValueReadError<R::Error>> |
| where |
| R: RmpRead, |
| { |
| match read_marker(rd)? { |
| Marker::FixArray(size) => Ok(size as u32), |
| Marker::Array16 => Ok(rd.read_data_u16()? as u32), |
| Marker::Array32 => Ok(rd.read_data_u32()?), |
| marker => Err(ValueReadError::TypeMismatch(marker)), |
| } |
| } |
| |
| /// Attempts to read up to 5 bytes from the given reader and to decode them as a big-endian u32 |
| /// map size. |
| /// |
| /// Map format family stores a sequence of elements in 1, 3, or 5 bytes of extra bytes in addition |
| /// to the elements. |
| /// |
| /// # Note |
| /// |
| /// This function will silently retry on every EINTR received from the underlying `Read` until |
| /// successful read. |
| // TODO: Docs. |
| pub fn read_map_len<R: RmpRead>(rd: &mut R) -> Result<u32, ValueReadError<R::Error>> { |
| let marker = read_marker(rd)?; |
| marker_to_len(rd, marker) |
| } |
| |
| pub fn marker_to_len<R: RmpRead>(rd: &mut R, marker: Marker) -> Result<u32, ValueReadError<R::Error>> { |
| match marker { |
| Marker::FixMap(size) => Ok(size as u32), |
| Marker::Map16 => Ok(rd.read_data_u16()? as u32), |
| Marker::Map32 => Ok(rd.read_data_u32()?), |
| marker => Err(ValueReadError::TypeMismatch(marker)), |
| } |
| } |
| |
| /// Attempts to read up to 5 bytes from the given reader and to decode them as Binary array length. |
| /// |
| /// # Note |
| /// |
| /// This function will silently retry on every EINTR received from the underlying `Read` until |
| /// successful read. |
| // TODO: Docs. |
| pub fn read_bin_len<R: RmpRead>(rd: &mut R) -> Result<u32, ValueReadError<R::Error>> { |
| match read_marker(rd)? { |
| Marker::Bin8 => Ok(rd.read_data_u8()? as u32), |
| Marker::Bin16 => Ok(rd.read_data_u16()? as u32), |
| Marker::Bin32 => Ok(rd.read_data_u32()?), |
| marker => Err(ValueReadError::TypeMismatch(marker)), |
| } |
| } |