blob: 8a1fe90caee8b4e45155eb2ebce1571db7adae72 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
//! Low level CBOR parsing tools
//!
//! This crate contains low-level types for encoding and decoding items in
//! CBOR. This crate is usable in both `no_std` and `no_alloc` environments.
//! To understand how this crate works, first we will look at the structure
//! of a CBOR item on the wire.
//!
//! # Anatomy of a CBOR Item
//!
//! This is a brief anatomy of a CBOR item on the wire.
//!
//! ```text
//! +------------+-----------+
//! | | |
//! | Major | Minor |
//! | (3bits) | (5bits) |
//! | | |
//! +------------+-----------+
//! ^ ^
//! | |
//! +-----+ +-----+
//! | |
//! | |
//! +----------------------------+--------------+
//! | | | |
//! | Prefix | Affix | Suffix |
//! | (1 byte) | (0-8 bytes) | (0+ bytes) |
//! | | | |
//! +------------+---------------+--------------+
//!
//! | | |
//! +------------+---------------+--------------+
//! | |
//! v v
//!
//! Header Body
//! ```
//!
//! The `ciborium` crate works by providing the `Decoder` and `Encoder` types
//! which provide input and output for a CBOR header (see: `Header`). From
//! there, you can either handle the body yourself or use the provided utility
//! functions.
//!
//! For more information on the CBOR format, see
//! [RFC 7049](https://tools.ietf.org/html/rfc7049).
//!
//! # Decoding
//!
//! In order to decode CBOR, you will create a `Decoder` from a reader. The
//! decoder instance will allow you to `Decoder::pull()` `Header` instances
//! from the input.
//!
//! Most CBOR items are fully contained in their headers and therefore have no
//! body. These items can be evaluated directly from the `Header` instance.
//!
//! Bytes and text items have a body but do not contain child items. Since
//! both bytes and text values may be segmented, parsing them can be a bit
//! tricky. Therefore, we provide helper functions to parse these types. See
//! `Decoder::bytes()` and `Decoder::text()` for more details.
//!
//! Array and map items have a body which contains child items. These can be
//! parsed by simply doing `Decoder::pull()` to parse the child items.
//!
//! ## Example
//!
//! ```rust
//! use ciborium_ll::{Decoder, Header};
//! use ciborium_io::Read as _;
//!
//! let input = b"\x6dHello, World!";
//! let mut decoder = Decoder::from(&input[..]);
//! let mut chunks = 0;
//!
//! match decoder.pull().unwrap() {
//! Header::Text(len) => {
//! let mut segments = decoder.text(len);
//! while let Some(mut segment) = segments.pull().unwrap() {
//! let mut buffer = [0u8; 7];
//! while let Some(chunk) = segment.pull(&mut buffer[..]).unwrap() {
//! match chunk {
//! "Hello, " if chunks == 0 => chunks = 1,
//! "World!" if chunks == 1 => chunks = 2,
//! _ => panic!("received unexpected chunk"),
//! }
//! }
//! }
//! }
//!
//! _ => panic!("received unexpected value"),
//! }
//!
//! assert_eq!(chunks, 2);
//! ```
//!
//! # Encoding
//!
//! To encode values to CBOR, create an `Encoder` from a writer. The encoder
//! instance provides the `Encoder::push()` method to write a `Header` value
//! to the wire. CBOR item bodies can be written directly.
//!
//! For bytes and text, there are the `Encoder::bytes()` and `Encoder::text()`
//! utility functions, respectively, which will properly segment the output
//! on the wire for you.
//!
//! ## Example
//!
//! ```rust
//! use ciborium_ll::{Encoder, Header};
//! use ciborium_io::Write as _;
//!
//! let mut buffer = [0u8; 19];
//! let mut encoder = Encoder::from(&mut buffer[..]);
//!
//! // Write the structure
//! encoder.push(Header::Map(Some(1))).unwrap();
//! encoder.push(Header::Positive(7)).unwrap();
//! encoder.text("Hello, World!", 7).unwrap();
//!
//! // Validate our output
//! encoder.flush().unwrap();
//! assert_eq!(b"\xa1\x07\x7f\x67Hello, \x66World!\xff", &buffer[..]);
//! ```
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
#![deny(clippy::all)]
#![deny(clippy::cargo)]
#[cfg(feature = "alloc")]
extern crate alloc;
mod dec;
mod enc;
mod hdr;
mod seg;
pub use dec::*;
pub use enc::*;
pub use hdr::*;
pub use seg::{Segment, Segments};
/// Simple value constants
pub mod simple {
#![allow(missing_docs)]
pub const FALSE: u8 = 20;
pub const TRUE: u8 = 21;
pub const NULL: u8 = 22;
pub const UNDEFINED: u8 = 23;
}
/// Tag constants
pub mod tag {
#![allow(missing_docs)]
pub const BIGPOS: u64 = 2;
pub const BIGNEG: u64 = 3;
}
#[derive(Debug)]
struct InvalidError(());
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Major {
Positive,
Negative,
Bytes,
Text,
Array,
Map,
Tag,
Other,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Minor {
This(u8),
Next1([u8; 1]),
Next2([u8; 2]),
Next4([u8; 4]),
Next8([u8; 8]),
More,
}
impl AsRef<[u8]> for Minor {
#[inline]
fn as_ref(&self) -> &[u8] {
match self {
Self::More => &[],
Self::This(..) => &[],
Self::Next1(x) => x.as_ref(),
Self::Next2(x) => x.as_ref(),
Self::Next4(x) => x.as_ref(),
Self::Next8(x) => x.as_ref(),
}
}
}
impl AsMut<[u8]> for Minor {
#[inline]
fn as_mut(&mut self) -> &mut [u8] {
match self {
Self::More => &mut [],
Self::This(..) => &mut [],
Self::Next1(x) => x.as_mut(),
Self::Next2(x) => x.as_mut(),
Self::Next4(x) => x.as_mut(),
Self::Next8(x) => x.as_mut(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct Title(pub Major, pub Minor);
#[cfg(test)]
mod tests {
use super::*;
macro_rules! neg {
($i:expr) => {
Header::Negative((($i as i128) ^ !0) as u64)
};
}
#[allow(clippy::excessive_precision)]
#[test]
fn leaf() {
use core::f64::{INFINITY, NAN};
let data = &[
(Header::Positive(0), "00", true),
(Header::Positive(1), "01", true),
(Header::Positive(10), "0a", true),
(Header::Positive(23), "17", true),
(Header::Positive(24), "1818", true),
(Header::Positive(25), "1819", true),
(Header::Positive(100), "1864", true),
(Header::Positive(1000), "1903e8", true),
(Header::Positive(1000000), "1a000f4240", true),
(Header::Positive(1000000000000), "1b000000e8d4a51000", true),
(
Header::Positive(18446744073709551615),
"1bffffffffffffffff",
true,
),
(neg!(-18446744073709551616), "3bffffffffffffffff", true),
(neg!(-1), "20", true),
(neg!(-10), "29", true),
(neg!(-100), "3863", true),
(neg!(-1000), "3903e7", true),
(Header::Float(0.0), "f90000", true),
(Header::Float(-0.0), "f98000", true),
(Header::Float(1.0), "f93c00", true),
(Header::Float(1.1), "fb3ff199999999999a", true),
(Header::Float(1.5), "f93e00", true),
(Header::Float(65504.0), "f97bff", true),
(Header::Float(100000.0), "fa47c35000", true),
(Header::Float(3.4028234663852886e+38), "fa7f7fffff", true),
(Header::Float(1.0e+300), "fb7e37e43c8800759c", true),
(Header::Float(5.960464477539063e-8), "f90001", true),
(Header::Float(0.00006103515625), "f90400", true),
(Header::Float(-4.0), "f9c400", true),
(Header::Float(-4.1), "fbc010666666666666", true),
(Header::Float(INFINITY), "f97c00", true),
(Header::Float(NAN), "f97e00", true),
(Header::Float(-INFINITY), "f9fc00", true),
(Header::Float(INFINITY), "fa7f800000", false),
(Header::Float(NAN), "fa7fc00000", false),
(Header::Float(-INFINITY), "faff800000", false),
(Header::Float(INFINITY), "fb7ff0000000000000", false),
(Header::Float(NAN), "fb7ff8000000000000", false),
(Header::Float(-INFINITY), "fbfff0000000000000", false),
(Header::Simple(simple::FALSE), "f4", true),
(Header::Simple(simple::TRUE), "f5", true),
(Header::Simple(simple::NULL), "f6", true),
(Header::Simple(simple::UNDEFINED), "f7", true),
(Header::Simple(16), "f0", true),
(Header::Simple(24), "f818", true),
(Header::Simple(255), "f8ff", true),
(Header::Tag(0), "c0", true),
(Header::Tag(1), "c1", true),
(Header::Tag(23), "d7", true),
(Header::Tag(24), "d818", true),
(Header::Tag(32), "d820", true),
(Header::Bytes(Some(0)), "40", true),
(Header::Bytes(Some(4)), "44", true),
(Header::Text(Some(0)), "60", true),
(Header::Text(Some(4)), "64", true),
];
for (header, bytes, encode) in data.iter().cloned() {
let bytes = hex::decode(bytes).unwrap();
let mut decoder = Decoder::from(&bytes[..]);
match (header, decoder.pull().unwrap()) {
// NaN equality...
(Header::Float(l), Header::Float(r)) if l.is_nan() && r.is_nan() => (),
// Everything else...
(l, r) => assert_eq!(l, r),
}
if encode {
let mut buffer = [0u8; 1024];
let mut writer = &mut buffer[..];
let mut encoder = Encoder::from(&mut writer);
encoder.push(header).unwrap();
let len = writer.len();
assert_eq!(&bytes[..], &buffer[..1024 - len]);
}
}
}
#[test]
fn node() {
let data: &[(&str, &[Header])] = &[
("80", &[Header::Array(Some(0))]),
(
"83010203",
&[
Header::Array(Some(3)),
Header::Positive(1),
Header::Positive(2),
Header::Positive(3),
],
),
(
"98190102030405060708090a0b0c0d0e0f101112131415161718181819",
&[
Header::Array(Some(25)),
Header::Positive(1),
Header::Positive(2),
Header::Positive(3),
Header::Positive(4),
Header::Positive(5),
Header::Positive(6),
Header::Positive(7),
Header::Positive(8),
Header::Positive(9),
Header::Positive(10),
Header::Positive(11),
Header::Positive(12),
Header::Positive(13),
Header::Positive(14),
Header::Positive(15),
Header::Positive(16),
Header::Positive(17),
Header::Positive(18),
Header::Positive(19),
Header::Positive(20),
Header::Positive(21),
Header::Positive(22),
Header::Positive(23),
Header::Positive(24),
Header::Positive(25),
],
),
("a0", &[Header::Map(Some(0))]),
(
"a201020304",
&[
Header::Map(Some(2)),
Header::Positive(1),
Header::Positive(2),
Header::Positive(3),
Header::Positive(4),
],
),
("9fff", &[Header::Array(None), Header::Break]),
(
"9f018202039f0405ffff",
&[
Header::Array(None),
Header::Positive(1),
Header::Array(Some(2)),
Header::Positive(2),
Header::Positive(3),
Header::Array(None),
Header::Positive(4),
Header::Positive(5),
Header::Break,
Header::Break,
],
),
(
"9f01820203820405ff",
&[
Header::Array(None),
Header::Positive(1),
Header::Array(Some(2)),
Header::Positive(2),
Header::Positive(3),
Header::Array(Some(2)),
Header::Positive(4),
Header::Positive(5),
Header::Break,
],
),
(
"83018202039f0405ff",
&[
Header::Array(Some(3)),
Header::Positive(1),
Header::Array(Some(2)),
Header::Positive(2),
Header::Positive(3),
Header::Array(None),
Header::Positive(4),
Header::Positive(5),
Header::Break,
],
),
(
"83019f0203ff820405",
&[
Header::Array(Some(3)),
Header::Positive(1),
Header::Array(None),
Header::Positive(2),
Header::Positive(3),
Header::Break,
Header::Array(Some(2)),
Header::Positive(4),
Header::Positive(5),
],
),
(
"9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff",
&[
Header::Array(None),
Header::Positive(1),
Header::Positive(2),
Header::Positive(3),
Header::Positive(4),
Header::Positive(5),
Header::Positive(6),
Header::Positive(7),
Header::Positive(8),
Header::Positive(9),
Header::Positive(10),
Header::Positive(11),
Header::Positive(12),
Header::Positive(13),
Header::Positive(14),
Header::Positive(15),
Header::Positive(16),
Header::Positive(17),
Header::Positive(18),
Header::Positive(19),
Header::Positive(20),
Header::Positive(21),
Header::Positive(22),
Header::Positive(23),
Header::Positive(24),
Header::Positive(25),
Header::Break,
],
),
];
for (bytes, headers) in data {
let bytes = hex::decode(bytes).unwrap();
// Test decoding
let mut decoder = Decoder::from(&bytes[..]);
for header in headers.iter().cloned() {
assert_eq!(header, decoder.pull().unwrap());
}
// Test encoding
let mut buffer = [0u8; 1024];
let mut writer = &mut buffer[..];
let mut encoder = Encoder::from(&mut writer);
for header in headers.iter().cloned() {
encoder.push(header).unwrap();
}
let len = writer.len();
assert_eq!(&bytes[..], &buffer[..1024 - len]);
}
}
}