logo
  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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Serialization traits, serializers, and adapters.

pub mod serializers;

use crate::{Archive, ArchiveUnsized, Fallible, RelPtr, Serialize, SerializeUnsized};
use core::{alloc::Layout, mem, ptr::NonNull, slice};

/// A byte sink that knows where it is.
///
/// A type that is [`io::Write`](std::io::Write) can be wrapped in a
/// [`WriteSerializer`](serializers::WriteSerializer) to equip it with `Serializer`.
///
/// It's important that the memory for archived objects is properly aligned before attempting to
/// read objects out of it; use an [`AlignedVec`](crate::AlignedVec) or the
/// [`AlignedBytes`](crate::AlignedBytes) wrappers if they are appropriate.
pub trait Serializer: Fallible {
    /// Returns the current position of the serializer.
    fn pos(&self) -> usize;

    /// Attempts to write the given bytes to the serializer.
    fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error>;

    /// Advances the given number of bytes as padding.
    #[inline]
    fn pad(&mut self, padding: usize) -> Result<(), Self::Error> {
        const MAX_ZEROES: usize = 32;
        const ZEROES: [u8; MAX_ZEROES] = [0; MAX_ZEROES];
        debug_assert!(padding < MAX_ZEROES);

        self.write(&ZEROES[0..padding])
    }

    /// Aligns the position of the serializer to the given alignment.
    #[inline]
    fn align(&mut self, align: usize) -> Result<usize, Self::Error> {
        let mask = align - 1;
        debug_assert_eq!(align & mask, 0);

        self.pad((align - (self.pos() & mask)) & mask)?;
        Ok(self.pos())
    }

    /// Aligns the position of the serializer to be suitable to write the given type.
    #[inline]
    fn align_for<T>(&mut self) -> Result<usize, Self::Error> {
        self.align(mem::align_of::<T>())
    }

    /// Resolves the given value with its resolver and writes the archived type.
    ///
    /// Returns the position of the written archived type.
    ///
    /// # Safety
    ///
    /// - `resolver` must be the result of serializing `value`
    /// - The serializer must be aligned for a `T::Archived`
    unsafe fn resolve_aligned<T: Archive + ?Sized>(
        &mut self,
        value: &T,
        resolver: T::Resolver,
    ) -> Result<usize, Self::Error> {
        let pos = self.pos();
        debug_assert_eq!(pos & (mem::align_of::<T::Archived>() - 1), 0);

        let mut resolved = mem::MaybeUninit::<T::Archived>::uninit();
        resolved.as_mut_ptr().write_bytes(0, 1);
        value.resolve(pos, resolver, resolved.as_mut_ptr());

        let data = resolved.as_ptr().cast::<u8>();
        let len = mem::size_of::<T::Archived>();
        self.write(slice::from_raw_parts(data, len))?;
        Ok(pos)
    }

    /// Archives the given object and returns the position it was archived at.
    #[inline]
    fn serialize_value<T: Serialize<Self>>(&mut self, value: &T) -> Result<usize, Self::Error> {
        let resolver = value.serialize(self)?;
        self.align_for::<T::Archived>()?;
        unsafe { self.resolve_aligned(value, resolver) }
    }

    /// Resolves the given reference with its resolver and writes the archived reference.
    ///
    /// Returns the position of the written archived `RelPtr`.
    ///
    /// # Safety
    ///
    /// - `metadata_resolver` must be the result of serializing the metadata of `value`
    /// - `to` must be the position of the serialized `value` within the archive
    /// - The serializer must be aligned for a `RelPtr<T::Archived>`
    unsafe fn resolve_unsized_aligned<T: ArchiveUnsized + ?Sized>(
        &mut self,
        value: &T,
        to: usize,
        metadata_resolver: T::MetadataResolver,
    ) -> Result<usize, Self::Error> {
        let from = self.pos();
        debug_assert_eq!(from & (mem::align_of::<RelPtr<T::Archived>>() - 1), 0);

        let mut resolved = mem::MaybeUninit::<RelPtr<T::Archived>>::uninit();
        resolved.as_mut_ptr().write_bytes(0, 1);
        value.resolve_unsized(from, to, metadata_resolver, resolved.as_mut_ptr());

        let data = resolved.as_ptr().cast::<u8>();
        let len = mem::size_of::<RelPtr<T::Archived>>();
        self.write(slice::from_raw_parts(data, len))?;
        Ok(from)
    }

    /// Archives a reference to the given object and returns the position it was archived at.
    #[inline]
    fn serialize_unsized_value<T: SerializeUnsized<Self> + ?Sized>(
        &mut self,
        value: &T,
    ) -> Result<usize, Self::Error> {
        let to = value.serialize_unsized(self)?;
        let metadata_resolver = value.serialize_metadata(self)?;
        self.align_for::<RelPtr<T::Archived>>()?;
        unsafe { self.resolve_unsized_aligned(value, to, metadata_resolver) }
    }
}

// Someday this can probably be replaced with alloc::Allocator

/// A serializer that can allocate scratch space.
pub trait ScratchSpace: Fallible {
    /// Allocates scratch space of the requested size.
    ///
    /// # Safety
    ///
    /// `layout` must have non-zero size.
    unsafe fn push_scratch(&mut self, layout: Layout) -> Result<NonNull<[u8]>, Self::Error>;

    /// Deallocates previously allocated scratch space.
    ///
    /// # Safety
    ///
    /// - `ptr` must be the scratch memory last allocated with `push_scratch`.
    /// - `layout` must be the same layout that was used to allocate that block of memory.
    unsafe fn pop_scratch(&mut self, ptr: NonNull<u8>, layout: Layout) -> Result<(), Self::Error>;
}

/// A registry that tracks serialized shared memory.
///
/// This trait is required to serialize shared pointers.
pub trait SharedSerializeRegistry: Fallible {
    /// Gets the position of a previously-added shared pointer.
    ///
    /// Returns `None` if the pointer has not yet been added.
    fn get_shared_ptr(&self, value: *const u8) -> Option<usize>;

    /// Gets the position of a previously-added shared value.
    ///
    /// Returns `None` if the value has not yet been added.
    #[inline]
    fn get_shared<T: ?Sized>(&self, value: &T) -> Option<usize> {
        self.get_shared_ptr(value as *const T as *const u8)
    }

    /// Adds the position of a shared pointer to the registry.
    fn add_shared_ptr(&mut self, value: *const u8, pos: usize) -> Result<(), Self::Error>;

    /// Adds the position of a shared value to the registry.
    #[inline]
    fn add_shared<T: ?Sized>(&mut self, value: &T, pos: usize) -> Result<(), Self::Error> {
        self.add_shared_ptr(value as *const T as *const u8, pos)
    }

    /// Archives the given shared value and returns its position. If the value has already been
    /// added then it returns the position of the previously added value.
    #[inline]
    fn serialize_shared<T: SerializeUnsized<Self> + ?Sized>(
        &mut self,
        value: &T,
    ) -> Result<usize, Self::Error>
    where
        Self: Serializer,
    {
        if let Some(pos) = self.get_shared(value) {
            Ok(pos)
        } else {
            let pos = value.serialize_unsized(self)?;
            self.add_shared(value, pos)?;
            Ok(pos)
        }
    }
}