1 | use super::*; |
2 | |
3 | use ciborium_io::Read; |
4 | |
5 | /// An error that occurred while decoding |
6 | #[derive(Debug)] |
7 | pub 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 | |
19 | impl<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. |
31 | pub struct Decoder<R: Read> { |
32 | reader: R, |
33 | offset: usize, |
34 | buffer: Option<Title>, |
35 | } |
36 | |
37 | impl<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 | |
48 | impl<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 | |
60 | impl<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 | |