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}