ciborium_ll/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Low level CBOR parsing tools
4//!
5//! This crate contains low-level types for encoding and decoding items in
6//! CBOR. This crate is usable in both `no_std` and `no_alloc` environments.
7//! To understand how this crate works, first we will look at the structure
8//! of a CBOR item on the wire.
9//!
10//! # Anatomy of a CBOR Item
11//!
12//! This is a brief anatomy of a CBOR item on the wire.
13//!
14//! ```text
15//! +------------+-----------+
16//! |            |           |
17//! |   Major    |   Minor   |
18//! |  (3bits)   |  (5bits)  |
19//! |            |           |
20//! +------------+-----------+
21//! ^                        ^
22//! |                        |
23//! +-----+            +-----+
24//!       |            |
25//!       |            |
26//!       +----------------------------+--------------+
27//!       |            |               |              |
28//!       |   Prefix   |     Affix     |    Suffix    |
29//!       |  (1 byte)  |  (0-8 bytes)  |  (0+ bytes)  |
30//!       |            |               |              |
31//!       +------------+---------------+--------------+
32//!
33//!       |                            |              |
34//!       +------------+---------------+--------------+
35//!                    |                       |
36//!                    v                       v
37//!
38//!                  Header                   Body
39//! ```
40//!
41//! The `ciborium` crate works by providing the `Decoder` and `Encoder` types
42//! which provide input and output for a CBOR header (see: `Header`). From
43//! there, you can either handle the body yourself or use the provided utility
44//! functions.
45//!
46//! For more information on the CBOR format, see
47//! [RFC 7049](https://tools.ietf.org/html/rfc7049).
48//!
49//! # Decoding
50//!
51//! In order to decode CBOR, you will create a `Decoder` from a reader. The
52//! decoder instance will allow you to `Decoder::pull()` `Header` instances
53//! from the input.
54//!
55//! Most CBOR items are fully contained in their headers and therefore have no
56//! body. These items can be evaluated directly from the `Header` instance.
57//!
58//! Bytes and text items have a body but do not contain child items. Since
59//! both bytes and text values may be segmented, parsing them can be a bit
60//! tricky. Therefore, we provide helper functions to parse these types. See
61//! `Decoder::bytes()` and `Decoder::text()` for more details.
62//!
63//! Array and map items have a body which contains child items. These can be
64//! parsed by simply doing `Decoder::pull()` to parse the child items.
65//!
66//! ## Example
67//!
68//! ```rust
69//! use ciborium_ll::{Decoder, Header};
70//! use ciborium_io::Read as _;
71//!
72//! let input = b"\x6dHello, World!";
73//! let mut decoder = Decoder::from(&input[..]);
74//! let mut chunks = 0;
75//!
76//! match decoder.pull().unwrap() {
77//!     Header::Text(len) => {
78//!         let mut segments = decoder.text(len);
79//!         while let Some(mut segment) = segments.pull().unwrap() {
80//!             let mut buffer = [0u8; 7];
81//!             while let Some(chunk) = segment.pull(&mut buffer[..]).unwrap() {
82//!                  match chunk {
83//!                      "Hello, " if chunks == 0 => chunks = 1,
84//!                      "World!" if chunks == 1 => chunks = 2,
85//!                      _ => panic!("received unexpected chunk"),
86//!                  }
87//!             }
88//!         }
89//!     }
90//!
91//!     _ => panic!("received unexpected value"),
92//! }
93//!
94//! assert_eq!(chunks, 2);
95//! ```
96//!
97//! # Encoding
98//!
99//! To encode values to CBOR, create an `Encoder` from a writer. The encoder
100//! instance provides the `Encoder::push()` method to write a `Header` value
101//! to the wire. CBOR item bodies can be written directly.
102//!
103//! For bytes and text, there are the `Encoder::bytes()` and `Encoder::text()`
104//! utility functions, respectively, which will properly segment the output
105//! on the wire for you.
106//!
107//! ## Example
108//!
109//! ```rust
110//! use ciborium_ll::{Encoder, Header};
111//! use ciborium_io::Write as _;
112//!
113//! let mut buffer = [0u8; 19];
114//! let mut encoder = Encoder::from(&mut buffer[..]);
115//!
116//! // Write the structure
117//! encoder.push(Header::Map(Some(1))).unwrap();
118//! encoder.push(Header::Positive(7)).unwrap();
119//! encoder.text("Hello, World!", 7).unwrap();
120//!
121//! // Validate our output
122//! encoder.flush().unwrap();
123//! assert_eq!(b"\xa1\x07\x7f\x67Hello, \x66World!\xff", &buffer[..]);
124//! ```
125
126#![cfg_attr(not(feature = "std"), no_std)]
127#![deny(missing_docs)]
128#![deny(clippy::all)]
129#![deny(clippy::cargo)]
130
131#[cfg(feature = "alloc")]
132extern crate alloc;
133
134mod dec;
135mod enc;
136mod hdr;
137mod seg;
138
139pub use dec::*;
140pub use enc::*;
141pub use hdr::*;
142pub use seg::{Segment, Segments};
143
144/// Simple value constants
145pub mod simple {
146    #![allow(missing_docs)]
147
148    pub const FALSE: u8 = 20;
149    pub const TRUE: u8 = 21;
150    pub const NULL: u8 = 22;
151    pub const UNDEFINED: u8 = 23;
152}
153
154/// Tag constants
155pub mod tag {
156    #![allow(missing_docs)]
157
158    pub const BIGPOS: u64 = 2;
159    pub const BIGNEG: u64 = 3;
160}
161
162#[derive(Debug)]
163struct InvalidError(());
164
165#[derive(Copy, Clone, Debug, PartialEq, Eq)]
166enum Major {
167    Positive,
168    Negative,
169    Bytes,
170    Text,
171    Array,
172    Map,
173    Tag,
174    Other,
175}
176
177#[derive(Copy, Clone, Debug, PartialEq, Eq)]
178enum Minor {
179    This(u8),
180    Next1([u8; 1]),
181    Next2([u8; 2]),
182    Next4([u8; 4]),
183    Next8([u8; 8]),
184    More,
185}
186
187impl AsRef<[u8]> for Minor {
188    #[inline]
189    fn as_ref(&self) -> &[u8] {
190        match self {
191            Self::More => &[],
192            Self::This(..) => &[],
193            Self::Next1(x) => x.as_ref(),
194            Self::Next2(x) => x.as_ref(),
195            Self::Next4(x) => x.as_ref(),
196            Self::Next8(x) => x.as_ref(),
197        }
198    }
199}
200
201impl AsMut<[u8]> for Minor {
202    #[inline]
203    fn as_mut(&mut self) -> &mut [u8] {
204        match self {
205            Self::More => &mut [],
206            Self::This(..) => &mut [],
207            Self::Next1(x) => x.as_mut(),
208            Self::Next2(x) => x.as_mut(),
209            Self::Next4(x) => x.as_mut(),
210            Self::Next8(x) => x.as_mut(),
211        }
212    }
213}
214
215#[derive(Copy, Clone, Debug, PartialEq, Eq)]
216struct Title(pub Major, pub Minor);
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    macro_rules! neg {
223        ($i:expr) => {
224            Header::Negative((($i as i128) ^ !0) as u64)
225        };
226    }
227
228    #[allow(clippy::excessive_precision)]
229    #[test]
230    fn leaf() {
231        use core::f64::{INFINITY, NAN};
232
233        let data = &[
234            (Header::Positive(0), "00", true),
235            (Header::Positive(1), "01", true),
236            (Header::Positive(10), "0a", true),
237            (Header::Positive(23), "17", true),
238            (Header::Positive(24), "1818", true),
239            (Header::Positive(25), "1819", true),
240            (Header::Positive(100), "1864", true),
241            (Header::Positive(1000), "1903e8", true),
242            (Header::Positive(1000000), "1a000f4240", true),
243            (Header::Positive(1000000000000), "1b000000e8d4a51000", true),
244            (
245                Header::Positive(18446744073709551615),
246                "1bffffffffffffffff",
247                true,
248            ),
249            (neg!(-18446744073709551616), "3bffffffffffffffff", true),
250            (neg!(-1), "20", true),
251            (neg!(-10), "29", true),
252            (neg!(-100), "3863", true),
253            (neg!(-1000), "3903e7", true),
254            (Header::Float(0.0), "f90000", true),
255            (Header::Float(-0.0), "f98000", true),
256            (Header::Float(1.0), "f93c00", true),
257            (Header::Float(1.1), "fb3ff199999999999a", true),
258            (Header::Float(1.5), "f93e00", true),
259            (Header::Float(65504.0), "f97bff", true),
260            (Header::Float(100000.0), "fa47c35000", true),
261            (Header::Float(3.4028234663852886e+38), "fa7f7fffff", true),
262            (Header::Float(1.0e+300), "fb7e37e43c8800759c", true),
263            (Header::Float(5.960464477539063e-8), "f90001", true),
264            (Header::Float(0.00006103515625), "f90400", true),
265            (Header::Float(-4.0), "f9c400", true),
266            (Header::Float(-4.1), "fbc010666666666666", true),
267            (Header::Float(INFINITY), "f97c00", true),
268            (Header::Float(NAN), "f97e00", true),
269            (Header::Float(-INFINITY), "f9fc00", true),
270            (Header::Float(INFINITY), "fa7f800000", false),
271            (Header::Float(NAN), "fa7fc00000", false),
272            (Header::Float(-INFINITY), "faff800000", false),
273            (Header::Float(INFINITY), "fb7ff0000000000000", false),
274            (Header::Float(NAN), "fb7ff8000000000000", false),
275            (Header::Float(-INFINITY), "fbfff0000000000000", false),
276            (Header::Simple(simple::FALSE), "f4", true),
277            (Header::Simple(simple::TRUE), "f5", true),
278            (Header::Simple(simple::NULL), "f6", true),
279            (Header::Simple(simple::UNDEFINED), "f7", true),
280            (Header::Simple(16), "f0", true),
281            (Header::Simple(24), "f818", true),
282            (Header::Simple(255), "f8ff", true),
283            (Header::Tag(0), "c0", true),
284            (Header::Tag(1), "c1", true),
285            (Header::Tag(23), "d7", true),
286            (Header::Tag(24), "d818", true),
287            (Header::Tag(32), "d820", true),
288            (Header::Bytes(Some(0)), "40", true),
289            (Header::Bytes(Some(4)), "44", true),
290            (Header::Text(Some(0)), "60", true),
291            (Header::Text(Some(4)), "64", true),
292        ];
293
294        for (header, bytes, encode) in data.iter().cloned() {
295            let bytes = hex::decode(bytes).unwrap();
296
297            let mut decoder = Decoder::from(&bytes[..]);
298            match (header, decoder.pull().unwrap()) {
299                // NaN equality...
300                (Header::Float(l), Header::Float(r)) if l.is_nan() && r.is_nan() => (),
301
302                // Everything else...
303                (l, r) => assert_eq!(l, r),
304            }
305
306            if encode {
307                let mut buffer = [0u8; 1024];
308                let mut writer = &mut buffer[..];
309                let mut encoder = Encoder::from(&mut writer);
310                encoder.push(header).unwrap();
311
312                let len = writer.len();
313                assert_eq!(&bytes[..], &buffer[..1024 - len]);
314            }
315        }
316    }
317
318    #[test]
319    fn node() {
320        let data: &[(&str, &[Header])] = &[
321            ("80", &[Header::Array(Some(0))]),
322            (
323                "83010203",
324                &[
325                    Header::Array(Some(3)),
326                    Header::Positive(1),
327                    Header::Positive(2),
328                    Header::Positive(3),
329                ],
330            ),
331            (
332                "98190102030405060708090a0b0c0d0e0f101112131415161718181819",
333                &[
334                    Header::Array(Some(25)),
335                    Header::Positive(1),
336                    Header::Positive(2),
337                    Header::Positive(3),
338                    Header::Positive(4),
339                    Header::Positive(5),
340                    Header::Positive(6),
341                    Header::Positive(7),
342                    Header::Positive(8),
343                    Header::Positive(9),
344                    Header::Positive(10),
345                    Header::Positive(11),
346                    Header::Positive(12),
347                    Header::Positive(13),
348                    Header::Positive(14),
349                    Header::Positive(15),
350                    Header::Positive(16),
351                    Header::Positive(17),
352                    Header::Positive(18),
353                    Header::Positive(19),
354                    Header::Positive(20),
355                    Header::Positive(21),
356                    Header::Positive(22),
357                    Header::Positive(23),
358                    Header::Positive(24),
359                    Header::Positive(25),
360                ],
361            ),
362            ("a0", &[Header::Map(Some(0))]),
363            (
364                "a201020304",
365                &[
366                    Header::Map(Some(2)),
367                    Header::Positive(1),
368                    Header::Positive(2),
369                    Header::Positive(3),
370                    Header::Positive(4),
371                ],
372            ),
373            ("9fff", &[Header::Array(None), Header::Break]),
374            (
375                "9f018202039f0405ffff",
376                &[
377                    Header::Array(None),
378                    Header::Positive(1),
379                    Header::Array(Some(2)),
380                    Header::Positive(2),
381                    Header::Positive(3),
382                    Header::Array(None),
383                    Header::Positive(4),
384                    Header::Positive(5),
385                    Header::Break,
386                    Header::Break,
387                ],
388            ),
389            (
390                "9f01820203820405ff",
391                &[
392                    Header::Array(None),
393                    Header::Positive(1),
394                    Header::Array(Some(2)),
395                    Header::Positive(2),
396                    Header::Positive(3),
397                    Header::Array(Some(2)),
398                    Header::Positive(4),
399                    Header::Positive(5),
400                    Header::Break,
401                ],
402            ),
403            (
404                "83018202039f0405ff",
405                &[
406                    Header::Array(Some(3)),
407                    Header::Positive(1),
408                    Header::Array(Some(2)),
409                    Header::Positive(2),
410                    Header::Positive(3),
411                    Header::Array(None),
412                    Header::Positive(4),
413                    Header::Positive(5),
414                    Header::Break,
415                ],
416            ),
417            (
418                "83019f0203ff820405",
419                &[
420                    Header::Array(Some(3)),
421                    Header::Positive(1),
422                    Header::Array(None),
423                    Header::Positive(2),
424                    Header::Positive(3),
425                    Header::Break,
426                    Header::Array(Some(2)),
427                    Header::Positive(4),
428                    Header::Positive(5),
429                ],
430            ),
431            (
432                "9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff",
433                &[
434                    Header::Array(None),
435                    Header::Positive(1),
436                    Header::Positive(2),
437                    Header::Positive(3),
438                    Header::Positive(4),
439                    Header::Positive(5),
440                    Header::Positive(6),
441                    Header::Positive(7),
442                    Header::Positive(8),
443                    Header::Positive(9),
444                    Header::Positive(10),
445                    Header::Positive(11),
446                    Header::Positive(12),
447                    Header::Positive(13),
448                    Header::Positive(14),
449                    Header::Positive(15),
450                    Header::Positive(16),
451                    Header::Positive(17),
452                    Header::Positive(18),
453                    Header::Positive(19),
454                    Header::Positive(20),
455                    Header::Positive(21),
456                    Header::Positive(22),
457                    Header::Positive(23),
458                    Header::Positive(24),
459                    Header::Positive(25),
460                    Header::Break,
461                ],
462            ),
463        ];
464
465        for (bytes, headers) in data {
466            let bytes = hex::decode(bytes).unwrap();
467
468            // Test decoding
469            let mut decoder = Decoder::from(&bytes[..]);
470            for header in headers.iter().cloned() {
471                assert_eq!(header, decoder.pull().unwrap());
472            }
473
474            // Test encoding
475            let mut buffer = [0u8; 1024];
476            let mut writer = &mut buffer[..];
477            let mut encoder = Encoder::from(&mut writer);
478
479            for header in headers.iter().cloned() {
480                encoder.push(header).unwrap();
481            }
482
483            let len = writer.len();
484            assert_eq!(&bytes[..], &buffer[..1024 - len]);
485        }
486    }
487}