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