use super::*; use ciborium_io::Read; /// An error that occurred while decoding #[derive(Debug)] pub enum Error { /// An error occurred while reading bytes /// /// Contains the underlying error reaturned while reading. Io(T), /// An error occurred while parsing bytes /// /// Contains the offset into the stream where the syntax error occurred. Syntax(usize), } impl From for Error { #[inline] fn from(value: T) -> Self { Self::Io(value) } } /// A decoder for deserializing CBOR items /// /// This decoder manages the low-level decoding of CBOR items into `Header` /// objects. It also contains utility functions for parsing segmented bytes /// and text inputs. pub struct Decoder { reader: R, offset: usize, buffer: Option, } impl<R: Read> From<R> for Decoder<R> { #[inline] fn from(value: R) -> Self { Self { reader: value, offset: 0, buffer: None, } } } impl<R: Read> Read for Decoder<R> { type Error = R::Error; #[inline] fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { assert!(self.buffer.is_none()); self.reader.read_exact(data)?; self.offset += data.len(); Ok(()) } } impl<R: Read> Decoder<R> { #[inline] fn pull_title(&mut self) -> Result<Title, Error<R::Error>> { if let Some(title) = self.buffer.take() { self.offset += title.1.as_ref().len() + 1; return Ok(title); } let mut prefix = [0u8; 1]; self.read_exact(&mut prefix[..])?; let major = match prefix[0] >> 5 { 0 => Major::Positive, 1 => Major::Negative, 2 => Major::Bytes, 3 => Major::Text, 4 => Major::Array, 5 => Major::Map, 6 => Major::Tag, 7 => Major::Other, _ => unreachable!(), }; let mut minor = match prefix[0] & 0b00011111 { x if x < 24 => Minor::This(x), 24 => Minor::Next1([0; 1]), 25 => Minor::Next2([0; 2]), 26 => Minor::Next4([0; 4]), 27 => Minor::Next8([0; 8]), 31 => Minor::More, _ => return Err(Error::Syntax(self.offset - 1)), }; self.read_exact(minor.as_mut())?; Ok(Title(major, minor)) } #[inline] fn push_title(&mut self, item: Title) { assert!(self.buffer.is_none()); self.buffer = Some(item); self.offset -= item.1.as_ref().len() + 1; } /// Pulls the next header from the input #[inline] pub fn pull(&mut self) -> Result<Header, Error<R::Error>> { let offset = self.offset; self.pull_title()? .try_into() .map_err(|_| Error::Syntax(offset)) } /// Push a single header into the input buffer /// /// # Panics /// /// This function panics if called while there is already a header in the /// input buffer. You should take care to call this function only after /// pulling a header to ensure there is nothing in the input buffer. #[inline] pub fn push(&mut self, item: Header) { self.push_title(Title::from(item)) } /// Gets the current byte offset into the stream /// /// The offset starts at zero when the decoder is created. Therefore, if /// bytes were already read from the reader before the decoder was created, /// you must account for this. #[inline] pub fn offset(&mut self) -> usize { self.offset } /// Process an incoming bytes item /// /// In CBOR, bytes can be segmented. The logic for this can be a bit tricky, /// so we encapsulate that logic using this function. This function **MUST** /// be called immediately after first pulling a `Header::Bytes(len)` from /// the wire and `len` must be provided to this function from that value. /// /// The `buf` parameter provides a buffer used when reading in the segmented /// bytes. A large buffer will result in fewer calls to read incoming bytes /// at the cost of memory usage. You should consider this trade off when /// deciding the size of your buffer. #[inline] pub fn bytes(&mut self, len: Option<usize>) -> Segments<R, crate::seg::Bytes> { self.push(Header::Bytes(len)); Segments::new(self, |header| match header { Header::Bytes(len) => Ok(len), _ => Err(()), }) } /// Process an incoming text item /// /// In CBOR, text can be segmented. The logic for this can be a bit tricky, /// so we encapsulate that logic using this function. This function **MUST** /// be called immediately after first pulling a `Header::Text(len)` from /// the wire and `len` must be provided to this function from that value. /// /// The `buf` parameter provides a buffer used when reading in the segmented /// text. A large buffer will result in fewer calls to read incoming bytes /// at the cost of memory usage. You should consider this trade off when /// deciding the size of your buffer. #[inline] pub fn text(&mut self, len: Option<usize>) -> Segments<R, crate::seg::Text> { self.push(Header::Text(len)); Segments::new(self, |header| match header { Header::Text(len) => Ok(len), _ => Err(()), }) } }