/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.4/src/ule/niche.rs
Line | Count | Source |
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 | | { |
64 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
65 | 0 | self.get().fmt(f) |
66 | 0 | } |
67 | | } |
68 | | |
69 | | impl<U: NicheBytes<N> + ULE, const N: usize> NichedOptionULE<U, N> { |
70 | | /// New `NichedOptionULE<U, N>` from `Option<U>` |
71 | 0 | pub fn new(opt: Option<U>) -> Self { |
72 | 0 | assert!(N == core::mem::size_of::<U>()); |
73 | 0 | match opt { |
74 | 0 | Some(u) => Self { valid: u }, |
75 | 0 | None => Self { |
76 | 0 | niche: <U as NicheBytes<N>>::NICHE_BIT_PATTERN, |
77 | 0 | }, |
78 | | } |
79 | 0 | } |
80 | | |
81 | | /// Convert to an `Option<U>` |
82 | 0 | pub fn get(self) -> Option<U> { |
83 | | // Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U |
84 | | unsafe { |
85 | 0 | if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN { |
86 | 0 | None |
87 | | } else { |
88 | 0 | Some(self.valid) |
89 | | } |
90 | | } |
91 | 0 | } |
92 | | |
93 | | /// Borrows as an `Option<&U>`. |
94 | 0 | pub fn as_ref(&self) -> Option<&U> { |
95 | | // Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U |
96 | | unsafe { |
97 | 0 | if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN { |
98 | 0 | None |
99 | | } else { |
100 | 0 | Some(&self.valid) |
101 | | } |
102 | | } |
103 | 0 | } Unexecuted instantiation: <zerovec::ule::niche::NichedOptionULE<icu_locale_core::subtags::script::Script, 4>>::as_ref Unexecuted instantiation: <zerovec::ule::niche::NichedOptionULE<_, _>>::as_ref |
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> { |
109 | 0 | fn clone(&self) -> Self { |
110 | 0 | *self |
111 | 0 | } |
112 | | } |
113 | | |
114 | | impl<U: NicheBytes<N> + ULE + PartialEq, const N: usize> PartialEq for NichedOptionULE<U, N> { |
115 | 0 | fn eq(&self, other: &Self) -> bool { |
116 | 0 | self.get().eq(&other.get()) |
117 | 0 | } |
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> { |
135 | 0 | fn validate_bytes(bytes: &[u8]) -> Result<(), crate::ule::UleError> { |
136 | 0 | 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 | 0 | debug_assert!(N == core::mem::size_of::<U>()); |
140 | | |
141 | | // The bytes should fully transmute to a collection of Self |
142 | 0 | if bytes.len() % size != 0 { |
143 | 0 | return Err(crate::ule::UleError::length::<Self>(bytes.len())); |
144 | 0 | } |
145 | 0 | 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 | 0 | if chunk == <U as NicheBytes<N>>::NICHE_BIT_PATTERN { |
149 | 0 | Ok(()) |
150 | | } else { |
151 | 0 | U::validate_bytes(chunk) |
152 | | } |
153 | 0 | }) |
154 | 0 | } |
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, PartialOrd, Ord)] |
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> { |
168 | 0 | fn default() -> Self { |
169 | 0 | Self(None) |
170 | 0 | } |
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 | | |
179 | 0 | fn to_unaligned(self) -> Self::ULE { |
180 | 0 | NichedOptionULE::new(self.0.map(U::to_unaligned)) |
181 | 0 | } |
182 | | |
183 | 0 | fn from_unaligned(unaligned: Self::ULE) -> Self { |
184 | 0 | Self(unaligned.get().map(U::from_unaligned)) |
185 | 0 | } |
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 | | |
203 | 0 | fn into_iter(self) -> Self::IntoIter { |
204 | 0 | self.0.into_iter() |
205 | 0 | } |
206 | | } |