Skip to main content

serde_cbor/
lib.rs

1//! CBOR and serialization.
2//!
3//! # Usage
4//!
5//! Serde CBOR supports Rust 1.40 and up. Add this to your `Cargo.toml`:
6//! ```toml
7//! [dependencies]
8//! serde_cbor = "0.10"
9//! ```
10//!
11//! Storing and loading Rust types is easy and requires only
12//! minimal modifications to the program code.
13//!
14//! ```rust
15//! use serde_derive::{Deserialize, Serialize};
16//! use std::error::Error;
17//! use std::fs::File;
18//!
19//! // Types annotated with `Serialize` can be stored as CBOR.
20//! // To be able to load them again add `Deserialize`.
21//! #[derive(Debug, Serialize, Deserialize)]
22//! struct Mascot {
23//!     name: String,
24//!     species: String,
25//!     year_of_birth: u32,
26//! }
27//!
28//! fn main() -> Result<(), Box<dyn Error>> {
29//!     let ferris = Mascot {
30//!         name: "Ferris".to_owned(),
31//!         species: "crab".to_owned(),
32//!         year_of_birth: 2015,
33//!     };
34//!
35//!     let ferris_file = File::create("examples/ferris.cbor")?;
36//!     // Write Ferris to the given file.
37//!     // Instead of a file you can use any type that implements `io::Write`
38//!     // like a HTTP body, database connection etc.
39//!     serde_cbor::to_writer(ferris_file, &ferris)?;
40//!
41//!     let tux_file = File::open("examples/tux.cbor")?;
42//!     // Load Tux from a file.
43//!     // Serde CBOR performs roundtrip serialization meaning that
44//!     // the data will not change in any way.
45//!     let tux: Mascot = serde_cbor::from_reader(tux_file)?;
46//!
47//!     println!("{:?}", tux);
48//!     // prints: Mascot { name: "Tux", species: "penguin", year_of_birth: 1996 }
49//!
50//!     Ok(())
51//! }
52//! ```
53//!
54//! There are a lot of options available to customize the format.
55//! To operate on untyped CBOR values have a look at the `Value` type.
56//!
57//! # Type-based Serialization and Deserialization
58//! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
59//! from CBOR via the serialization API. To be able to serialize a piece of data, it must implement
60//! the `serde::Serialize` trait. To be able to deserialize a piece of data, it must implement the
61//! `serde::Deserialize` trait. Serde provides an annotation to automatically generate the
62//! code for these traits: `#[derive(Serialize, Deserialize)]`.
63//!
64//! The CBOR API also provides an enum `serde_cbor::Value`.
65//!
66//! # Packed Encoding
67//! When serializing structs or enums in CBOR the keys or enum variant names will be serialized
68//! as string keys to a map. Especially in embedded environments this can increase the file
69//! size too much. In packed encoding all struct keys, as well as any enum variant that has no data,
70//! will be serialized as variable sized integers. The first 24 entries in any struct consume only a
71//! single byte!  Packed encoding uses serde's preferred [externally tagged enum
72//! format](https://serde.rs/enum-representations.html) and therefore serializes enum variant names
73//! as string keys when that variant contains data.  So, in the packed encoding example, `FirstVariant`
74//! encodes to a single byte, but encoding `SecondVariant` requires 16 bytes.
75//!
76//! To serialize a document in this format use `Serializer::new(writer).packed_format()` or
77//! the shorthand `ser::to_vec_packed`. The deserialization works without any changes.
78//!
79//! If you would like to omit the enum variant encoding for all variants, including ones that
80//! contain data, you can add `legacy_enums()` in addition to `packed_format()`, as can seen
81//! in the Serialize using minimal encoding example.
82//!
83//! # Self describing documents
84//! In some contexts different formats are used but there is no way to declare the format used
85//! out of band. For this reason CBOR has a magic number that may be added before any document.
86//! Self describing documents are created with `serializer.self_describe()`.
87//!
88//! # Examples
89//! Read a CBOR value that is known to be a map of string keys to string values and print it.
90//!
91//! ```rust
92//! use std::collections::BTreeMap;
93//! use serde_cbor::from_slice;
94//!
95//! let slice = b"\xa5aaaAabaBacaCadaDaeaE";
96//! let value: BTreeMap<String, String> = from_slice(slice).unwrap();
97//! println!("{:?}", value); // {"e": "E", "d": "D", "a": "A", "c": "C", "b": "B"}
98//! ```
99//!
100//! Read a general CBOR value with an unknown content.
101//!
102//! ```rust
103//! use serde_cbor::from_slice;
104//! use serde_cbor::value::Value;
105//!
106//! let slice = b"\x82\x01\xa1aaab";
107//! let value: Value = from_slice(slice).unwrap();
108//! println!("{:?}", value); // Array([U64(1), Object({String("a"): String("b")})])
109//! ```
110//!
111//! Serialize an object.
112//!
113//! ```rust
114//! use std::collections::BTreeMap;
115//! use serde_cbor::to_vec;
116//!
117//! let mut programming_languages = BTreeMap::new();
118//! programming_languages.insert("rust", vec!["safe", "concurrent", "fast"]);
119//! programming_languages.insert("python", vec!["powerful", "friendly", "open"]);
120//! programming_languages.insert("js", vec!["lightweight", "interpreted", "object-oriented"]);
121//! let encoded = to_vec(&programming_languages);
122//! assert_eq!(encoded.unwrap().len(), 103);
123//! ```
124//!
125//! Deserializing data in the middle of a slice
126//! ```
127//! # extern crate serde_cbor;
128//! use serde_cbor::Deserializer;
129//!
130//! # fn main() {
131//! let data: Vec<u8> = vec![
132//!     0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x66, 0x66, 0x6f, 0x6f, 0x62,
133//!     0x61, 0x72,
134//! ];
135//! let mut deserializer = Deserializer::from_slice(&data);
136//! let value: &str = serde::de::Deserialize::deserialize(&mut deserializer)
137//!     .unwrap();
138//! let rest = &data[deserializer.byte_offset()..];
139//! assert_eq!(value, "foobar");
140//! assert_eq!(rest, &[0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72]);
141//! # }
142//! ```
143//!
144//! Serialize using packed encoding
145//!
146//! ```rust
147//! use serde_derive::{Deserialize, Serialize};
148//! use serde_cbor::ser::to_vec_packed;
149//! use WithTwoVariants::*;
150//!
151//! #[derive(Debug, Serialize, Deserialize)]
152//! enum WithTwoVariants {
153//!     FirstVariant,
154//!     SecondVariant(u8),
155//! }
156//!
157//! let cbor = to_vec_packed(&FirstVariant).unwrap();
158//! assert_eq!(cbor.len(), 1);
159//!
160//! let cbor = to_vec_packed(&SecondVariant(0)).unwrap();
161//! assert_eq!(cbor.len(), 16); // Includes 13 bytes of "SecondVariant"
162//! ```
163//!
164//! Serialize using minimal encoding
165//!
166//! ```rust
167//! use serde_derive::{Deserialize, Serialize};
168//! use serde_cbor::{Result, Serializer, ser::{self, IoWrite}};
169//! use WithTwoVariants::*;
170//!
171//! fn to_vec_minimal<T>(value: &T) -> Result<Vec<u8>>
172//! where
173//!     T: serde::Serialize,
174//! {
175//!     let mut vec = Vec::new();
176//!     value.serialize(&mut Serializer::new(&mut IoWrite::new(&mut vec)).packed_format().legacy_enums())?;
177//!     Ok(vec)
178//! }
179//!
180//! #[derive(Debug, Serialize, Deserialize)]
181//! enum WithTwoVariants {
182//!     FirstVariant,
183//!     SecondVariant(u8),
184//! }
185//!
186//! let cbor = to_vec_minimal(&FirstVariant).unwrap();
187//! assert_eq!(cbor.len(), 1);
188//!
189//! let cbor = to_vec_minimal(&SecondVariant(0)).unwrap();
190//! assert_eq!(cbor.len(), 3);
191//! ```
192//!
193//! # `no-std` support
194//!
195//! Serde CBOR supports building in a `no_std` context, use the following lines
196//! in your `Cargo.toml` dependencies:
197//! ``` toml
198//! [dependencies]
199//! serde = { version = "1.0", default-features = false }
200//! serde_cbor = { version = "0.10", default-features = false }
201//! ```
202//!
203//! Without the `std` feature the functions [from_reader], [from_slice], [to_vec], and [to_writer]
204//! are not exported. To export [from_slice] and [to_vec] enable the `alloc` feature. The `alloc`
205//! feature uses the [`alloc` library][alloc-lib] and requires at least version 1.36.0 of Rust.
206//!
207//! [alloc-lib]: https://doc.rust-lang.org/alloc/
208//!
209//! *Note*: to use derive macros in serde you will need to declare `serde`
210//! dependency like so:
211//! ``` toml
212//! serde = { version = "1.0", default-features = false, features = ["derive"] }
213//! ```
214//!
215//! Serialize an object with `no_std` and without `alloc`.
216//! ``` rust
217//! # #[macro_use] extern crate serde_derive;
218//! # fn main() -> Result<(), serde_cbor::Error> {
219//! use serde::Serialize;
220//! use serde_cbor::Serializer;
221//! use serde_cbor::ser::SliceWrite;
222//!
223//! #[derive(Serialize)]
224//! struct User {
225//!     user_id: u32,
226//!     password_hash: [u8; 4],
227//! }
228//!
229//! let mut buf = [0u8; 100];
230//! let writer = SliceWrite::new(&mut buf[..]);
231//! let mut ser = Serializer::new(writer);
232//! let user = User {
233//!     user_id: 42,
234//!     password_hash: [1, 2, 3, 4],
235//! };
236//! user.serialize(&mut ser)?;
237//! let writer = ser.into_inner();
238//! let size = writer.bytes_written();
239//! let expected = [
240//!     0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
241//!     0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
242//!     0x68, 0x84, 0x1, 0x2, 0x3, 0x4
243//! ];
244//! assert_eq!(&buf[..size], expected);
245//! # Ok(())
246//! # }
247//! ```
248//!
249//! Deserialize an object.
250//! ``` rust
251//! # #[macro_use] extern crate serde_derive;
252//! # fn main() -> Result<(), serde_cbor::Error> {
253//! #[derive(Debug, PartialEq, Deserialize)]
254//! struct User {
255//!     user_id: u32,
256//!     password_hash: [u8; 4],
257//! }
258//!
259//! let value = [
260//!     0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
261//!     0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
262//!     0x68, 0x84, 0x1, 0x2, 0x3, 0x4
263//! ];
264//!
265//! // from_slice_with_scratch will not alter input data, use it whenever you
266//! // borrow from somewhere else.
267//! // You will have to size your scratch according to the input data you
268//! // expect.
269//! use serde_cbor::de::from_slice_with_scratch;
270//! let mut scratch = [0u8; 32];
271//! let user: User = from_slice_with_scratch(&value[..], &mut scratch)?;
272//! assert_eq!(user, User {
273//!     user_id: 42,
274//!     password_hash: [1, 2, 3, 4],
275//! });
276//!
277//! let mut value = [
278//!     0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
279//!     0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
280//!     0x68, 0x84, 0x1, 0x2, 0x3, 0x4
281//! ];
282//!
283//! // from_mut_slice will move data around the input slice, you may only use it
284//! // on data you may own or can modify.
285//! use serde_cbor::de::from_mut_slice;
286//! let user: User = from_mut_slice(&mut value[..])?;
287//! assert_eq!(user, User {
288//!     user_id: 42,
289//!     password_hash: [1, 2, 3, 4],
290//! });
291//! # Ok(())
292//! # }
293//! ```
294//!
295//! # Limitations
296//!
297//! While Serde CBOR strives to support all features of Serde and CBOR
298//! there are a few limitations.
299//!
300//! * [Tags] are ignored during deserialization and can't be emitted during
301//!     serialization. This is because Serde has no concept of tagged
302//!     values. See:&nbsp;[#3]
303//! * Unknown [simple values] cause an `UnassignedCode` error.
304//!     The simple values *False* and *True* are recognized and parsed as bool.
305//!     *Null* and *Undefined* are both deserialized as *unit*.
306//!     The *unit* type is serialized as *Null*. See:&nbsp;[#86]
307//! * [128-bit integers] can't be directly encoded in CBOR. If you need them
308//!     store them as a byte string. See:&nbsp;[#77]
309//!
310//! [Tags]: https://tools.ietf.org/html/rfc7049#section-2.4.4
311//! [#3]: https://github.com/pyfisch/cbor/issues/3
312//! [simple values]: https://tools.ietf.org/html/rfc7049#section-3.5
313//! [#86]: https://github.com/pyfisch/cbor/issues/86
314//! [128-bit integers]: https://doc.rust-lang.org/std/primitive.u128.html
315//! [#77]: https://github.com/pyfisch/cbor/issues/77
316
317#![deny(missing_docs)]
318#![cfg_attr(not(feature = "std"), no_std)]
319
320// When we are running tests in no_std mode we need to explicitly link std, because `cargo test`
321// will not work without it.
322#[cfg(all(not(feature = "std"), test))]
323extern crate std;
324
325#[cfg(feature = "alloc")]
326extern crate alloc;
327
328pub mod de;
329pub mod error;
330mod read;
331pub mod ser;
332pub mod tags;
333mod write;
334
335#[cfg(feature = "std")]
336pub mod value;
337
338// Re-export the [items recommended by serde](https://serde.rs/conventions.html).
339#[doc(inline)]
340pub use crate::de::{Deserializer, StreamDeserializer};
341
342#[doc(inline)]
343pub use crate::error::{Error, Result};
344
345#[doc(inline)]
346pub use crate::ser::Serializer;
347
348// Convenience functions for serialization and deserialization.
349// These functions are only available in `std` mode.
350#[cfg(feature = "std")]
351#[doc(inline)]
352pub use crate::de::from_reader;
353
354#[cfg(any(feature = "std", feature = "alloc"))]
355#[doc(inline)]
356pub use crate::de::from_slice;
357
358#[cfg(any(feature = "std", feature = "alloc"))]
359#[doc(inline)]
360pub use crate::ser::to_vec;
361
362#[cfg(feature = "std")]
363#[doc(inline)]
364pub use crate::ser::to_writer;
365
366// Re-export the value type like serde_json
367#[cfg(feature = "std")]
368#[doc(inline)]
369pub use crate::value::Value;