ciborium_ll/dec.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
use super::*;
use ciborium_io::Read;
/// An error that occurred while decoding
#[derive(Debug)]
pub enum Error<T> {
/// An error occurred while reading bytes
///
/// Contains the underlying error returned while reading.
Io(T),
/// An error occurred while parsing bytes
///
/// Contains the offset into the stream where the syntax error occurred.
Syntax(usize),
}
impl<T> From<T> for Error<T> {
#[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<R: Read> {
reader: R,
offset: usize,
buffer: Option<Title>,
}
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(()),
})
}
}