1use super::*;
2
3use ciborium_io::Read;
4
5/// An error that occurred while decoding
6#[derive(Debug)]
7pub enum Error<T> {
8 /// An error occurred while reading bytes
9 ///
10 /// Contains the underlying error reaturned while reading.
11 Io(T),
12
13 /// An error occurred while parsing bytes
14 ///
15 /// Contains the offset into the stream where the syntax error occurred.
16 Syntax(usize),
17}
18
19impl<T> From<T> for Error<T> {
20 #[inline]
21 fn from(value: T) -> Self {
22 Self::Io(value)
23 }
24}
25
26/// A decoder for deserializing CBOR items
27///
28/// This decoder manages the low-level decoding of CBOR items into `Header`
29/// objects. It also contains utility functions for parsing segmented bytes
30/// and text inputs.
31pub struct Decoder<R: Read> {
32 reader: R,
33 offset: usize,
34 buffer: Option<Title>,
35}
36
37impl<R: Read> From<R> for Decoder<R> {
38 #[inline]
39 fn from(value: R) -> Self {
40 Self {
41 reader: value,
42 offset: 0,
43 buffer: None,
44 }
45 }
46}
47
48impl<R: Read> Read for Decoder<R> {
49 type Error = R::Error;
50
51 #[inline]
52 fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
53 assert!(self.buffer.is_none());
54 self.reader.read_exact(data)?;
55 self.offset += data.len();
56 Ok(())
57 }
58}
59
60impl<R: Read> Decoder<R> {
61 #[inline]
62 fn pull_title(&mut self) -> Result<Title, Error<R::Error>> {
63 if let Some(title) = self.buffer.take() {
64 self.offset += title.1.as_ref().len() + 1;
65 return Ok(title);
66 }
67
68 let mut prefix = [0u8; 1];
69 self.read_exact(&mut prefix[..])?;
70
71 let major = match prefix[0] >> 5 {
72 0 => Major::Positive,
73 1 => Major::Negative,
74 2 => Major::Bytes,
75 3 => Major::Text,
76 4 => Major::Array,
77 5 => Major::Map,
78 6 => Major::Tag,
79 7 => Major::Other,
80 _ => unreachable!(),
81 };
82
83 let mut minor = match prefix[0] & 0b00011111 {
84 x if x < 24 => Minor::This(x),
85 24 => Minor::Next1([0; 1]),
86 25 => Minor::Next2([0; 2]),
87 26 => Minor::Next4([0; 4]),
88 27 => Minor::Next8([0; 8]),
89 31 => Minor::More,
90 _ => return Err(Error::Syntax(self.offset - 1)),
91 };
92
93 self.read_exact(minor.as_mut())?;
94 Ok(Title(major, minor))
95 }
96
97 #[inline]
98 fn push_title(&mut self, item: Title) {
99 assert!(self.buffer.is_none());
100 self.buffer = Some(item);
101 self.offset -= item.1.as_ref().len() + 1;
102 }
103
104 /// Pulls the next header from the input
105 #[inline]
106 pub fn pull(&mut self) -> Result<Header, Error<R::Error>> {
107 let offset = self.offset;
108 self.pull_title()?
109 .try_into()
110 .map_err(|_| Error::Syntax(offset))
111 }
112
113 /// Push a single header into the input buffer
114 ///
115 /// # Panics
116 ///
117 /// This function panics if called while there is already a header in the
118 /// input buffer. You should take care to call this function only after
119 /// pulling a header to ensure there is nothing in the input buffer.
120 #[inline]
121 pub fn push(&mut self, item: Header) {
122 self.push_title(Title::from(item))
123 }
124
125 /// Gets the current byte offset into the stream
126 ///
127 /// The offset starts at zero when the decoder is created. Therefore, if
128 /// bytes were already read from the reader before the decoder was created,
129 /// you must account for this.
130 #[inline]
131 pub fn offset(&mut self) -> usize {
132 self.offset
133 }
134
135 /// Process an incoming bytes item
136 ///
137 /// In CBOR, bytes can be segmented. The logic for this can be a bit tricky,
138 /// so we encapsulate that logic using this function. This function **MUST**
139 /// be called immediately after first pulling a `Header::Bytes(len)` from
140 /// the wire and `len` must be provided to this function from that value.
141 ///
142 /// The `buf` parameter provides a buffer used when reading in the segmented
143 /// bytes. A large buffer will result in fewer calls to read incoming bytes
144 /// at the cost of memory usage. You should consider this trade off when
145 /// deciding the size of your buffer.
146 #[inline]
147 pub fn bytes(&mut self, len: Option<usize>) -> Segments<R, crate::seg::Bytes> {
148 self.push(Header::Bytes(len));
149 Segments::new(self, |header| match header {
150 Header::Bytes(len) => Ok(len),
151 _ => Err(()),
152 })
153 }
154
155 /// Process an incoming text item
156 ///
157 /// In CBOR, text can be segmented. The logic for this can be a bit tricky,
158 /// so we encapsulate that logic using this function. This function **MUST**
159 /// be called immediately after first pulling a `Header::Text(len)` from
160 /// the wire and `len` must be provided to this function from that value.
161 ///
162 /// The `buf` parameter provides a buffer used when reading in the segmented
163 /// text. A large buffer will result in fewer calls to read incoming bytes
164 /// at the cost of memory usage. You should consider this trade off when
165 /// deciding the size of your buffer.
166 #[inline]
167 pub fn text(&mut self, len: Option<usize>) -> Segments<R, crate::seg::Text> {
168 self.push(Header::Text(len));
169 Segments::new(self, |header| match header {
170 Header::Text(len) => Ok(len),
171 _ => Err(()),
172 })
173 }
174}
175