rkyv/niche/
option_box.rs

1//! A niched archived `Option<Box<T>>` that uses less space.
2
3use core::{
4    cmp, fmt, hash, hint::unreachable_unchecked, mem::ManuallyDrop, ops::Deref,
5};
6
7use munge::munge;
8use rancor::Fallible;
9
10use crate::{
11    boxed::{ArchivedBox, BoxResolver},
12    seal::Seal,
13    ser::Writer,
14    traits::ArchivePointee,
15    ArchiveUnsized, Place, Portable, RelPtr, SerializeUnsized,
16};
17
18/// A niched archived `Option<Box<T>>`.
19///
20/// It uses less space by storing the `None` variant as a null pointer.
21#[derive(Portable)]
22#[rkyv(crate)]
23#[cfg_attr(feature = "bytecheck", derive(bytecheck::CheckBytes))]
24#[repr(transparent)]
25pub struct ArchivedOptionBox<T: ArchivePointee + ?Sized> {
26    repr: Repr<T>,
27}
28
29#[derive(Portable)]
30#[rkyv(crate)]
31#[repr(C)]
32union Repr<T: ArchivePointee + ?Sized> {
33    boxed: ManuallyDrop<ArchivedBox<T>>,
34    ptr: ManuallyDrop<RelPtr<T>>,
35}
36
37impl<T: ArchivePointee + ?Sized> Repr<T> {
38    fn is_invalid(&self) -> bool {
39        unsafe { self.ptr.is_invalid() }
40    }
41}
42
43#[cfg(feature = "bytecheck")]
44const _: () = {
45    use crate::{
46        bytecheck::{CheckBytes, Verify},
47        rancor::Source,
48        traits::LayoutRaw,
49        validation::ArchiveContext,
50    };
51
52    unsafe impl<T, C> CheckBytes<C> for Repr<T>
53    where
54        T: ArchivePointee + ?Sized,
55        C: Fallible + ?Sized,
56        RelPtr<T>: CheckBytes<C>,
57        Self: Verify<C>,
58    {
59        unsafe fn check_bytes(
60            value: *const Self,
61            context: &mut C,
62        ) -> Result<(), C::Error> {
63            // SAFETY: `Repr<T>` is a `#[repr(C)]` union of an `ArchivedBox<T>`
64            // and a `RelPtr<T>`, and so is guaranteed to be aligned and point
65            // to enough bytes for a `RelPtr<T>`.
66            unsafe {
67                RelPtr::check_bytes(value.cast::<RelPtr<T>>(), context)?;
68            }
69
70            // verify with null check
71            Self::verify(unsafe { &*value }, context)
72        }
73    }
74
75    unsafe impl<T, C> Verify<C> for Repr<T>
76    where
77        T: ArchivePointee + CheckBytes<C> + LayoutRaw + ?Sized,
78        T::ArchivedMetadata: CheckBytes<C>,
79        C: Fallible + ArchiveContext + ?Sized,
80        C::Error: Source,
81    {
82        fn verify(&self, context: &mut C) -> Result<(), C::Error> {
83            let is_invalid = unsafe { self.ptr.is_invalid() };
84            if is_invalid {
85                // This is a `None` and doesn't need to be checked further
86                Ok(())
87            } else {
88                unsafe { self.boxed.verify(context) }
89            }
90        }
91    }
92};
93
94impl<T: ArchivePointee + ?Sized> ArchivedOptionBox<T> {
95    /// Returns `true` if the option box is a `None` value.
96    pub fn is_none(&self) -> bool {
97        self.as_ref().is_none()
98    }
99
100    /// Returns `true` if the option box is a `Some` value.
101    pub fn is_some(&self) -> bool {
102        self.as_ref().is_some()
103    }
104
105    /// Converts to an `Option<&ArchivedBox<T>>`.
106    pub fn as_ref(&self) -> Option<&ArchivedBox<T>> {
107        if self.repr.is_invalid() {
108            None
109        } else {
110            unsafe { Some(&self.repr.boxed) }
111        }
112    }
113
114    /// Converts to an `Option<&mut ArchivedBox<T>>`.
115    pub fn as_mut(&mut self) -> Option<&mut ArchivedBox<T>> {
116        if self.repr.is_invalid() {
117            None
118        } else {
119            unsafe { Some(&mut self.repr.boxed) }
120        }
121    }
122
123    /// Converts from `Seal<'_, ArchivedOption<T>>` to `Option<Seal<'_,
124    /// ArchivedBox<T>>>`.
125    pub fn as_seal(this: Seal<'_, Self>) -> Option<Seal<'_, ArchivedBox<T>>> {
126        let this = unsafe { Seal::unseal_unchecked(this) };
127        this.as_mut().map(Seal::new)
128    }
129
130    /// Returns an iterator over the possibly-contained value.
131    pub fn iter(&self) -> Iter<&'_ ArchivedBox<T>> {
132        Iter::new(self.as_ref())
133    }
134
135    /// Returns an iterator over the mutable possibly-contained value.
136    pub fn iter_mut(&mut self) -> Iter<&'_ mut ArchivedBox<T>> {
137        Iter::new(self.as_mut())
138    }
139
140    /// Returns an iterator over the sealed possibly-contained value.
141    pub fn iter_seal(this: Seal<'_, Self>) -> Iter<Seal<'_, ArchivedBox<T>>> {
142        Iter::new(Self::as_seal(this))
143    }
144
145    /// Converts from `&ArchivedOptionBox<T>` to `Option<&T>`.
146    ///
147    /// Leaves the original `ArchivedOptionBox` in-place, creating a new one
148    /// with a reference to the original one.
149    pub fn as_deref(&self) -> Option<&T> {
150        self.as_ref().map(|x| (*x).deref())
151    }
152}
153
154impl<T: ArchivePointee + ?Sized> ArchivedOptionBox<T> {
155    /// Resolves an `ArchivedOptionBox<T::Archived>` from an `Option<&T>`.
156    pub fn resolve_from_option<U: ArchiveUnsized<Archived = T> + ?Sized>(
157        field: Option<&U>,
158        resolver: OptionBoxResolver,
159        out: Place<Self>,
160    ) {
161        munge!(let Self { repr } = out);
162        if let Some(value) = field {
163            let resolver =
164                if let OptionBoxResolver::Some(metadata_resolver) = resolver {
165                    metadata_resolver
166                } else {
167                    unsafe {
168                        unreachable_unchecked();
169                    }
170                };
171
172            let out = unsafe { repr.cast_unchecked::<ArchivedBox<T>>() };
173            ArchivedBox::resolve_from_ref(value, resolver, out)
174        } else {
175            let out = unsafe { repr.cast_unchecked::<RelPtr<T>>() };
176            RelPtr::emplace_invalid(out);
177        }
178    }
179
180    /// Serializes an `ArchivedOptionBox<T::Archived>` from an `Option<&T>`.
181    pub fn serialize_from_option<U, S>(
182        field: Option<&U>,
183        serializer: &mut S,
184    ) -> Result<OptionBoxResolver, S::Error>
185    where
186        U: SerializeUnsized<S, Archived = T> + ?Sized,
187        S: Fallible + Writer + ?Sized,
188    {
189        if let Some(value) = field {
190            Ok(OptionBoxResolver::Some(ArchivedBox::serialize_from_ref(
191                value, serializer,
192            )?))
193        } else {
194            Ok(OptionBoxResolver::None)
195        }
196    }
197}
198
199impl<T: ArchivePointee + ?Sized> fmt::Debug for ArchivedOptionBox<T>
200where
201    T::ArchivedMetadata: fmt::Debug,
202{
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        match self.as_ref() {
205            Some(inner) => inner.fmt(f),
206            None => f.debug_tuple("None").finish(),
207        }
208    }
209}
210
211impl<T: ArchivePointee + Eq + ?Sized> Eq for ArchivedOptionBox<T> {}
212
213impl<T: ArchivePointee + hash::Hash + ?Sized> hash::Hash
214    for ArchivedOptionBox<T>
215{
216    fn hash<H: hash::Hasher>(&self, state: &mut H) {
217        self.as_ref().hash(state)
218    }
219}
220
221impl<T: ArchivePointee + Ord + ?Sized> Ord for ArchivedOptionBox<T> {
222    fn cmp(&self, other: &Self) -> cmp::Ordering {
223        self.as_ref().cmp(&other.as_ref())
224    }
225}
226
227impl<T: ArchivePointee + PartialEq + ?Sized> PartialEq
228    for ArchivedOptionBox<T>
229{
230    fn eq(&self, other: &Self) -> bool {
231        self.as_ref().eq(&other.as_ref())
232    }
233}
234
235impl<T: ArchivePointee + PartialOrd + ?Sized> PartialOrd
236    for ArchivedOptionBox<T>
237{
238    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
239        self.as_ref().partial_cmp(&other.as_ref())
240    }
241}
242
243/// An iterator over a reference to the `Some` variant of an
244/// `ArchivedOptionBox`.
245///
246/// This iterator yields one value if the `ArchivedOptionBox` is a `Some`,
247/// otherwise none.
248pub type Iter<P> = crate::option::Iter<P>;
249
250/// The resolver for [`ArchivedOptionBox`].
251pub enum OptionBoxResolver {
252    /// The `ArchivedOptionBox` was `None`
253    None,
254    /// The resolver for the `ArchivedBox`
255    Some(BoxResolver),
256}