/rust/registry/src/index.crates.io-6f17d22bba15001f/zerovec-0.10.4/src/ule/option.rs
Line | Count | Source (jump to first uncovered line) |
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 super::*; |
6 | | use core::cmp::Ordering; |
7 | | use core::marker::PhantomData; |
8 | | use core::mem::{self, MaybeUninit}; |
9 | | |
10 | | /// This type is the [`ULE`] type for `Option<U>` where `U` is a [`ULE`] type |
11 | | /// |
12 | | /// # Example |
13 | | /// |
14 | | /// ```rust |
15 | | /// use zerovec::ZeroVec; |
16 | | /// |
17 | | /// let z = ZeroVec::alloc_from_slice(&[ |
18 | | /// Some('a'), |
19 | | /// Some('á'), |
20 | | /// Some('ø'), |
21 | | /// None, |
22 | | /// Some('ł'), |
23 | | /// ]); |
24 | | /// |
25 | | /// assert_eq!(z.get(2), Some(Some('ø'))); |
26 | | /// assert_eq!(z.get(3), Some(None)); |
27 | | /// ``` |
28 | | // Invariants: |
29 | | // The MaybeUninit is zeroed when None (bool = false), |
30 | | // and is valid when Some (bool = true) |
31 | | #[repr(C, packed)] |
32 | | pub struct OptionULE<U>(bool, MaybeUninit<U>); |
33 | | |
34 | | impl<U: Copy> OptionULE<U> { |
35 | | /// Obtain this as an `Option<T>` |
36 | 0 | pub fn get(self) -> Option<U> { |
37 | 0 | if self.0 { |
38 | | unsafe { |
39 | | // safety: self.0 is true so the MaybeUninit is valid |
40 | 0 | Some(self.1.assume_init()) |
41 | | } |
42 | | } else { |
43 | 0 | None |
44 | | } |
45 | 0 | } Unexecuted instantiation: <zerovec::ule::option::OptionULE<icu_locid::subtags::region::Region>>::get Unexecuted instantiation: <zerovec::ule::option::OptionULE<icu_locid::subtags::script::Script>>::get Unexecuted instantiation: <zerovec::ule::option::OptionULE<_>>::get |
46 | | |
47 | | /// Construct an `OptionULE<U>` from an equivalent `Option<T>` |
48 | 0 | pub fn new(opt: Option<U>) -> Self { |
49 | 0 | if let Some(inner) = opt { |
50 | 0 | Self(true, MaybeUninit::new(inner)) |
51 | | } else { |
52 | 0 | Self(false, MaybeUninit::zeroed()) |
53 | | } |
54 | 0 | } |
55 | | } |
56 | | |
57 | | impl<U: Copy + core::fmt::Debug> core::fmt::Debug for OptionULE<U> { |
58 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
59 | 0 | self.get().fmt(f) |
60 | 0 | } |
61 | | } |
62 | | |
63 | | // Safety (based on the safety checklist on the ULE trait): |
64 | | // 1. OptionULE does not include any uninitialized or padding bytes. |
65 | | // (achieved by `#[repr(C, packed)]` on a struct containing only ULE fields, |
66 | | // in the context of this impl. The MaybeUninit is valid for all byte sequences, and we only generate |
67 | | /// zeroed or valid-T byte sequences to fill it) |
68 | | // 2. OptionULE is aligned to 1 byte. |
69 | | // (achieved by `#[repr(C, packed)]` on a struct containing only ULE fields, in the context of this impl) |
70 | | // 3. The impl of validate_byte_slice() returns an error if any byte is not valid. |
71 | | // 4. The impl of validate_byte_slice() returns an error if there are extra bytes. |
72 | | // 5. The other ULE methods use the default impl. |
73 | | // 6. OptionULE byte equality is semantic equality by relying on the ULE equality |
74 | | // invariant on the subfields |
75 | | unsafe impl<U: ULE> ULE for OptionULE<U> { |
76 | 0 | fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { |
77 | 0 | let size = mem::size_of::<Self>(); |
78 | 0 | if bytes.len() % size != 0 { |
79 | 0 | return Err(ZeroVecError::length::<Self>(bytes.len())); |
80 | 0 | } |
81 | 0 | for chunk in bytes.chunks(size) { |
82 | | #[allow(clippy::indexing_slicing)] // `chunk` will have enough bytes to fit Self |
83 | 0 | match chunk[0] { |
84 | | // https://doc.rust-lang.org/reference/types/boolean.html |
85 | | // Rust booleans are always size 1, align 1 values with valid bit patterns 0x0 or 0x1 |
86 | | 0 => { |
87 | 0 | if !chunk[1..].iter().all(|x| *x == 0) { |
88 | 0 | return Err(ZeroVecError::parse::<Self>()); |
89 | 0 | } |
90 | | } |
91 | 0 | 1 => U::validate_byte_slice(&chunk[1..])?, |
92 | 0 | _ => return Err(ZeroVecError::parse::<Self>()), |
93 | | } |
94 | | } |
95 | 0 | Ok(()) |
96 | 0 | } |
97 | | } |
98 | | |
99 | | impl<T: AsULE> AsULE for Option<T> { |
100 | | type ULE = OptionULE<T::ULE>; |
101 | 0 | fn to_unaligned(self) -> OptionULE<T::ULE> { |
102 | 0 | OptionULE::new(self.map(T::to_unaligned)) |
103 | 0 | } |
104 | | |
105 | 0 | fn from_unaligned(other: OptionULE<T::ULE>) -> Self { |
106 | 0 | other.get().map(T::from_unaligned) |
107 | 0 | } Unexecuted instantiation: <core::option::Option<icu_locid::subtags::region::Region> as zerovec::ule::AsULE>::from_unaligned Unexecuted instantiation: <core::option::Option<icu_locid::subtags::script::Script> as zerovec::ule::AsULE>::from_unaligned Unexecuted instantiation: <core::option::Option<_> as zerovec::ule::AsULE>::from_unaligned |
108 | | } |
109 | | |
110 | | impl<U: Copy> Copy for OptionULE<U> {} |
111 | | |
112 | | impl<U: Copy> Clone for OptionULE<U> { |
113 | 0 | fn clone(&self) -> Self { |
114 | 0 | *self |
115 | 0 | } |
116 | | } |
117 | | |
118 | | impl<U: Copy + PartialEq> PartialEq for OptionULE<U> { |
119 | 0 | fn eq(&self, other: &Self) -> bool { |
120 | 0 | self.get().eq(&other.get()) |
121 | 0 | } |
122 | | } |
123 | | |
124 | | impl<U: Copy + Eq> Eq for OptionULE<U> {} |
125 | | |
126 | | /// A type allowing one to represent `Option<U>` for [`VarULE`] `U` types. |
127 | | /// |
128 | | /// ```rust |
129 | | /// use zerovec::ule::OptionVarULE; |
130 | | /// use zerovec::VarZeroVec; |
131 | | /// |
132 | | /// let mut zv: VarZeroVec<OptionVarULE<str>> = VarZeroVec::new(); |
133 | | /// |
134 | | /// zv.make_mut().push(&None::<&str>); |
135 | | /// zv.make_mut().push(&Some("hello")); |
136 | | /// zv.make_mut().push(&Some("world")); |
137 | | /// zv.make_mut().push(&None::<&str>); |
138 | | /// |
139 | | /// assert_eq!(zv.get(0).unwrap().as_ref(), None); |
140 | | /// assert_eq!(zv.get(1).unwrap().as_ref(), Some("hello")); |
141 | | /// ``` |
142 | | // The slice field is empty when None (bool = false), |
143 | | // and is a valid T when Some (bool = true) |
144 | | #[repr(C, packed)] |
145 | | pub struct OptionVarULE<U: VarULE + ?Sized>(PhantomData<U>, bool, [u8]); |
146 | | |
147 | | impl<U: VarULE + ?Sized> OptionVarULE<U> { |
148 | | /// Obtain this as an `Option<&U>` |
149 | 0 | pub fn as_ref(&self) -> Option<&U> { |
150 | 0 | if self.1 { |
151 | | unsafe { |
152 | | // Safety: byte field is a valid T if boolean field is true |
153 | 0 | Some(U::from_byte_slice_unchecked(&self.2)) |
154 | | } |
155 | | } else { |
156 | 0 | None |
157 | | } |
158 | 0 | } |
159 | | } |
160 | | |
161 | | impl<U: VarULE + ?Sized + core::fmt::Debug> core::fmt::Debug for OptionVarULE<U> { |
162 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
163 | 0 | self.as_ref().fmt(f) |
164 | 0 | } |
165 | | } |
166 | | |
167 | | // Safety (based on the safety checklist on the VarULE trait): |
168 | | // 1. OptionVarULE<T> does not include any uninitialized or padding bytes |
169 | | // (achieved by being repr(C, packed) on ULE types) |
170 | | // 2. OptionVarULE<T> is aligned to 1 byte (achieved by being repr(C, packed) on ULE types) |
171 | | // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. |
172 | | // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety |
173 | | // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. |
174 | | // 6. All other methods are defaulted |
175 | | // 7. OptionVarULE<T> byte equality is semantic equality (achieved by being an aggregate) |
176 | | unsafe impl<U: VarULE + ?Sized> VarULE for OptionVarULE<U> { |
177 | | #[inline] |
178 | 0 | fn validate_byte_slice(slice: &[u8]) -> Result<(), ZeroVecError> { |
179 | 0 | if slice.is_empty() { |
180 | 0 | return Err(ZeroVecError::length::<Self>(slice.len())); |
181 | 0 | } |
182 | 0 | #[allow(clippy::indexing_slicing)] // slice already verified to be nonempty |
183 | 0 | match slice[0] { |
184 | | // https://doc.rust-lang.org/reference/types/boolean.html |
185 | | // Rust booleans are always size 1, align 1 values with valid bit patterns 0x0 or 0x1 |
186 | | 0 => { |
187 | 0 | if slice.len() != 1 { |
188 | 0 | Err(ZeroVecError::length::<Self>(slice.len())) |
189 | | } else { |
190 | 0 | Ok(()) |
191 | | } |
192 | | } |
193 | 0 | 1 => U::validate_byte_slice(&slice[1..]), |
194 | 0 | _ => Err(ZeroVecError::parse::<Self>()), |
195 | | } |
196 | 0 | } |
197 | | |
198 | | #[inline] |
199 | 0 | unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { |
200 | 0 | let entire_struct_as_slice: *const [u8] = |
201 | 0 | ::core::ptr::slice_from_raw_parts(bytes.as_ptr(), bytes.len() - 1); |
202 | 0 | &*(entire_struct_as_slice as *const Self) |
203 | 0 | } |
204 | | } |
205 | | |
206 | | unsafe impl<T, U> EncodeAsVarULE<OptionVarULE<U>> for Option<T> |
207 | | where |
208 | | T: EncodeAsVarULE<U>, |
209 | | U: VarULE + ?Sized, |
210 | | { |
211 | 0 | fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R { |
212 | 0 | // unnecessary if the other two are implemented |
213 | 0 | unreachable!() |
214 | | } |
215 | | |
216 | | #[inline] |
217 | 0 | fn encode_var_ule_len(&self) -> usize { |
218 | 0 | if let Some(ref inner) = *self { |
219 | | // slice + boolean |
220 | 0 | 1 + inner.encode_var_ule_len() |
221 | | } else { |
222 | | // boolean + empty slice |
223 | 0 | 1 |
224 | | } |
225 | 0 | } |
226 | | |
227 | | #[allow(clippy::indexing_slicing)] // This method is allowed to panic when lengths are invalid |
228 | 0 | fn encode_var_ule_write(&self, dst: &mut [u8]) { |
229 | 0 | if let Some(ref inner) = *self { |
230 | 0 | debug_assert!( |
231 | 0 | !dst.is_empty(), |
232 | 0 | "OptionVarULE must have at least one byte when Some" |
233 | | ); |
234 | 0 | dst[0] = 1; |
235 | 0 | inner.encode_var_ule_write(&mut dst[1..]); |
236 | | } else { |
237 | 0 | debug_assert!( |
238 | 0 | dst.len() == 1, |
239 | 0 | "OptionVarULE must have exactly one byte when None" |
240 | | ); |
241 | 0 | dst[0] = 0; |
242 | | } |
243 | 0 | } |
244 | | } |
245 | | |
246 | | impl<U: VarULE + ?Sized + PartialEq> PartialEq for OptionVarULE<U> { |
247 | 0 | fn eq(&self, other: &Self) -> bool { |
248 | 0 | self.as_ref().eq(&other.as_ref()) |
249 | 0 | } |
250 | | } |
251 | | |
252 | | impl<U: VarULE + ?Sized + Eq> Eq for OptionVarULE<U> {} |
253 | | |
254 | | impl<U: VarULE + ?Sized + PartialOrd> PartialOrd for OptionVarULE<U> { |
255 | 0 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
256 | 0 | self.as_ref().partial_cmp(&other.as_ref()) |
257 | 0 | } |
258 | | } |
259 | | |
260 | | impl<U: VarULE + ?Sized + Ord> Ord for OptionVarULE<U> { |
261 | 0 | fn cmp(&self, other: &Self) -> Ordering { |
262 | 0 | self.as_ref().cmp(&other.as_ref()) |
263 | 0 | } |
264 | | } |