Skip to main content

rkyv/niche/
niched_option.rs

1//! A niched `ArchivedOption<T>` that uses less space based on a [`Niching`].
2
3use core::{cmp, fmt, marker::PhantomData, mem::MaybeUninit, ops::Deref};
4
5use munge::munge;
6use rancor::Fallible;
7
8use super::niching::Niching;
9use crate::{seal::Seal, Archive, Place, Portable, Serialize};
10
11/// A niched `ArchivedOption<T>`.
12///
13/// It has the same layout as `T`, and thus uses less space by storing the
14/// `None` variant in a custom way based on `N`.
15#[derive(Portable)]
16#[rkyv(crate)]
17#[repr(transparent)]
18pub struct NichedOption<T, N: ?Sized> {
19    repr: MaybeUninit<T>,
20    _niching: PhantomData<N>,
21}
22
23#[cfg(feature = "bytecheck")]
24const _: () = {
25    use core::ptr::addr_of;
26
27    use crate::bytecheck::CheckBytes;
28
29    unsafe impl<T, N, C> CheckBytes<C> for NichedOption<T, N>
30    where
31        T: CheckBytes<C>,
32        N: Niching<T> + ?Sized,
33        C: Fallible + ?Sized,
34    {
35        unsafe fn check_bytes(
36            value: *const Self,
37            context: &mut C,
38        ) -> Result<(), C::Error> {
39            let ptr = unsafe { addr_of!((*value).repr).cast::<T>() };
40            let is_niched = unsafe { N::is_niched(ptr) };
41
42            if !is_niched {
43                unsafe {
44                    T::check_bytes(ptr, context)?;
45                }
46            }
47            Ok(())
48        }
49    }
50};
51
52impl<T, N: Niching<T> + ?Sized> NichedOption<T, N> {
53    /// Returns `true` if the option is a `None` value.
54    pub fn is_none(&self) -> bool {
55        unsafe { N::is_niched(self.repr.as_ptr()) }
56    }
57
58    /// Returns `true` if the option is a `Some` value.
59    pub fn is_some(&self) -> bool {
60        !self.is_none()
61    }
62
63    /// Converts to an `Option<&T>`.
64    pub fn as_ref(&self) -> Option<&T> {
65        if self.is_none() {
66            None
67        } else {
68            Some(unsafe { self.repr.assume_init_ref() })
69        }
70    }
71
72    /// Converts to an `Option<&mut T>`.
73    pub fn as_mut(&mut self) -> Option<&mut T> {
74        if self.is_none() {
75            None
76        } else {
77            Some(unsafe { self.repr.assume_init_mut() })
78        }
79    }
80
81    /// Converts from `Seal<'_, NichedOption<T, N>>` to `Option<Seal<'_, T>>`.
82    pub fn as_seal(this: Seal<'_, Self>) -> Option<Seal<'_, T>> {
83        let this = unsafe { Seal::unseal_unchecked(this) };
84        this.as_mut().map(Seal::new)
85    }
86
87    /// Returns an iterator over the possibly-contained value.
88    pub fn iter(&self) -> Iter<&'_ T> {
89        Iter::new(self.as_ref())
90    }
91
92    /// Returns an iterator over the mutable possibly-contained value.
93    pub fn iter_mut(&mut self) -> Iter<&'_ mut T> {
94        Iter::new(self.as_mut())
95    }
96
97    /// Returns an iterator over the sealed possibly-contained value.
98    pub fn iter_seal(this: Seal<'_, Self>) -> Iter<Seal<'_, T>> {
99        Iter::new(Self::as_seal(this))
100    }
101
102    /// Resolves a `NichedOption<U::Archived, N>` from an `Option<&U>`.
103    pub fn resolve_from_option<U>(
104        option: Option<&U>,
105        resolver: Option<U::Resolver>,
106        out: Place<Self>,
107    ) where
108        U: Archive<Archived = T>,
109    {
110        let out = Self::munge_place(out);
111        match option {
112            Some(value) => {
113                let resolver = resolver.expect("non-niched resolver");
114                value.resolve(resolver, out);
115            }
116            None => N::resolve_niched(out),
117        }
118    }
119
120    /// Serializes a `NichedOption<U::Archived, N>` from an `Option<&U>`.
121    pub fn serialize_from_option<U, S>(
122        option: Option<&U>,
123        serializer: &mut S,
124    ) -> Result<Option<U::Resolver>, S::Error>
125    where
126        U: Serialize<S, Archived = T>,
127        S: Fallible + ?Sized,
128    {
129        match option {
130            Some(value) => value.serialize(serializer).map(Some),
131            None => Ok(None),
132        }
133    }
134
135    pub(crate) fn munge_place(out: Place<Self>) -> Place<T> {
136        munge!(let Self { repr, .. } = out);
137
138        unsafe { repr.cast_unchecked::<T>() }
139    }
140}
141
142impl<T, N> NichedOption<T, N>
143where
144    T: Deref,
145    N: Niching<T> + ?Sized,
146{
147    /// Converts from `&NichedOption<T, N>` to `Option<&T::Target>`.
148    pub fn as_deref(&self) -> Option<&<T as Deref>::Target> {
149        self.as_ref().map(Deref::deref)
150    }
151}
152
153impl<T, N> fmt::Debug for NichedOption<T, N>
154where
155    T: fmt::Debug,
156    N: Niching<T> + ?Sized,
157{
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        self.as_ref().fmt(f)
160    }
161}
162
163impl<T, N> Eq for NichedOption<T, N>
164where
165    T: Eq,
166    N: Niching<T> + ?Sized,
167{
168}
169
170impl<T, N> PartialEq for NichedOption<T, N>
171where
172    T: PartialEq,
173    N: Niching<T> + ?Sized,
174{
175    fn eq(&self, other: &Self) -> bool {
176        self.as_ref().eq(&other.as_ref())
177    }
178}
179
180impl<T, N, Rhs> PartialEq<Option<Rhs>> for NichedOption<T, N>
181where
182    T: PartialEq<Rhs>,
183    N: Niching<T> + ?Sized,
184{
185    fn eq(&self, other: &Option<Rhs>) -> bool {
186        match (self.as_ref(), other) {
187            (Some(self_value), Some(other_value)) => self_value.eq(other_value),
188            (None, None) => true,
189            _ => false,
190        }
191    }
192}
193
194impl<T, N> Ord for NichedOption<T, N>
195where
196    T: Ord,
197    N: Niching<T> + ?Sized,
198{
199    fn cmp(&self, other: &Self) -> cmp::Ordering {
200        self.as_ref().cmp(&other.as_ref())
201    }
202}
203
204impl<T, N> PartialOrd for NichedOption<T, N>
205where
206    T: PartialOrd,
207    N: Niching<T> + ?Sized,
208{
209    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
210        self.as_ref().partial_cmp(&other.as_ref())
211    }
212}
213
214/// An iterator over a reference to the `Some` variant of a `NichedOption`.
215///
216/// This iterator yields one value if the `NichedOption` is a `Some`, otherwise
217/// none.
218pub type Iter<P> = crate::option::Iter<P>;