Skip to main content

fxfs/
zerocopy_serialization.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Efficient serialization and deserialization for `Vec<T>` and `Box<[T]>` where `T` supports
6//! zerocopy.
7
8use serde::{Deserializer, Serializer};
9use std::marker::PhantomData;
10use zerocopy::{FromBytes, Immutable, IntoBytes};
11
12// Only little endian is supported.
13static_assertions::assert_cfg!(target_endian = "little");
14/// A trait for container types that can be efficiently serialized as a byte slice and
15/// reconstructed from a `Vec` of their elements.
16pub trait SerializeAsBytes {
17    type Inner: FromBytes + Immutable + Copy;
18
19    /// Returns a byte slice that represents the container.
20    fn as_bytes(&self) -> &[u8];
21
22    /// Constructs a container from a `Vec` of its elements.
23    fn from_vec(slice: Vec<Self::Inner>) -> Self;
24}
25
26impl<T: FromBytes + IntoBytes + Immutable + Copy> SerializeAsBytes for Vec<T> {
27    type Inner = T;
28
29    fn as_bytes(&self) -> &[u8] {
30        self.as_slice().as_bytes()
31    }
32
33    fn from_vec(slice: Vec<T>) -> Self {
34        slice
35    }
36}
37
38impl<T: FromBytes + IntoBytes + Immutable + Copy> SerializeAsBytes for Box<[T]> {
39    type Inner = T;
40
41    fn as_bytes(&self) -> &[u8] {
42        (&**self).as_bytes()
43    }
44
45    fn from_vec(slice: Vec<T>) -> Self {
46        slice.into_boxed_slice()
47    }
48}
49
50/// Serializes a container type as a byte slice.
51pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
52where
53    T: SerializeAsBytes,
54    S: Serializer,
55{
56    serializer.serialize_bytes(value.as_bytes())
57}
58
59/// Deserializes a container type from a byte slice.
60pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
61where
62    T: SerializeAsBytes,
63    D: Deserializer<'de>,
64{
65    // We shouldn't be deserializing zero-sized types.
66    debug_assert!(size_of::<T::Inner>() > 0);
67
68    // Bincode reads the bytes into a `Vec<u8>`. If the type we're deserializing has the same
69    // alignment as `u8` then it's possible to take the `Vec<u8>` instead of copying the data.
70    if align_of::<T::Inner>() == align_of::<u8>() {
71        // Calls `visit_byte_buf`.
72        deserializer.deserialize_byte_buf(Visitor(PhantomData::<T>))
73    } else {
74        // Calls `visit_bytes`.
75        deserializer.deserialize_bytes(Visitor(PhantomData::<T>))
76    }
77}
78
79/// A visitor for deserializing a container type from a byte slice.
80struct Visitor<T>(PhantomData<T>);
81impl<'de, T> serde::de::Visitor<'de> for Visitor<T>
82where
83    T: SerializeAsBytes,
84{
85    type Value = T;
86
87    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(
89            formatter,
90            "a byte array with a length that is a multiple of {}",
91            size_of::<T::Inner>()
92        )
93    }
94
95    fn visit_bytes<E>(self, bytes: &[u8]) -> Result<Self::Value, E>
96    where
97        E: serde::de::Error,
98    {
99        if !bytes.len().is_multiple_of(size_of::<T::Inner>()) {
100            return Err(E::custom(
101                "input bytes are not a multiple of the size of the desired type",
102            ));
103        }
104        let elements = bytes.len() / size_of::<T::Inner>();
105        let mut vec: Vec<T::Inner> = Vec::with_capacity(elements);
106        let dst = vec.spare_capacity_mut();
107        unsafe {
108            // SAFETY:
109            //   - Both `bytes` and `dst` are nonnull, aligned, and nonoverlapping.
110            //   - `bytes` is valid for reading `bytes.len()` bytes.
111            //   - `dst` is valid for writing `bytes.len()` bytes.
112            //   - `T::Inner` implements `FromBytes` which means that any bit pattern is valid and
113            //     it's safe to initialize the elements this way.
114            std::ptr::copy_nonoverlapping(
115                bytes.as_ptr(),
116                dst.as_mut_ptr().cast::<u8>(),
117                bytes.len(),
118            );
119            // SAFETY: All of the elements were initialized.
120            vec.set_len(elements);
121        }
122        Ok(T::from_vec(vec))
123    }
124
125    fn visit_byte_buf<E>(self, bytes: Vec<u8>) -> Result<Self::Value, E>
126    where
127        E: serde::de::Error,
128    {
129        // This method should only be invoked from `deserialize` when the inner type has `u8`
130        // alignment.
131        debug_assert!(align_of::<T::Inner>() == align_of::<u8>());
132        // We shouldn't be deserializing zero-sized types.
133        debug_assert!(size_of::<T::Inner>() > 0);
134
135        if !bytes.len().is_multiple_of(size_of::<T::Inner>()) {
136            return Err(E::custom(
137                "input bytes are not a multiple of the size of the desired type",
138            ));
139        }
140        let elements = bytes.len() / size_of::<T::Inner>();
141
142        // Both the size and capacity of `bytes` must be a multiple of the size of `T::Inner` to be
143        // able to change the type of the `Vec`.
144        let vec = if !bytes.capacity().is_multiple_of(size_of::<T::Inner>()) {
145            // Calling `Vec::into_boxed_slice` will realloc the allocation to drop the excess
146            // capacity. If we're lucky, the new and old capacity will be in the same allocator
147            // bucket and no reallocation will happen.
148            let ptr = Box::into_raw(bytes.into_boxed_slice());
149            // SAFETY:
150            //   - All of the requirements for `Vec::from_raw_parts` are upheld:
151            //     - Fxfs only uses the global allocator.
152            //     - `u8` and `T::Inner` have the same alignment.
153            //     - The size of the allocation is `size_of::<T::Inner>() * elements` because of the
154            //       `Vec::into_boxed_slice` call.
155            //     - All of the elements are initialized.
156            //   - The pointer cast is safe because `T::Inner` implements `FromBytes`.
157            unsafe { Vec::from_raw_parts(ptr.cast::<T::Inner>(), elements, elements) }
158        } else {
159            let (ptr, _size, capacity) = bytes.into_raw_parts();
160            let capacity = capacity / size_of::<T::Inner>();
161            // SAFETY:
162            //   - All of the requirements for `Vec::from_raw_parts` are upheld:
163            //     - Fxfs only uses the global allocator.
164            //     - `u8` and `T::Inner` have the same alignment.
165            //     - `size_of::<T::Inner>() * capacity` is the same as `bytes.capacity()`.
166            //     - All of the elements are initialized.
167            //   - The pointer cast is safe because `T::Inner` implements `FromBytes`.
168            unsafe { Vec::from_raw_parts(ptr.cast::<T::Inner>(), elements, capacity) }
169        };
170        Ok(T::from_vec(vec))
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use crate::serialized_types::{LATEST_VERSION, Versioned};
177    use serde::{Deserialize, Serialize};
178
179    #[fuchsia::test]
180    fn test_boxed_slice_of_u8_is_the_same() {
181        #[derive(Serialize, Deserialize, Versioned)]
182        struct Regular(Box<[u8]>);
183        #[derive(Serialize, Deserialize, Versioned, PartialEq, Eq, Debug)]
184        struct Optimized(#[serde(with = "crate::zerocopy_serialization")] Box<[u8]>);
185
186        let regular = Regular(vec![0, 1, 2, 3, 254, 255].into_boxed_slice());
187        let mut regular_serialized = Vec::new();
188        regular.serialize_into(&mut regular_serialized).unwrap();
189
190        let optimized = Optimized(regular.0);
191        let mut optimized_serialized = Vec::new();
192        optimized.serialize_into(&mut optimized_serialized).unwrap();
193
194        assert_eq!(regular_serialized, optimized_serialized);
195
196        assert_eq!(
197            Optimized::deserialize_from(&mut optimized_serialized.as_slice(), LATEST_VERSION)
198                .unwrap(),
199            optimized
200        );
201    }
202
203    #[fuchsia::test]
204    fn test_vec_of_u8_is_the_same() {
205        #[derive(Serialize, Deserialize, Versioned)]
206        struct Regular(Vec<u8>);
207        #[derive(Serialize, Deserialize, Versioned, PartialEq, Eq, Debug)]
208        struct Optimized(#[serde(with = "crate::zerocopy_serialization")] Vec<u8>);
209
210        let regular = Regular(vec![0, 1, 2, 3, 254, 255]);
211        let mut regular_serialized = Vec::new();
212        regular.serialize_into(&mut regular_serialized).unwrap();
213
214        let optimized = Optimized(regular.0);
215        let mut optimized_serialized = Vec::new();
216        optimized.serialize_into(&mut optimized_serialized).unwrap();
217
218        assert_eq!(regular_serialized, optimized_serialized);
219
220        assert_eq!(
221            Optimized::deserialize_from(&mut optimized_serialized.as_slice(), LATEST_VERSION)
222                .unwrap(),
223            optimized
224        );
225    }
226
227    #[fuchsia::test]
228    fn test_boxed_slice_of_array_of_u8() {
229        #[derive(Serialize, Deserialize, Versioned)]
230        struct Regular(Box<[[u8; 4]]>);
231        #[derive(Serialize, Deserialize, Versioned, PartialEq, Eq, Debug)]
232        struct Optimized(#[serde(with = "crate::zerocopy_serialization")] Box<[[u8; 4]]>);
233
234        let regular = Regular(vec![[0, 1, 2, 3], [252, 253, 254, 255]].into_boxed_slice());
235        let mut regular_serialized = Vec::new();
236        regular.serialize_into(&mut regular_serialized).unwrap();
237
238        let optimized = Optimized(regular.0);
239        let mut optimized_serialized = Vec::new();
240        optimized.serialize_into(&mut optimized_serialized).unwrap();
241
242        // The serialized data is the same, but the number of elements is different.
243        assert_eq!(&regular_serialized[1..], &optimized_serialized[1..]);
244        assert_eq!(regular_serialized[0], 2);
245        assert_eq!(optimized_serialized[0], 8);
246
247        assert_eq!(
248            Optimized::deserialize_from(&mut optimized_serialized.as_slice(), LATEST_VERSION)
249                .unwrap(),
250            optimized
251        );
252    }
253
254    #[fuchsia::test]
255    fn test_vec_of_array_of_u8() {
256        #[derive(Serialize, Deserialize, Versioned)]
257        struct Regular(Vec<[u8; 4]>);
258        #[derive(Serialize, Deserialize, Versioned, PartialEq, Eq, Debug)]
259        struct Optimized(#[serde(with = "crate::zerocopy_serialization")] Vec<[u8; 4]>);
260
261        let regular = Regular(vec![[0, 1, 2, 3], [252, 253, 254, 255]]);
262        let mut regular_serialized = Vec::new();
263        regular.serialize_into(&mut regular_serialized).unwrap();
264
265        let optimized = Optimized(regular.0);
266        let mut optimized_serialized = Vec::new();
267        optimized.serialize_into(&mut optimized_serialized).unwrap();
268
269        // The serialized data is the same, but the number of elements is different.
270        assert_eq!(&regular_serialized[1..], &optimized_serialized[1..]);
271        assert_eq!(regular_serialized[0], 2);
272        assert_eq!(optimized_serialized[0], 8);
273
274        assert_eq!(
275            Optimized::deserialize_from(&mut optimized_serialized.as_slice(), LATEST_VERSION)
276                .unwrap(),
277            optimized
278        );
279    }
280
281    #[fuchsia::test]
282    fn test_vec_of_u64_round_trip() {
283        #[derive(Serialize, Deserialize, Versioned, PartialEq, Eq, Debug)]
284        struct Optimized(#[serde(with = "crate::zerocopy_serialization")] Vec<u64>);
285
286        let optimized =
287            Optimized(vec![0, 1, u32::MAX as u64, u32::MAX as u64 + 1, u64::MAX - 1, u64::MAX]);
288        let mut optimized_serialized = Vec::new();
289        optimized.serialize_into(&mut optimized_serialized).unwrap();
290
291        // 1 byte varint encoded length + 6 * 8 bytes per u64.
292        assert_eq!(optimized_serialized.len(), 1 + 6 * 8);
293
294        assert_eq!(
295            Optimized::deserialize_from(&mut optimized_serialized.as_slice(), LATEST_VERSION)
296                .unwrap(),
297            optimized
298        );
299    }
300
301    #[fuchsia::test]
302    fn test_empty_vec() {
303        #[derive(Serialize, Deserialize, Versioned, PartialEq, Eq, Debug)]
304        struct OptimizedVec(#[serde(with = "crate::zerocopy_serialization")] Vec<u8>);
305        let optimized = OptimizedVec(Vec::new());
306        let mut buf = Vec::new();
307        optimized.serialize_into(&mut buf).unwrap();
308        assert_eq!(
309            OptimizedVec::deserialize_from(&mut buf.as_slice(), LATEST_VERSION).unwrap(),
310            optimized
311        );
312    }
313
314    #[fuchsia::test]
315    fn test_visit_byte_buf_capacity_mismatch() {
316        // Create a Vec<u8> with capacity that is not a multiple of 4.
317        let mut bytes = Vec::with_capacity(11);
318        bytes.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
319        assert_eq!(bytes.len(), 8);
320        assert!(bytes.capacity() >= 11);
321        assert!(!bytes.capacity().is_multiple_of(size_of::<u32>()));
322
323        // [u8; 4] has size 4, alignment 1.
324        let visitor = super::Visitor(std::marker::PhantomData::<Vec<[u8; 4]>>);
325        let result: Vec<[u8; 4]> =
326            serde::de::Visitor::visit_byte_buf::<serde::de::value::Error>(visitor, bytes)
327                .expect("visit_byte_buf failed");
328        assert_eq!(&result, &[[1, 2, 3, 4], [5, 6, 7, 8]]);
329    }
330
331    #[fuchsia::test]
332    fn test_visit_byte_buf_invalid_length() {
333        let bytes = vec![1, 2, 3, 4, 5]; // Length 5 is not a multiple of 4.
334        let visitor = super::Visitor(std::marker::PhantomData::<Vec<[u8; 4]>>);
335        let result: Result<_, serde::de::value::Error> =
336            serde::de::Visitor::visit_byte_buf(visitor, bytes);
337        assert!(result.is_err());
338    }
339
340    #[fuchsia::test]
341    fn test_visit_bytes_invalid_length() {
342        let bytes = vec![1, 2, 3, 4, 5]; // Length 5 is not a multiple of 2.
343        let visitor = super::Visitor(std::marker::PhantomData::<Vec<u16>>);
344        let result: Result<_, serde::de::value::Error> =
345            serde::de::Visitor::visit_bytes(visitor, &bytes);
346        assert!(result.is_err());
347    }
348
349    #[fuchsia::test]
350    fn test_visit_bytes_with_bad_alignment() {
351        const E1: u64 = 0x0123456789ABCDEF;
352        const E2: u64 = u64::MAX;
353        // Scudo guarantees 16 byte alignment for allocations. Extra bytes are added at the front to
354        // force the u64s to be misaligned.
355        let mut bytes: Vec<u8> = vec![0, 1, 2];
356        bytes.extend_from_slice(&E1.to_le_bytes());
357        bytes.extend_from_slice(&E2.to_le_bytes());
358        assert!(!(&bytes[3..]).as_ptr().cast::<u64>().is_aligned());
359        let visitor = super::Visitor(std::marker::PhantomData::<Vec<u64>>);
360        let result: Vec<u64> =
361            serde::de::Visitor::visit_bytes::<serde::de::value::Error>(visitor, &bytes[3..])
362                .expect("visit_bytes failed");
363        assert_eq!(&result, &[E1, E2]);
364    }
365
366    #[fuchsia::test]
367    fn test_visit_bytes_with_empty_slice() {
368        let visitor = super::Visitor(std::marker::PhantomData::<Vec<u64>>);
369        let result: Vec<u64> =
370            serde::de::Visitor::visit_bytes::<serde::de::value::Error>(visitor, &[])
371                .expect("visit_bytes failed");
372        assert!(result.is_empty());
373    }
374}