• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // This file is part of ICU4X. For terms of use, please see the file
2 // called LICENSE at the top level of the ICU4X source tree
3 // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4 
5 use core::{marker::Copy, mem::size_of};
6 
7 #[cfg(feature = "alloc")]
8 use crate::map::ZeroMapKV;
9 #[cfg(feature = "alloc")]
10 use crate::{ZeroSlice, ZeroVec};
11 
12 use super::{AsULE, ULE};
13 
14 /// The [`ULE`] types implementing this trait guarantee that [`NicheBytes::NICHE_BIT_PATTERN`]
15 /// can never occur as a valid byte representation of the type.
16 ///
17 /// Guarantees for a valid implementation.
18 /// 1. N must be equal to `core::mem::sizeo_of::<Self>()` or else it will
19 ///    cause panics.
20 /// 2. The bit pattern [`NicheBytes::NICHE_BIT_PATTERN`] must not be incorrect as it would lead to
21 ///    weird behaviour.
22 /// 3. The abstractions built on top of this trait must panic on an invalid N.
23 /// 4. The abstractions built on this trait that use type punning must ensure that type being
24 ///    punned is [`ULE`].
25 pub trait NicheBytes<const N: usize> {
26     const NICHE_BIT_PATTERN: [u8; N];
27 }
28 
29 /// [`ULE`] type for [`NichedOption<U,N>`] where U implements [`NicheBytes`].
30 /// The invalid bit pattern is used as the niche.
31 ///
32 /// This uses 1 byte less than [`crate::ule::OptionULE<U>`] to represent [`NichedOption<U,N>`].
33 ///
34 /// # Example
35 ///
36 /// ```
37 /// use core::num::NonZeroI8;
38 /// use zerovec::ule::NichedOption;
39 /// use zerovec::ZeroVec;
40 ///
41 /// let bytes = &[0x00, 0x01, 0x02, 0x00];
42 /// let zv_no: ZeroVec<NichedOption<NonZeroI8, 1>> =
43 ///     ZeroVec::parse_bytes(bytes).expect("Unable to parse as NichedOption.");
44 ///
45 /// assert_eq!(zv_no.get(0).map(|e| e.0), Some(None));
46 /// assert_eq!(zv_no.get(1).map(|e| e.0), Some(NonZeroI8::new(1)));
47 /// assert_eq!(zv_no.get(2).map(|e| e.0), Some(NonZeroI8::new(2)));
48 /// assert_eq!(zv_no.get(3).map(|e| e.0), Some(None));
49 /// ```
50 // Invariants:
51 // The union stores [`NicheBytes::NICHE_BIT_PATTERN`] when None.
52 // Any other bit pattern is a valid.
53 #[repr(C)]
54 pub union NichedOptionULE<U: NicheBytes<N> + ULE, const N: usize> {
55     /// Invariant: The value is `niche` only if the bytes equal NICHE_BIT_PATTERN.
56     niche: [u8; N],
57     /// Invariant: The value is `valid` if the `niche` field does not match NICHE_BIT_PATTERN.
58     valid: U,
59 }
60 
61 impl<U: NicheBytes<N> + ULE + core::fmt::Debug, const N: usize> core::fmt::Debug
62     for NichedOptionULE<U, N>
63 {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result64     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65         self.get().fmt(f)
66     }
67 }
68 
69 impl<U: NicheBytes<N> + ULE, const N: usize> NichedOptionULE<U, N> {
70     /// New `NichedOptionULE<U, N>` from `Option<U>`
new(opt: Option<U>) -> Self71     pub fn new(opt: Option<U>) -> Self {
72         assert!(N == core::mem::size_of::<U>());
73         match opt {
74             Some(u) => Self { valid: u },
75             None => Self {
76                 niche: <U as NicheBytes<N>>::NICHE_BIT_PATTERN,
77             },
78         }
79     }
80 
81     /// Convert to an `Option<U>`
get(self) -> Option<U>82     pub fn get(self) -> Option<U> {
83         // Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U
84         unsafe {
85             if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
86                 None
87             } else {
88                 Some(self.valid)
89             }
90         }
91     }
92 
93     /// Borrows as an `Option<&U>`.
as_ref(&self) -> Option<&U>94     pub fn as_ref(&self) -> Option<&U> {
95         // Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U
96         unsafe {
97             if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
98                 None
99             } else {
100                 Some(&self.valid)
101             }
102         }
103     }
104 }
105 
106 impl<U: NicheBytes<N> + ULE, const N: usize> Copy for NichedOptionULE<U, N> {}
107 
108 impl<U: NicheBytes<N> + ULE, const N: usize> Clone for NichedOptionULE<U, N> {
clone(&self) -> Self109     fn clone(&self) -> Self {
110         *self
111     }
112 }
113 
114 impl<U: NicheBytes<N> + ULE + PartialEq, const N: usize> PartialEq for NichedOptionULE<U, N> {
eq(&self, other: &Self) -> bool115     fn eq(&self, other: &Self) -> bool {
116         self.get().eq(&other.get())
117     }
118 }
119 
120 impl<U: NicheBytes<N> + ULE + Eq, const N: usize> Eq for NichedOptionULE<U, N> {}
121 
122 /// Safety for ULE trait
123 /// 1. NichedOptionULE does not have any padding bytes due to `#[repr(C)]` on a struct
124 ///    containing only ULE fields.
125 ///    NichedOptionULE either contains NICHE_BIT_PATTERN or valid U byte sequences.
126 ///    In both cases the data is initialized.
127 /// 2. NichedOptionULE is aligned to 1 byte due to `#[repr(C, packed)]` on a struct containing only
128 ///    ULE fields.
129 /// 3. validate_bytes impl returns an error if invalid bytes are encountered.
130 /// 4. validate_bytes impl returns an error there are extra bytes.
131 /// 5. The other ULE methods are left to their default impl.
132 /// 6. NichedOptionULE equality is based on ULE equality of the subfield, assuming that NicheBytes
133 ///    has been implemented correctly (this is a correctness but not a safety guarantee).
134 unsafe impl<U: NicheBytes<N> + ULE, const N: usize> ULE for NichedOptionULE<U, N> {
validate_bytes(bytes: &[u8]) -> Result<(), crate::ule::UleError>135     fn validate_bytes(bytes: &[u8]) -> Result<(), crate::ule::UleError> {
136         let size = size_of::<Self>();
137         // The implemention is only correct if NICHE_BIT_PATTERN has same number of bytes as the
138         // type.
139         debug_assert!(N == core::mem::size_of::<U>());
140 
141         // The bytes should fully transmute to a collection of Self
142         if bytes.len() % size != 0 {
143             return Err(crate::ule::UleError::length::<Self>(bytes.len()));
144         }
145         bytes.chunks(size).try_for_each(|chunk| {
146             // Associated const cannot be referenced in a pattern
147             // https://doc.rust-lang.org/error-index.html#E0158
148             if chunk == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
149                 Ok(())
150             } else {
151                 U::validate_bytes(chunk)
152             }
153         })
154     }
155 }
156 
157 /// Optional type which uses [`NichedOptionULE<U,N>`] as ULE type.
158 ///
159 /// The implementors guarantee that `N == core::mem::size_of::<Self>()`
160 /// [`repr(transparent)`] guarantees that the layout is same as [`Option<U>`]
161 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
162 #[repr(transparent)]
163 #[allow(clippy::exhaustive_structs)] // newtype
164 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
165 pub struct NichedOption<U, const N: usize>(pub Option<U>);
166 
167 impl<U, const N: usize> Default for NichedOption<U, N> {
default() -> Self168     fn default() -> Self {
169         Self(None)
170     }
171 }
172 
173 impl<U: AsULE, const N: usize> AsULE for NichedOption<U, N>
174 where
175     U::ULE: NicheBytes<N>,
176 {
177     type ULE = NichedOptionULE<U::ULE, N>;
178 
to_unaligned(self) -> Self::ULE179     fn to_unaligned(self) -> Self::ULE {
180         NichedOptionULE::new(self.0.map(U::to_unaligned))
181     }
182 
from_unaligned(unaligned: Self::ULE) -> Self183     fn from_unaligned(unaligned: Self::ULE) -> Self {
184         Self(unaligned.get().map(U::from_unaligned))
185     }
186 }
187 
188 #[cfg(feature = "alloc")]
189 impl<'a, T: AsULE + 'static, const N: usize> ZeroMapKV<'a> for NichedOption<T, N>
190 where
191     T::ULE: NicheBytes<N>,
192 {
193     type Container = ZeroVec<'a, NichedOption<T, N>>;
194     type Slice = ZeroSlice<NichedOption<T, N>>;
195     type GetType = <NichedOption<T, N> as AsULE>::ULE;
196     type OwnedType = Self;
197 }
198 
199 impl<T, const N: usize> IntoIterator for NichedOption<T, N> {
200     type IntoIter = <Option<T> as IntoIterator>::IntoIter;
201     type Item = T;
202 
into_iter(self) -> Self::IntoIter203     fn into_iter(self) -> Self::IntoIter {
204         self.0.into_iter()
205     }
206 }
207