Skip to main content

cbor_core/
decoder.rs

1//! Iterators over CBOR sequences.
2//!
3//! [`SequenceDecoder`] iterates over an in-memory buffer; [`SequenceReader`]
4//! iterates over an `io::Read`. Both are produced by factory methods
5//! on [`DecodeOptions`](crate::DecodeOptions).
6
7use std::io;
8
9use crate::{
10    DecodeOptions, Format, IoResult, Result, Value,
11    io::{HexReader, HexSliceReader, PeekReader, SliceReader},
12    parse::Parser,
13};
14
15/// Iterator over a CBOR sequence stored in a byte slice.
16///
17/// Construct with [`SequenceDecoder::new`] for the crate defaults, or with
18/// [`DecodeOptions::sequence_decoder`] to choose an input format and decoding
19/// limits. Yields `Result<Value>` for each item; `next` returns `None`
20/// when the slice is fully consumed. For input from an `io::Read`
21/// source, use [`SequenceReader`] instead.
22///
23/// Sequence semantics depend on the format:
24///
25/// * [`Format::Binary`] and [`Format::Hex`]: items are concatenated with
26///   no separator. `next` returns `None` as soon as the buffer is fully
27///   consumed.
28/// * [`Format::Diagnostic`]: items are separated by a top-level comma
29///   and optional whitespace or comments. A trailing comma is
30///   accepted.
31///
32/// ```
33/// use cbor_core::SequenceDecoder;
34///
35/// // Binary CBOR sequence: three one-byte items.
36/// let items: Vec<_> = SequenceDecoder::new(&[0x01, 0x02, 0x03])
37///     .collect::<Result<_, _>>()
38///     .unwrap();
39/// assert_eq!(items.len(), 3);
40/// ```
41pub struct SequenceDecoder<'a> {
42    inner: SequenceDecoderInner<'a>,
43}
44
45enum SequenceDecoderInner<'a> {
46    Binary {
47        reader: SliceReader<'a>,
48        opts: DecodeOptions,
49    },
50    Hex {
51        reader: HexSliceReader<'a>,
52        opts: DecodeOptions,
53    },
54    Diagnostic {
55        parser: Parser<SliceReader<'a>>,
56    },
57}
58
59impl<'a> SequenceDecoder<'a> {
60    /// Decode a binary CBOR sequence from a byte slice.
61    ///
62    /// Shorthand for [`DecodeOptions::new().sequence_decoder(input)`](DecodeOptions::sequence_decoder),
63    /// so all limits use their defaults. Use the [`DecodeOptions`]
64    /// builder instead when you need hex or diagnostic input, or
65    /// want to adjust `recursion_limit`, `length_limit`, or
66    /// `oom_mitigation`.
67    ///
68    /// ```
69    /// use cbor_core::SequenceDecoder;
70    ///
71    /// let mut d = SequenceDecoder::new(&[0x01, 0x02]);
72    /// assert_eq!(d.next().unwrap().unwrap().to_u32().unwrap(), 1);
73    /// assert_eq!(d.next().unwrap().unwrap().to_u32().unwrap(), 2);
74    /// assert!(d.next().is_none());
75    /// ```
76    pub fn new<B: AsRef<[u8]> + ?Sized>(input: &'a B) -> Self {
77        Self::with_options(DecodeOptions::new(), input.as_ref())
78    }
79
80    pub(crate) fn with_options(opts: DecodeOptions, input: &'a [u8]) -> Self {
81        let inner = match opts.format_value() {
82            Format::Binary => SequenceDecoderInner::Binary {
83                reader: SliceReader(input),
84                opts,
85            },
86            Format::Hex => SequenceDecoderInner::Hex {
87                reader: HexSliceReader(input),
88                opts,
89            },
90            Format::Diagnostic => SequenceDecoderInner::Diagnostic {
91                parser: Parser::new(SliceReader(input), opts.recursion_limit_value()),
92            },
93        };
94        Self { inner }
95    }
96}
97
98impl<'a> Iterator for SequenceDecoder<'a> {
99    type Item = Result<Value>;
100
101    fn next(&mut self) -> Option<Self::Item> {
102        match &mut self.inner {
103            SequenceDecoderInner::Binary { reader, opts } => {
104                if reader.0.is_empty() {
105                    None
106                } else {
107                    Some(opts.decode_one(reader))
108                }
109            }
110            SequenceDecoderInner::Hex { reader, opts } => {
111                if reader.0.is_empty() {
112                    None
113                } else {
114                    Some(opts.decode_one(reader))
115                }
116            }
117            SequenceDecoderInner::Diagnostic { parser } => parser.parse_seq_item().transpose(),
118        }
119    }
120}
121
122/// Iterator over a CBOR sequence pulled from an [`io::Read`] source.
123///
124/// Construct with [`SequenceReader::new`] for the crate defaults, or with
125/// [`DecodeOptions::sequence_reader`] to choose an input format and
126/// decoding limits. Yields `IoResult<Value>` for each item. `next`
127/// returns `None` when the stream ends at an item boundary; a
128/// truncated item returns `Some(Err(IoError::Data(Error::UnexpectedEof)))`.
129/// For in-memory input, use [`SequenceDecoder`] instead: it returns plain
130/// `Result<Value>` without the I/O error arm.
131///
132/// Sequence semantics depend on the format:
133///
134/// * [`Format::Binary`] and [`Format::Hex`]: items are concatenated with
135///   no separator.
136/// * [`Format::Diagnostic`]: items are separated by a top-level comma
137///   and optional whitespace or comments. A trailing comma is
138///   accepted.
139///
140/// ```
141/// use cbor_core::SequenceReader;
142///
143/// let bytes: &[u8] = &[0x01, 0x02, 0x03];
144/// let items: Vec<_> = SequenceReader::new(bytes)
145///     .collect::<Result<_, _>>()
146///     .unwrap();
147/// assert_eq!(items.len(), 3);
148/// ```
149pub struct SequenceReader<R: io::Read> {
150    inner: SequenceReaderInner<R>,
151}
152
153enum SequenceReaderInner<R: io::Read> {
154    Binary {
155        reader: PeekReader<R>,
156        opts: DecodeOptions,
157    },
158    Hex {
159        reader: HexReader<PeekReader<R>>,
160        opts: DecodeOptions,
161    },
162    Diagnostic {
163        parser: Parser<R>,
164    },
165}
166
167impl<R: io::Read> SequenceReader<R> {
168    /// Decode a binary CBOR sequence from an [`io::Read`] source.
169    ///
170    /// Shorthand for [`DecodeOptions::new().sequence_reader(reader)`](DecodeOptions::sequence_reader),
171    /// so all limits use their defaults. Use the [`DecodeOptions`]
172    /// builder instead when you need hex or diagnostic input, or
173    /// want to adjust `recursion_limit`, `length_limit`, or
174    /// `oom_mitigation`.
175    ///
176    /// ```
177    /// use cbor_core::SequenceReader;
178    ///
179    /// let bytes: &[u8] = &[0x01, 0x02];
180    /// let mut s = SequenceReader::new(bytes);
181    /// assert_eq!(s.next().unwrap().unwrap().to_u32().unwrap(), 1);
182    /// assert_eq!(s.next().unwrap().unwrap().to_u32().unwrap(), 2);
183    /// assert!(s.next().is_none());
184    /// ```
185    pub fn new(reader: R) -> Self {
186        Self::with_options(DecodeOptions::new(), reader)
187    }
188
189    pub(crate) fn with_options(opts: DecodeOptions, reader: R) -> Self {
190        let inner = match opts.format_value() {
191            Format::Binary => SequenceReaderInner::Binary {
192                reader: PeekReader::new(reader),
193                opts,
194            },
195            Format::Hex => SequenceReaderInner::Hex {
196                reader: HexReader(PeekReader::new(reader)),
197                opts,
198            },
199            Format::Diagnostic => SequenceReaderInner::Diagnostic {
200                parser: Parser::new(reader, opts.recursion_limit_value()),
201            },
202        };
203        Self { inner }
204    }
205}
206
207impl<R: io::Read> Iterator for SequenceReader<R> {
208    type Item = IoResult<Value>;
209
210    fn next(&mut self) -> Option<Self::Item> {
211        match &mut self.inner {
212            SequenceReaderInner::Binary { reader, opts } => match reader.at_eof() {
213                Ok(true) => None,
214                Ok(false) => Some(opts.decode_one(reader)),
215                Err(error) => Some(Err(error)),
216            },
217            SequenceReaderInner::Hex { reader, opts } => match reader.0.at_eof() {
218                Ok(true) => None,
219                Ok(false) => Some(opts.decode_one(reader)),
220                Err(error) => Some(Err(error)),
221            },
222            SequenceReaderInner::Diagnostic { parser } => parser.parse_seq_item().transpose(),
223        }
224    }
225}