blob: 1736ce8d5633137f2ed2d29b35d3b53b6a0d8fb8 [file] [log] [blame]
//! CBOR and serialization.
//!
//! # Usage
//!
//! Serde CBOR supports Rust 1.40 and up. Add this to your `Cargo.toml`:
//! ```toml
//! [dependencies]
//! serde_cbor = "0.10"
//! ```
//!
//! Storing and loading Rust types is easy and requires only
//! minimal modifications to the program code.
//!
//! ```rust
//! use serde_derive::{Deserialize, Serialize};
//! use std::error::Error;
//! use std::fs::File;
//!
//! // Types annotated with `Serialize` can be stored as CBOR.
//! // To be able to load them again add `Deserialize`.
//! #[derive(Debug, Serialize, Deserialize)]
//! struct Mascot {
//! name: String,
//! species: String,
//! year_of_birth: u32,
//! }
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//! let ferris = Mascot {
//! name: "Ferris".to_owned(),
//! species: "crab".to_owned(),
//! year_of_birth: 2015,
//! };
//!
//! let ferris_file = File::create("examples/ferris.cbor")?;
//! // Write Ferris to the given file.
//! // Instead of a file you can use any type that implements `io::Write`
//! // like a HTTP body, database connection etc.
//! serde_cbor::to_writer(ferris_file, &ferris)?;
//!
//! let tux_file = File::open("examples/tux.cbor")?;
//! // Load Tux from a file.
//! // Serde CBOR performs roundtrip serialization meaning that
//! // the data will not change in any way.
//! let tux: Mascot = serde_cbor::from_reader(tux_file)?;
//!
//! println!("{:?}", tux);
//! // prints: Mascot { name: "Tux", species: "penguin", year_of_birth: 1996 }
//!
//! Ok(())
//! }
//! ```
//!
//! There are a lot of options available to customize the format.
//! To operate on untyped CBOR values have a look at the `Value` type.
//!
//! # Type-based Serialization and Deserialization
//! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
//! from CBOR via the serialization API. To be able to serialize a piece of data, it must implement
//! the `serde::Serialize` trait. To be able to deserialize a piece of data, it must implement the
//! `serde::Deserialize` trait. Serde provides an annotation to automatically generate the
//! code for these traits: `#[derive(Serialize, Deserialize)]`.
//!
//! The CBOR API also provides an enum `serde_cbor::Value`.
//!
//! # Packed Encoding
//! When serializing structs or enums in CBOR the keys or enum variant names will be serialized
//! as string keys to a map. Especially in embedded environments this can increase the file
//! size too much. In packed encoding the keys and variants will be serialized as variable sized
//! integers. The first 24 entries in any struct consume only a single byte!
//! To serialize a document in this format use `Serializer::new(writer).packed_format()` or
//! the shorthand `ser::to_vec_packed`. The deserialization works without any changes.
//!
//! # Self describing documents
//! In some contexts different formats are used but there is no way to declare the format used
//! out of band. For this reason CBOR has a magic number that may be added before any document.
//! Self describing documents are created with `serializer.self_describe()`.
//!
//! # Examples
//! Read a CBOR value that is known to be a map of string keys to string values and print it.
//!
//! ```rust
//! use std::collections::BTreeMap;
//! use serde_cbor::from_slice;
//!
//! let slice = b"\xa5aaaAabaBacaCadaDaeaE";
//! let value: BTreeMap<String, String> = from_slice(slice).unwrap();
//! println!("{:?}", value); // {"e": "E", "d": "D", "a": "A", "c": "C", "b": "B"}
//! ```
//!
//! Read a general CBOR value with an unknown content.
//!
//! ```rust
//! use serde_cbor::from_slice;
//! use serde_cbor::value::Value;
//!
//! let slice = b"\x82\x01\xa1aaab";
//! let value: Value = from_slice(slice).unwrap();
//! println!("{:?}", value); // Array([U64(1), Object({String("a"): String("b")})])
//! ```
//!
//! Serialize an object.
//!
//! ```rust
//! use std::collections::BTreeMap;
//! use serde_cbor::to_vec;
//!
//! let mut programming_languages = BTreeMap::new();
//! programming_languages.insert("rust", vec!["safe", "concurrent", "fast"]);
//! programming_languages.insert("python", vec!["powerful", "friendly", "open"]);
//! programming_languages.insert("js", vec!["lightweight", "interpreted", "object-oriented"]);
//! let encoded = to_vec(&programming_languages);
//! assert_eq!(encoded.unwrap().len(), 103);
//! ```
//!
//! Deserializing data in the middle of a slice
//! ```
//! # extern crate serde_cbor;
//! use serde_cbor::Deserializer;
//!
//! # fn main() {
//! let data: Vec<u8> = vec![
//! 0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x66, 0x66, 0x6f, 0x6f, 0x62,
//! 0x61, 0x72,
//! ];
//! let mut deserializer = Deserializer::from_slice(&data);
//! let value: &str = serde::de::Deserialize::deserialize(&mut deserializer)
//! .unwrap();
//! let rest = &data[deserializer.byte_offset()..];
//! assert_eq!(value, "foobar");
//! assert_eq!(rest, &[0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72]);
//! # }
//! ```
//!
//! # `no-std` support
//!
//! Serde CBOR supports building in a `no_std` context, use the following lines
//! in your `Cargo.toml` dependencies:
//! ``` toml
//! [dependencies]
//! serde = { version = "1.0", default-features = false }
//! serde_cbor = { version = "0.10", default-features = false }
//! ```
//!
//! Without the `std` feature the functions [from_reader], [from_slice], [to_vec], and [to_writer]
//! are not exported. To export [from_slice] and [to_vec] enable the `alloc` feature. The `alloc`
//! feature uses the [`alloc` library][alloc-lib] and requires at least version 1.36.0 of Rust.
//!
//! [alloc-lib]: https://doc.rust-lang.org/alloc/
//!
//! *Note*: to use derive macros in serde you will need to declare `serde`
//! dependency like so:
//! ``` toml
//! serde = { version = "1.0", default-features = false, features = ["derive"] }
//! ```
//!
//! Serialize an object with `no_std` and without `alloc`.
//! ``` rust
//! # #[macro_use] extern crate serde_derive;
//! # fn main() -> Result<(), serde_cbor::Error> {
//! use serde::Serialize;
//! use serde_cbor::Serializer;
//! use serde_cbor::ser::SliceWrite;
//!
//! #[derive(Serialize)]
//! struct User {
//! user_id: u32,
//! password_hash: [u8; 4],
//! }
//!
//! let mut buf = [0u8; 100];
//! let writer = SliceWrite::new(&mut buf[..]);
//! let mut ser = Serializer::new(writer);
//! let user = User {
//! user_id: 42,
//! password_hash: [1, 2, 3, 4],
//! };
//! user.serialize(&mut ser)?;
//! let writer = ser.into_inner();
//! let size = writer.bytes_written();
//! let expected = [
//! 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
//! 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
//! 0x68, 0x84, 0x1, 0x2, 0x3, 0x4
//! ];
//! assert_eq!(&buf[..size], expected);
//! # Ok(())
//! # }
//! ```
//!
//! Deserialize an object.
//! ``` rust
//! # #[macro_use] extern crate serde_derive;
//! # fn main() -> Result<(), serde_cbor::Error> {
//! #[derive(Debug, PartialEq, Deserialize)]
//! struct User {
//! user_id: u32,
//! password_hash: [u8; 4],
//! }
//!
//! let value = [
//! 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
//! 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
//! 0x68, 0x84, 0x1, 0x2, 0x3, 0x4
//! ];
//!
//! // from_slice_with_scratch will not alter input data, use it whenever you
//! // borrow from somewhere else.
//! // You will have to size your scratch according to the input data you
//! // expect.
//! use serde_cbor::de::from_slice_with_scratch;
//! let mut scratch = [0u8; 32];
//! let user: User = from_slice_with_scratch(&value[..], &mut scratch)?;
//! assert_eq!(user, User {
//! user_id: 42,
//! password_hash: [1, 2, 3, 4],
//! });
//!
//! let mut value = [
//! 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
//! 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
//! 0x68, 0x84, 0x1, 0x2, 0x3, 0x4
//! ];
//!
//! // from_mut_slice will move data around the input slice, you may only use it
//! // on data you may own or can modify.
//! use serde_cbor::de::from_mut_slice;
//! let user: User = from_mut_slice(&mut value[..])?;
//! assert_eq!(user, User {
//! user_id: 42,
//! password_hash: [1, 2, 3, 4],
//! });
//! # Ok(())
//! # }
//! ```
//!
//! # Limitations
//!
//! While Serde CBOR strives to support all features of Serde and CBOR
//! there are a few limitations.
//!
//! * [Tags] are ignored during deserialization and can't be emitted during
//! serialization. This is because Serde has no concept of tagged
//! values. See:&nbsp;[#3]
//! * Unknown [simple values] cause an `UnassignedCode` error.
//! The simple values *False* and *True* are recognized and parsed as bool.
//! *Null* and *Undefined* are both deserialized as *unit*.
//! The *unit* type is serialized as *Null*. See:&nbsp;[#86]
//! * [128-bit integers] can't be directly encoded in CBOR. If you need them
//! store them as a byte string. See:&nbsp;[#77]
//!
//! [Tags]: https://tools.ietf.org/html/rfc7049#section-2.4.4
//! [#3]: https://github.com/pyfisch/cbor/issues/3
//! [simple values]: https://tools.ietf.org/html/rfc7049#section-3.5
//! [#86]: https://github.com/pyfisch/cbor/issues/86
//! [128-bit integers]: https://doc.rust-lang.org/std/primitive.u128.html
//! [#77]: https://github.com/pyfisch/cbor/issues/77
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
// When we are running tests in no_std mode we need to explicitly link std, because `cargo test`
// will not work without it.
#[cfg(all(not(feature = "std"), test))]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod de;
pub mod error;
mod read;
pub mod ser;
pub mod tags;
mod write;
#[cfg(feature = "std")]
pub mod value;
// Re-export the [items recommended by serde](https://serde.rs/conventions.html).
#[doc(inline)]
pub use crate::de::{Deserializer, StreamDeserializer};
#[doc(inline)]
pub use crate::error::{Error, Result};
#[doc(inline)]
pub use crate::ser::Serializer;
// Convenience functions for serialization and deserialization.
// These functions are only available in `std` mode.
#[cfg(feature = "std")]
#[doc(inline)]
pub use crate::de::from_reader;
#[cfg(any(feature = "std", feature = "alloc"))]
#[doc(inline)]
pub use crate::de::from_slice;
#[cfg(any(feature = "std", feature = "alloc"))]
#[doc(inline)]
pub use crate::ser::to_vec;
#[cfg(feature = "std")]
#[doc(inline)]
pub use crate::ser::to_writer;
// Re-export the value type like serde_json
#[cfg(feature = "std")]
#[doc(inline)]
pub use crate::value::Value;