hubpack/
lib.rs

1//! A predictable serialization format for `serde`.
2//!
3//! `hubpack` is an algorithm for converting Rust values to bytes and back. It
4//! was originally designed for encoding messages sent between embedded
5//! programs. It is designed for use with `serde`.
6//!
7//! Some of the nice things about `hubpack` include:
8//!
9//! - Its encoding format is relatively compact.
10//!
11//! - Its encoding format is _predictable._ In particular, there are no
12//!   variable-length integer encodings.
13//!
14//! - Because the size is predictable, `hubpack` provides a `SerializedSize` trait.
15//!   Any type that implements `SerializedSize` can report the maximum number of
16//!   bytes necessary to encode it using `hubpack`. This means you can allocate a
17//!   fixed-size buffer without worry. (You can `#[derive(SerializedSize)]` for your
18//!   own types.)
19//!
20//! - The encode/decode implementations generate fairly small, efficient code.
21//!
22//! - The implementation uses very little `unsafe` code, only in specific cases
23//!   with a measurable performance improvement and no reasonable alternative.
24//!
25//! You might not want to use `hubpack` because of the following limitations:
26//!
27//! - `hubpack` is designed for fixed-size small data structures, and cannot encode
28//!   things like `Vec`, `str`, and maps.
29//!
30//! - `hubpack` does not support `enum` types with more than 256 variants.
31//!
32//! - `hubpack` aims for predictability over compactness, so certain types of data
33//!   -- like lots of integers whose values are small relative to their types -- can
34//!   be more compactly encoded using formats like `bincode`.
35
36#![no_std]
37
38pub mod ser;
39pub mod de;
40pub mod error;
41
42pub mod size;
43
44pub use de::deserialize;
45pub use error::{Error, Result};
46pub use ser::serialize;
47pub use size::SerializedSize;
48
49/// Derive macro for the `SerializedSize` trait.
50pub use hubpack_derive::SerializedSize;
51
52// Internal re-export to make derive macros work inside the crate.
53extern crate self as hubpack;
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    use serde::{Serialize, Deserialize};
60
61    macro_rules! round_trip {
62        ($testname:ident: $t:ty = $init:expr) => {
63            #[test]
64            fn $testname() {
65                let input: $t = $init;
66                const BUFSZ: usize = <$t as crate::SerializedSize>::MAX_SIZE;
67                const PAD: usize = 3;
68                let mut buffer: [u8; BUFSZ + PAD] = [0; BUFSZ + PAD];
69                let len = serialize(&mut buffer, &input).unwrap();
70                let (output, rest) = deserialize::<$t>(&buffer).unwrap();
71
72                assert_eq!(output, input);
73                assert_eq!(buffer.len() - rest.len(), len);
74                assert!(len <= BUFSZ,
75                    "Serialized length ({}) should be less than SerializedSize predicts ({})", len, BUFSZ);
76            }
77        }
78    }
79
80    round_trip!(rt_unit: () = ());
81
82    round_trip!(rt_u8: u8 = 42);
83    round_trip!(rt_u16: u16 = 0xDEAD);
84    round_trip!(rt_u32: u32 = 0xDEADBEEF);
85    round_trip!(rt_u64: u64 = 0xDEAD_BEEF_CAFE_D00D);
86    round_trip!(rt_u128: u128 = 0xDEAD_BEEF_CAFE_D00D_1234_5678_ABCD_EF00);
87
88    round_trip!(rt_i8: i8 = -42);
89    round_trip!(rt_i16: i16 = -0x4EAD);
90    round_trip!(rt_i32: i32 = -0x4EADBEEF);
91    round_trip!(rt_i64: i64 = -0x4EAD_BEEF_CAFE_D00D);
92    round_trip!(rt_i128: i128 = -0x4EAD_BEEF_CAFE_D00D_1234_5678_ABCD_EF00);
93
94    round_trip!(rt_f32: f32 = core::f32::consts::PI);
95    round_trip!(rt_f64: f64 = core::f64::consts::PI);
96
97    round_trip!(rt_true: bool = true);
98    round_trip!(rt_false: bool = false);
99
100    round_trip!(rt_option_u8_none: Option<u8> = None);
101    round_trip!(rt_option_u8_some: Option<u8> = Some(0xAA));
102
103    round_trip!(rt_tuple: (u8, u16, bool) = (55, 0xCAFE, false));
104
105    #[derive(Debug, Serialize, Deserialize, PartialEq, SerializedSize)]
106    struct UnitStruct;
107
108    round_trip!(rt_unit_struct: UnitStruct = UnitStruct);
109
110    #[derive(Debug, Serialize, Deserialize, PartialEq, SerializedSize)]
111    struct EmptyStruct {}
112
113    round_trip!(rt_empty_struct: EmptyStruct = EmptyStruct {});
114
115    #[derive(Debug, Serialize, Deserialize, PartialEq, SerializedSize)]
116    struct TupleStruct(u8, u32);
117
118    round_trip!(rt_tuple_struct: TupleStruct = TupleStruct(12, 345678));
119
120    #[derive(Debug, Serialize, Deserialize, PartialEq, SerializedSize)]
121    struct Struct {
122        a: Option<u16>,
123        b: i16,
124    }
125
126    round_trip!(rt_struct: Struct = Struct { a: Some(0xF00D), b: -30 });
127
128    #[derive(Debug, Serialize, Deserialize, PartialEq, SerializedSize)]
129    enum Enum {
130        Unit,
131        Tuple(u8, u16),
132        Struct {
133            a: Option<u16>,
134            b: i16,
135        },
136    }
137
138    round_trip!(rt_enum_unit: Enum = Enum::Unit);
139    round_trip!(rt_enum_tuple: Enum = Enum::Tuple(12, 3456));
140    round_trip!(rt_enum_struct: Enum = Enum::Struct { a: Some(0xF00D), b: -12 });
141
142    #[test]
143    fn whither_usize() {
144        let mut buf = [0; 8];
145        let n = ser::serialize(&mut buf, &0usize).unwrap();
146        assert_eq!(n, 8);
147    }
148}