l2r0_small_serde/
lib.rs

1// Copyright 2024 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Serialization and deserialization tools for the RISC Zero zkVM
16//!
17//! Data needs to be serialized for transmission between the zkVM host and
18//! guest. This module contains tools for this serialization and the
19//! corresponding deserialization.
20//!
21//! On the host side, a serialization function such as [to_vec] should be used
22//! when transmitting data to the guest. Similarly, the deserialization function
23//! [from_slice] should be used when reading data from the guest. For example:
24//! ```rust
25//! use l2r0_small_serde::{from_slice_compact, to_vec_compact};
26//! let input = 42_u32;
27//! let encoded = to_vec_compact(&[input]).unwrap();
28//! let output: u32 = from_slice_compact(&encoded).unwrap();
29//! assert_eq!(input, output);
30//! ```
31//!
32//! On the guest side, the necessary (de)serialization functionality is
33//! included in [`env`] module functions such as [`env::read`] and
34//! [`env::commit`], so this crate rarely needs to be directly used in the
35//! guest.
36//!
37//! [`env`]: ../guest/env/index.html
38//! [`env::commit`]: ../guest/env/fn.commit.html
39//! [`env::read`]: ../guest/env/fn.read.html
40
41extern crate alloc;
42
43/// Align the given address `addr` upwards to alignment `align`.
44///
45/// Requires that `align` is a power of two.
46pub const fn align_up(addr: usize, align: usize) -> usize {
47    (addr + align - 1) & !(align - 1)
48}
49
50mod deserializer;
51mod err;
52mod serializer;
53
54pub use deserializer::{from_slice_compact, Deserializer};
55pub use err::{Error, Result};
56pub use serializer::{to_vec_compact, to_vec_compact_with_capacity, Serializer};
57
58#[cfg(test)]
59mod tests {
60    use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
61    use serde::{Deserialize, Serialize};
62
63    use crate::{from_slice_compact, to_vec_compact};
64
65    #[test]
66    fn test_vec_round_trip() {
67        let input: Vec<u64> = vec![1, 2, 3];
68        let data = to_vec_compact(&input).unwrap();
69        let output: Vec<u64> = from_slice_compact(data.as_slice()).unwrap();
70        assert_eq!(input, output);
71    }
72
73    #[test]
74    fn test_map_round_trip() {
75        let input: BTreeMap<String, u32> =
76            BTreeMap::from([("foo".into(), 1), ("bar".into(), 2), ("baz".into(), 3)]);
77        let data = to_vec_compact(&input).unwrap();
78        let output: BTreeMap<String, u32> = from_slice_compact(data.as_slice()).unwrap();
79        assert_eq!(input, output);
80    }
81
82    #[test]
83    fn test_tuple_round_trip() {
84        let input: (u32, u64) = (1, 2);
85        let data = to_vec_compact(&input).unwrap();
86        let output: (u32, u64) = from_slice_compact(data.as_slice()).unwrap();
87        assert_eq!(input, output);
88    }
89
90    #[test]
91    fn test_mixed_tuple() {
92        type TestType = (Vec<u8>, u32, u8, u8, Vec<u8>, u8);
93        let input: TestType = (vec![0u8, 1], 8u32, 7u8, 222u8, vec![1, 1, 1, 1, 1], 5u8);
94        let data = to_vec_compact(&input).unwrap();
95        assert_eq!([2, 256, 8, 56839, 5, 16843009, 1281].as_slice(), data);
96        let output: TestType = from_slice_compact(&data).unwrap();
97        assert_eq!(input, output);
98    }
99
100    #[test]
101    fn test_mixed_struct() {
102        #[derive(Debug, Serialize, PartialEq, Eq, Deserialize)]
103        struct WrappedU8(pub u8);
104
105        #[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
106        struct TestType {
107            pub wrapped_u8_seq: Vec<WrappedU8>,
108            pub u16_seq: Vec<u16>,
109            pub u32_seq: Vec<u32>,
110            pub u64_seq: Vec<u64>,
111            pub i8_seq: Vec<i8>,
112            pub i16_seq: Vec<i16>,
113            pub i32_seq: Vec<i32>,
114            pub i64_seq: Vec<i64>,
115            pub u8: u8,
116            pub bool: bool,
117            pub some_u16: Option<u16>,
118            pub none_u32: Option<u32>,
119            pub string_seq: Vec<u8>,
120            pub string_seq_seq: Vec<Vec<u8>>,
121        }
122
123        let mut input = TestType::default();
124        input.wrapped_u8_seq = vec![WrappedU8(1u8), WrappedU8(231u8), WrappedU8(123u8)];
125        input.u16_seq = vec![124u16, 41374u16];
126        input.u32_seq = vec![14710471u32, 3590275702u32, 1u32, 2u32];
127        input.u64_seq = vec![352905235952532u64, 2147102974910410u64];
128        input.i8_seq = vec![-1i8, 120i8, -22i8];
129        input.i16_seq = vec![-7932i16];
130        input.i32_seq = vec![-4327i32, 35207277i32];
131        input.i64_seq = vec![-1i64, 1i64];
132        input.u8 = 3u8;
133        input.bool = true;
134        input.some_u16 = Some(5u16);
135        input.none_u32 = None;
136        input.string_seq = b"Here is a string.".to_vec();
137        input.string_seq_seq = vec![b"string a".to_vec(), b"34720471290497230".to_vec()];
138
139        let data = to_vec_compact(&input).unwrap();
140        assert_eq!(
141            [
142                3u32, 8120065, 2, 124, 41374, 4, 14710471, 3590275702, 1, 2, 2, 658142100, 82167,
143                1578999754, 499911, 3, 4294967295, 120, 4294967274, 1, 4294959364, 2, 4294962969,
144                35207277, 2, 4294967295, 4294967295, 1, 0, 259, 1, 5, 0, 17, 1701995848, 544434464,
145                1953701985, 1735289202, 46, 2, 8, 1769108595, 1629513582, 17, 842478643, 825701424,
146                875575602, 858928953, 48,
147            ]
148            .as_slice(),
149            data
150        );
151
152        let output: TestType = from_slice_compact(&data).unwrap();
153        assert_eq!(input, output);
154    }
155
156    #[test]
157    fn test_edge_cases() {
158        #[derive(PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
159        struct U8SeqThenU32(Vec<u8>, u32);
160        #[derive(PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
161        struct U32ThenU8SeqT(u32, Vec<u8>);
162
163        for i in 0..=5 {
164            let mut input = U8SeqThenU32::default();
165            input.0 = vec![127u8; i];
166            let data = to_vec_compact(&input).unwrap();
167            let output: U8SeqThenU32 = from_slice_compact(&data).unwrap();
168            assert_eq!(input, output);
169        }
170
171        for i in 0..=5 {
172            let mut input = U32ThenU8SeqT::default();
173            input.1 = vec![127u8; i];
174            let data = to_vec_compact(&input).unwrap();
175            let output: U32ThenU8SeqT = from_slice_compact(&data).unwrap();
176            assert_eq!(input, output);
177        }
178    }
179}