vector_buffers/variants/disk_v2/
backed_archive.rs

1use std::marker::PhantomData;
2#[cfg(test)]
3use std::pin::Pin;
4
5use bytecheck::CheckBytes;
6use rkyv::{
7    Archive, Serialize, archived_root, check_archived_root,
8    ser::{Serializer, serializers::AllocSerializer},
9    validation::validators::DefaultValidator,
10};
11
12use super::ser::{DeserializeError, SerializeError};
13
14/// A batteries-included serializer implementation.
15///
16/// Callers do not need to know or care about this, but it must be public as it is part of the
17/// `BackedArchive` API.
18pub type DefaultSerializer = AllocSerializer<4096>;
19
20/// Backed wrapper for any type that implements [`Archive`][archive].
21///
22/// For any backing store that can provide references to an underlying byte slice of suitable size,
23/// we can deserialize and serialize a type that is archivable. `BackedArchive` provides specific
24/// entrypoints to either deserialize the given type from the backing store, or to serialize a
25/// provided value to the backing store.
26///
27/// Once wrapped, the archived type can be accessed either immutably or mutably.  This provides a
28/// simple mechanism to use a variety of backing stores, such as `Vec<u8>` or a memory-mapped
29/// region.  This can in turn be used to avoid serializing to intermediate buffers when possible.
30///
31/// ## Archived types
32///
33/// Traditionally, (de)serialization frameworks focus on taking some type `T`, and translating it to
34/// and from another format: structured text like JSON, or maybe binary data for efficient
35/// on-the-wire representation, like Protocol Buffers.  `rkyv` works slightly differently, as a
36/// zero-copy (de)serialization framework, by providing a projected type, or "archive", over the
37/// underlying byte representation of `T`.
38///
39/// In general, what this means is that when you derive the correct traits for some type `T`, `rkyv`
40/// generates an `ArchivedT` type that can correctly represent `T` when serialized to disk,
41/// regardless of whether `T` contains primitive types or types holding underlying allocations, like
42/// `Vec<T>`.
43///
44/// Crucially, the archive type -- `ArchivedT` -- can be pointer casted against the underlying bytes
45/// to provide a reference of `ArchivedT`, or even a mutable reference.  This means that we can
46/// access an object that is like our `T`, in a native and ergonomic way, without copying any bytes,
47/// thus zero-copy deserialization.  Building off the ability to get a mutable reference, we can
48/// also expose way to trivially update the underlying bytes through a safe interface, which can
49/// avoid constantly serializing the entire type after changing a single field.
50///
51/// [archive]: rkyv::Archive
52#[derive(Debug)]
53pub struct BackedArchive<B, T> {
54    backing: B,
55    _archive: PhantomData<T>,
56}
57
58impl<B, T> BackedArchive<B, T>
59where
60    B: AsRef<[u8]>,
61    T: Archive,
62{
63    /// Deserializes the archived value from the backing store and wraps it.
64    ///
65    /// # Errors
66    ///
67    /// If the data in the backing store is not valid for `T`, an error variant will be returned.
68    pub fn from_backing(backing: B) -> Result<BackedArchive<B, T>, DeserializeError>
69    where
70        for<'a> T::Archived: CheckBytes<DefaultValidator<'a>>,
71    {
72        // Validate that the input is, well, valid.
73        _ = check_archived_root::<T>(backing.as_ref())?;
74
75        // Now that we know the buffer fits T, we're good to go!
76        Ok(Self {
77            backing,
78            _archive: PhantomData,
79        })
80    }
81
82    /// Gets a reference to the backing store.
83    pub fn get_backing_ref(&self) -> &B {
84        &self.backing
85    }
86
87    /// Gets a reference to the archived value.
88    pub fn get_archive_ref(&self) -> &T::Archived {
89        unsafe { archived_root::<T>(self.backing.as_ref()) }
90    }
91}
92
93impl<B, T> BackedArchive<B, T>
94where
95    B: AsMut<[u8]>,
96    T: Archive,
97{
98    /// Serializes the provided value to the backing store and wraps it.
99    ///
100    /// # Errors
101    ///
102    /// If there is an error during serializing of the value, an error variant will be returned that
103    /// describes the error.  If the backing store is too small to hold the serialized version of
104    /// the value, an error variant will be returned defining the minimum size the backing store
105    /// must be, as well containing the value that failed to get serialized.
106    pub fn from_value(mut backing: B, value: T) -> Result<BackedArchive<B, T>, SerializeError<T>>
107    where
108        T: Serialize<DefaultSerializer>,
109    {
110        // Serialize our value so we can shove it into the backing.
111        let mut serializer = DefaultSerializer::default();
112        _ = serializer
113            .serialize_value(&value)
114            .map_err(|e| SerializeError::FailedToSerialize(e.to_string()))?;
115
116        let src_buf = serializer.into_serializer().into_inner();
117
118        // Now we have to write the serialized version to the backing store.  For obvious reasons,
119        // the backing store needs to be able to hold the entire serialized representation, so we
120        // check for that.  As well, instead of using `archived_root_mut`, we use
121        // `archived_value_mut`, because this lets us relax need the backing store to be sized
122        // _identically_ to the serialized size.
123        let dst_buf = backing.as_mut();
124        if dst_buf.len() < src_buf.len() {
125            return Err(SerializeError::BackingStoreTooSmall(value, src_buf.len()));
126        }
127
128        dst_buf[..src_buf.len()].copy_from_slice(&src_buf);
129
130        Ok(Self {
131            backing,
132            _archive: PhantomData,
133        })
134    }
135
136    /// Gets a reference to the archived value.
137    #[cfg(test)]
138    pub fn get_archive_mut(&mut self) -> Pin<&mut T::Archived> {
139        use rkyv::archived_root_mut;
140
141        let pinned = Pin::new(self.backing.as_mut());
142        unsafe { archived_root_mut::<T>(pinned) }
143    }
144}