1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::marker::PhantomData;
#[cfg(test)]
use std::pin::Pin;

use bytecheck::CheckBytes;
use rkyv::{
    archived_root, check_archived_root,
    ser::{serializers::AllocSerializer, Serializer},
    validation::validators::DefaultValidator,
    Archive, Serialize,
};

use super::ser::{DeserializeError, SerializeError};

/// A batteries-included serializer implementation.
///
/// Callers do not need to know or care about this, but it must be public as it is part of the
/// `BackedArchive` API.
pub type DefaultSerializer = AllocSerializer<4096>;

/// Backed wrapper for any type that implements [`Archive`][archive].
///
/// For any backing store that can provide references to an underlying byte slice of suitable size,
/// we can deserialize and serialize a type that is archivable. `BackedArchive` provides specific
/// entrypoints to either deserialize the given type from the backing store, or to serialize a
/// provided value to the backing store.
///
/// Once wrapped, the archived type can be accessed either immutably or mutably.  This provides a
/// simple mechanism to use a variety of backing stores, such as `Vec<u8>` or a memory-mapped
/// region.  This can in turn be used to avoid serializing to intermediate buffers when possible.
///
/// ## Archived types
///
/// Traditionally, (de)serialization frameworks focus on taking some type `T`, and translating it to
/// and from another format: structured text like JSON, or maybe binary data for efficient
/// on-the-wire representation, like Protocol Buffers.  `rkyv` works slightly differently, as a
/// zero-copy (de)serialization framework, by providing a projected type, or "archive", over the
/// underlying byte representation of `T`.
///
/// In general, what this means is that when you derive the correct traits for some type `T`, `rkyv`
/// generates an `ArchivedT` type that can correctly represent `T` when serialized to disk,
/// regardless of whether `T` contains primitive types or types holding underlying allocations, like
/// `Vec<T>`.
///
/// Crucially, the archive type -- `ArchivedT` -- can be pointer casted against the underlying bytes
/// to provide a reference of `ArchivedT`, or even a mutable reference.  This means that we can
/// access an object that is like our `T`, in a native and ergonomic way, without copying any bytes,
/// thus zero-copy deserialization.  Building off the ability to get a mutable reference, we can
/// also expose way to trivially update the underlying bytes through a safe interface, which can
/// avoid constantly serializing the entire type after changing a single field.
///
/// [archive]: rkyv::Archive
#[derive(Debug)]
pub struct BackedArchive<B, T> {
    backing: B,
    _archive: PhantomData<T>,
}

impl<B, T> BackedArchive<B, T>
where
    B: AsRef<[u8]>,
    T: Archive,
{
    /// Deserializes the archived value from the backing store and wraps it.
    ///
    /// # Errors
    ///
    /// If the data in the backing store is not valid for `T`, an error variant will be returned.
    pub fn from_backing(backing: B) -> Result<BackedArchive<B, T>, DeserializeError>
    where
        for<'a> T::Archived: CheckBytes<DefaultValidator<'a>>,
    {
        // Validate that the input is, well, valid.
        _ = check_archived_root::<T>(backing.as_ref())?;

        // Now that we know the buffer fits T, we're good to go!
        Ok(Self {
            backing,
            _archive: PhantomData,
        })
    }

    /// Gets a reference to the backing store.
    pub fn get_backing_ref(&self) -> &B {
        &self.backing
    }

    /// Gets a reference to the archived value.
    pub fn get_archive_ref(&self) -> &T::Archived {
        unsafe { archived_root::<T>(self.backing.as_ref()) }
    }
}

impl<B, T> BackedArchive<B, T>
where
    B: AsMut<[u8]>,
    T: Archive,
{
    /// Serializes the provided value to the backing store and wraps it.
    ///
    /// # Errors
    ///
    /// If there is an error during serializing of the value, an error variant will be returned that
    /// describes the error.  If the backing store is too small to hold the serialized version of
    /// the value, an error variant will be returned defining the minimum size the backing store
    /// must be, as well containing the value that failed to get serialized.
    pub fn from_value(mut backing: B, value: T) -> Result<BackedArchive<B, T>, SerializeError<T>>
    where
        T: Serialize<DefaultSerializer>,
    {
        // Serialize our value so we can shove it into the backing.
        let mut serializer = DefaultSerializer::default();
        _ = serializer
            .serialize_value(&value)
            .map_err(|e| SerializeError::FailedToSerialize(e.to_string()))?;

        let src_buf = serializer.into_serializer().into_inner();

        // Now we have to write the serialized version to the backing store.  For obvious reasons,
        // the backing store needs to be able to hold the entire serialized representation, so we
        // check for that.  As well, instead of using `archived_root_mut`, we use
        // `archived_value_mut`, because this lets us relax need the backing store to be sized
        // _identically_ to the serialized size.
        let dst_buf = backing.as_mut();
        if dst_buf.len() < src_buf.len() {
            return Err(SerializeError::BackingStoreTooSmall(value, src_buf.len()));
        }

        dst_buf[..src_buf.len()].copy_from_slice(&src_buf);

        Ok(Self {
            backing,
            _archive: PhantomData,
        })
    }

    /// Gets a reference to the archived value.
    #[cfg(test)]
    pub fn get_archive_mut(&mut self) -> Pin<&mut T::Archived> {
        use rkyv::archived_root_mut;

        let pinned = Pin::new(self.backing.as_mut());
        unsafe { archived_root_mut::<T>(pinned) }
    }
}