cbor_diag/parse/
binary.rs

1#![allow(clippy::useless_let_if_seq)]
2use std::{convert::TryFrom, str};
3
4use half::f16;
5use nom::{
6    bits::{bits, bytes},
7    branch::alt,
8    bytes::streaming::take as take_bytes,
9    combinator::{map, map_res, verify},
10    error::{make_error, ErrorKind},
11    multi::{count, many_till},
12    number::streaming::{be_f32, be_f64, be_u16},
13    sequence::{pair, preceded},
14    Err, IResult,
15};
16
17use crate::{ByteString, DataItem, FloatWidth, IntegerWidth, Result, Simple, Tag, TextString};
18
19pub fn take_bits<I, O>(count: usize) -> impl FnMut((I, usize)) -> IResult<(I, usize), O>
20where
21    I: nom::Slice<std::ops::RangeFrom<usize>> + nom::InputIter<Item = u8> + nom::InputLength,
22    O: From<u8>
23        + core::ops::AddAssign
24        + core::ops::Shl<usize, Output = O>
25        + core::ops::Shr<usize, Output = O>,
26{
27    nom::bits::streaming::take(count)
28}
29
30pub fn tag_bits<I, O>(pattern: O, count: usize) -> impl FnMut((I, usize)) -> IResult<(I, usize), O>
31where
32    I: nom::Slice<std::ops::RangeFrom<usize>>
33        + nom::InputIter<Item = u8>
34        + nom::InputLength
35        + Clone,
36    O: From<u8>
37        + core::ops::AddAssign
38        + core::ops::Shl<usize, Output = O>
39        + core::ops::Shr<usize, Output = O>
40        + PartialEq,
41{
42    nom::bits::streaming::tag(pattern, count)
43}
44
45fn integer(input: (&[u8], usize)) -> IResult<(&[u8], usize), (u64, IntegerWidth)> {
46    alt((
47        pair(verify(take_bits(5), |&v| v < 24), |i| {
48            Ok((i, IntegerWidth::Zero))
49        }),
50        pair(preceded(tag_bits(24, 5), take_bits(8)), |i| {
51            Ok((i, IntegerWidth::Eight))
52        }),
53        pair(preceded(tag_bits(25, 5), take_bits(16)), |i| {
54            Ok((i, IntegerWidth::Sixteen))
55        }),
56        pair(preceded(tag_bits(26, 5), take_bits(32)), |i| {
57            Ok((i, IntegerWidth::ThirtyTwo))
58        }),
59        pair(preceded(tag_bits(27, 5), take_bits(64)), |i| {
60            Ok((i, IntegerWidth::SixtyFour))
61        }),
62    ))(input)
63}
64
65fn positive(input: &[u8]) -> IResult<&[u8], DataItem> {
66    bits(preceded(
67        tag_bits(0, 3),
68        map(integer, |(value, bitwidth)| DataItem::Integer {
69            value,
70            bitwidth,
71        }),
72    ))(input)
73}
74
75fn negative(input: &[u8]) -> IResult<&[u8], DataItem> {
76    bits(preceded(
77        tag_bits(1, 3),
78        map(integer, |(value, bitwidth)| DataItem::Negative {
79            value,
80            bitwidth,
81        }),
82    ))(input)
83}
84
85fn definite_bytestring(input: &[u8]) -> IResult<&[u8], ByteString> {
86    let (input, (length, bitwidth)) = bits(preceded(tag_bits(2, 3), integer))(input)?;
87    let length = usize::try_from(length)
88        .map_err(|_| Err::Error(make_error(input, ErrorKind::LengthValue)))?;
89    let (input, data) = take_bytes(length)(input)?;
90    let data = data.to_owned();
91    Ok((input, ByteString { data, bitwidth }))
92}
93
94fn indefinite_bytestring(input: &[u8]) -> IResult<&[u8], DataItem> {
95    preceded(
96        bits(pair(tag_bits(2, 3), tag_bits(31, 5))),
97        map(many_till(definite_bytestring, stop_code), |(strings, _)| {
98            DataItem::IndefiniteByteString(strings)
99        }),
100    )(input)
101}
102
103fn bytestring(input: &[u8]) -> IResult<&[u8], DataItem> {
104    alt((
105        map(definite_bytestring, DataItem::ByteString),
106        indefinite_bytestring,
107    ))(input)
108}
109
110fn definite_textstring(input: &[u8]) -> IResult<&[u8], TextString> {
111    let (input, (length, bitwidth)) = bits(preceded(tag_bits(3, 3), integer))(input)?;
112    let length = usize::try_from(length)
113        .map_err(|_| Err::Error(make_error(input, ErrorKind::LengthValue)))?;
114    let (input, data) = map_res(take_bytes(length), str::from_utf8)(input)?;
115    let data = data.to_owned();
116    Ok((input, TextString { data, bitwidth }))
117}
118
119fn indefinite_textstring(input: &[u8]) -> IResult<&[u8], DataItem> {
120    preceded(
121        bits(pair(tag_bits(3, 3), tag_bits(31, 5))),
122        map(many_till(definite_textstring, stop_code), |(strings, _)| {
123            DataItem::IndefiniteTextString(strings)
124        }),
125    )(input)
126}
127
128fn textstring(input: &[u8]) -> IResult<&[u8], DataItem> {
129    alt((
130        map(definite_textstring, DataItem::TextString),
131        indefinite_textstring,
132    ))(input)
133}
134
135fn definite_array(input: &[u8]) -> IResult<&[u8], DataItem> {
136    let (input, (length, bitwidth)) = bits(preceded(tag_bits(4, 3), integer))(input)?;
137    let (input, data) = count(data_item, length as usize)(input)?;
138    Ok((
139        input,
140        DataItem::Array {
141            data,
142            bitwidth: Some(bitwidth),
143        },
144    ))
145}
146
147fn indefinite_array(input: &[u8]) -> IResult<&[u8], DataItem> {
148    preceded(
149        bits(pair(tag_bits(4, 3), tag_bits(31, 5))),
150        map(many_till(data_item, stop_code), |(data, _)| {
151            DataItem::Array {
152                data,
153                bitwidth: None,
154            }
155        }),
156    )(input)
157}
158
159fn array(input: &[u8]) -> IResult<&[u8], DataItem> {
160    alt((definite_array, indefinite_array))(input)
161}
162
163fn definite_map(input: &[u8]) -> IResult<&[u8], DataItem> {
164    let (input, (length, bitwidth)) = bits(preceded(tag_bits(5, 3), integer))(input)?;
165    let (input, data) = count(pair(data_item, data_item), length as usize)(input)?;
166    Ok((
167        input,
168        DataItem::Map {
169            data,
170            bitwidth: Some(bitwidth),
171        },
172    ))
173}
174
175fn indefinite_map(input: &[u8]) -> IResult<&[u8], DataItem> {
176    preceded(
177        bits(pair(tag_bits(5, 3), tag_bits(31, 5))),
178        map(
179            many_till(pair(data_item, data_item), stop_code),
180            |(data, _)| DataItem::Map {
181                data,
182                bitwidth: None,
183            },
184        ),
185    )(input)
186}
187
188fn data_map(input: &[u8]) -> IResult<&[u8], DataItem> {
189    alt((definite_map, indefinite_map))(input)
190}
191
192fn tag_bitsged(input: &[u8]) -> IResult<&[u8], DataItem> {
193    let (input, (tag, bitwidth)) = bits(preceded(tag_bits(6, 3), integer))(input)?;
194    let (input, value) = data_item(input)?;
195    let value = Box::new(value);
196    Ok((
197        input,
198        DataItem::Tag {
199            tag: Tag(tag),
200            bitwidth,
201            value,
202        },
203    ))
204}
205
206fn float(input: &[u8]) -> IResult<&[u8], DataItem> {
207    bits(preceded(
208        tag_bits(7, 3),
209        map(
210            alt((
211                preceded(
212                    tag_bits(25, 5),
213                    bytes::<_, _, nom::error::Error<&[u8]>, _, _>(map(be_u16, |u| {
214                        (f16::from_bits(u).to_f64(), FloatWidth::Sixteen)
215                    })),
216                ),
217                preceded(
218                    tag_bits(26, 5),
219                    bytes::<_, _, nom::error::Error<&[u8]>, _, _>(map(be_f32, |f| {
220                        (f64::from(f), FloatWidth::ThirtyTwo)
221                    })),
222                ),
223                preceded(
224                    tag_bits(27, 5),
225                    bytes::<_, _, nom::error::Error<&[u8]>, _, _>(map(be_f64, |f| {
226                        (f, FloatWidth::SixtyFour)
227                    })),
228                ),
229            )),
230            |(value, bitwidth)| DataItem::Float { value, bitwidth },
231        ),
232    ))(input)
233}
234
235fn simple(input: &[u8]) -> IResult<&[u8], DataItem> {
236    bits(preceded(
237        tag_bits(7, 3),
238        map(
239            alt((
240                verify(take_bits(5), |&v| v < 24),
241                preceded(tag_bits(24, 5), take_bits(8)),
242            )),
243            |value| DataItem::Simple(Simple(value)),
244        ),
245    ))(input)
246}
247
248fn stop_code(input: &[u8]) -> IResult<&[u8], DataItem> {
249    bits(preceded(
250        tag_bits(7, 3),
251        map(tag_bits(31, 5), |value| DataItem::Simple(Simple(value))),
252    ))(input)
253}
254
255fn data_item(input: &[u8]) -> IResult<&[u8], DataItem> {
256    alt((
257        positive,
258        negative,
259        bytestring,
260        textstring,
261        array,
262        data_map,
263        tag_bitsged,
264        float,
265        simple,
266    ))(input)
267}
268
269/// Parse a string containing a binary encoded CBOR data item.
270///
271/// # Examples
272///
273/// ```rust
274/// use cbor_diag::{DataItem, IntegerWidth, Tag, TextString};
275///
276/// assert_eq!(
277///     cbor_diag::parse_bytes(&b"\
278///         \xd8\x20\x73\x68\x74\x74\x70\x73\x3a\x2f\x2f\x65\x78\x61\x6d\x70\
279///         \x6c\x65\x2e\x63\x6f\x6d\
280///     "[..]).unwrap(),
281///     DataItem::Tag {
282///         tag: Tag::URI,
283///         bitwidth: IntegerWidth::Eight,
284///         value: Box::new(DataItem::TextString(TextString {
285///             data: "https://example.com".into(),
286///             bitwidth: IntegerWidth::Zero,
287///         })),
288///     });
289/// ```
290pub fn parse_bytes(bytes: impl AsRef<[u8]>) -> Result<DataItem> {
291    let (remaining, parsed) =
292        data_item(bytes.as_ref()).map_err(|e| format!("Parsing error ({e:?})"))?;
293    if !remaining.is_empty() {
294        return Err(format!(
295            "Remaining bytes ({})",
296            data_encoding::HEXLOWER.encode(remaining)
297        )
298        .into());
299    }
300    Ok(parsed)
301}
302
303/// Parse a string containing a binary encoded CBOR data item, optionally followed by more data.
304///
305/// Returns one of:
306///
307///  * `Err(_)` => a parsing error if there was an issue encountered
308///  * `Ok(None)` => the end of a data item was not reached
309///  * `Ok(Some(_))` => the parsed item along with how many bytes were used to parse this item
310///
311/// # Examples
312///
313/// ```rust
314/// use cbor_diag::{DataItem, IntegerWidth, Tag, TextString};
315///
316/// assert_eq!(
317///     cbor_diag::parse_bytes_partial(&b"\
318///         \xd8\x20\x73\x68\x74\x74\x70\x73\x3a\x2f\x2f\x65\x78\x61\x6d\x70\
319///         \x6c\x65\x2e\x63\x6f\
320///     "[..]).unwrap(),
321///     None);
322///
323/// assert_eq!(
324///     cbor_diag::parse_bytes_partial(&b"\
325///         \xd8\x20\x73\x68\x74\x74\x70\x73\x3a\x2f\x2f\x65\x78\x61\x6d\x70\
326///         \x6c\x65\x2e\x63\x6f\x6d\xff\
327///     "[..]).unwrap(),
328///     Some((
329///         DataItem::Tag {
330///             tag: Tag::URI,
331///             bitwidth: IntegerWidth::Eight,
332///             value: Box::new(DataItem::TextString(TextString {
333///                 data: "https://example.com".into(),
334///                 bitwidth: IntegerWidth::Zero,
335///             })),
336///         },
337///         22
338///     )));
339/// ```
340pub fn parse_bytes_partial(bytes: impl AsRef<[u8]>) -> Result<Option<(DataItem, usize)>> {
341    match data_item(bytes.as_ref()) {
342        Ok((remaining, item)) => Ok(Some((item, bytes.as_ref().len() - remaining.len()))),
343        Err(nom::Err::Incomplete(_)) => Ok(None),
344        Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => Err("Parser error".into()),
345    }
346}