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 ).
45use core::{marker::Copy, mem::size_of};
67#[cfg(feature = "alloc")]
8use crate::map::ZeroMapKV;
9#[cfg(feature = "alloc")]
10use crate::{ZeroSlice, ZeroVec};
1112use super::{AsULE, ULE};
1314/// 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`].
25pub trait NicheBytes<const N: usize> {
26const NICHE_BIT_PATTERN: [u8; N];
27}
2829/// [`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)]
54pub union NichedOptionULE<U: NicheBytes<N> + ULE, const N: usize> {
55/// Invariant: The value is `niche` only if the bytes equal NICHE_BIT_PATTERN.
56niche: [u8; N],
57/// Invariant: The value is `valid` if the `niche` field does not match NICHE_BIT_PATTERN.
58valid: U,
59}
6061impl<U: NicheBytes<N> + ULE + core::fmt::Debug, const N: usize> core::fmt::Debug62for NichedOptionULE<U, N>
63{
64fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65self.get().fmt(f)
66 }
67}
6869impl<U: NicheBytes<N> + ULE, const N: usize> NichedOptionULE<U, N> {
70/// New `NichedOptionULE<U, N>` from `Option<U>`
71pub fn new(opt: Option<U>) -> Self {
72if !(N == core::mem::size_of::<U>()) {
::core::panicking::panic("assertion failed: N == core::mem::size_of::<U>()")
};assert!(N == core::mem::size_of::<U>());
73match opt {
74Some(u) => Self { valid: u },
75None => Self {
76 niche: <U as NicheBytes<N>>::NICHE_BIT_PATTERN,
77 },
78 }
79 }
8081/// Convert to an `Option<U>`
82pub fn get(self) -> Option<U> {
83// Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U
84unsafe {
85if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
86None87 } else {
88Some(self.valid)
89 }
90 }
91 }
9293/// Borrows as an `Option<&U>`.
94pub fn as_ref(&self) -> Option<&U> {
95// Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U
96unsafe {
97if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
98None99 } else {
100Some(&self.valid)
101 }
102 }
103 }
104}
105106impl<U: NicheBytes<N> + ULE, const N: usize> Copyfor NichedOptionULE<U, N> {}
107108impl<U: NicheBytes<N> + ULE, const N: usize> Clonefor NichedOptionULE<U, N> {
109fn clone(&self) -> Self {
110*self111 }
112}
113114impl<U: NicheBytes<N> + ULE + PartialEq, const N: usize> PartialEqfor NichedOptionULE<U, N> {
115fn eq(&self, other: &Self) -> bool {
116self.get().eq(&other.get())
117 }
118}
119120impl<U: NicheBytes<N> + ULE + Eq, const N: usize> Eqfor NichedOptionULE<U, N> {}
121122/// 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).
134unsafe impl<U: NicheBytes<N> + ULE, const N: usize> ULEfor NichedOptionULE<U, N> {
135fn validate_bytes(bytes: &[u8]) -> Result<(), crate::ule::UleError> {
136let size = size_of::<Self>();
137// The implemention is only correct if NICHE_BIT_PATTERN has same number of bytes as the
138 // type.
139if true {
if !(N == core::mem::size_of::<U>()) {
::core::panicking::panic("assertion failed: N == core::mem::size_of::<U>()")
};
};debug_assert!(N == core::mem::size_of::<U>());
140141// The bytes should fully transmute to a collection of Self
142if bytes.len() % size != 0 {
143return Err(crate::ule::UleError::length::<Self>(bytes.len()));
144 }
145bytes.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
148if chunk == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
149Ok(())
150 } else {
151 U::validate_bytes(chunk)
152 }
153 })
154 }
155}
156157/// 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(#[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::fmt::Debug, const N : usize> ::core::fmt::Debug for
NichedOption<U, N> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "NichedOption",
&&self.0)
}
}Debug, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::marker::Copy, const N : usize> ::core::marker::Copy for
NichedOption<U, N> {
}Copy, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::clone::Clone, const N : usize> ::core::clone::Clone for
NichedOption<U, N> {
#[inline]
fn clone(&self) -> NichedOption<U, N> {
NichedOption(::core::clone::Clone::clone(&self.0))
}
}Clone, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::cmp::PartialEq, const N : usize> ::core::cmp::PartialEq for
NichedOption<U, N> {
#[inline]
fn eq(&self, other: &NichedOption<U, N>) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::cmp::Eq, const N : usize> ::core::cmp::Eq for
NichedOption<U, N> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<Option<U>>;
}
}Eq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::hash::Hash, const N : usize> ::core::hash::Hash for
NichedOption<U, N> {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::cmp::PartialOrd, const N : usize> ::core::cmp::PartialOrd for
NichedOption<U, N> {
#[inline]
fn partial_cmp(&self, other: &NichedOption<U, N>)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
}
}PartialOrd, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<U: ::core::cmp::Ord, const N : usize> ::core::cmp::Ord for
NichedOption<U, N> {
#[inline]
fn cmp(&self, other: &NichedOption<U, N>) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord)]
162#[repr(transparent)]
163#[allow(clippy::exhaustive_structs)] // newtype
164#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
165pub struct NichedOption<U, const N: usize>(pub Option<U>);
166167impl<U, const N: usize> Defaultfor NichedOption<U, N> {
168fn default() -> Self {
169Self(None)
170 }
171}
172173impl<U: AsULE, const N: usize> AsULEfor NichedOption<U, N>
174where
175U::ULE: NicheBytes<N>,
176{
177type ULE = NichedOptionULE<U::ULE, N>;
178179fn to_unaligned(self) -> Self::ULE {
180NichedOptionULE::new(self.0.map(U::to_unaligned))
181 }
182183fn from_unaligned(unaligned: Self::ULE) -> Self {
184Self(unaligned.get().map(U::from_unaligned))
185 }
186}
187188#[cfg(feature = "alloc")]
189impl<'a, T: AsULE + 'static, const N: usize> ZeroMapKV<'a> for NichedOption<T, N>
190where
191T::ULE: NicheBytes<N>,
192{
193type Container = ZeroVec<'a, NichedOption<T, N>>;
194type Slice = ZeroSlice<NichedOption<T, N>>;
195type GetType = <NichedOption<T, N> as AsULE>::ULE;
196type OwnedType = Self;
197}
198199impl<T, const N: usize> IntoIteratorfor NichedOption<T, N> {
200type IntoIter = <Option<T> as IntoIterator>::IntoIter;
201type Item = T;
202203fn into_iter(self) -> Self::IntoIter {
204self.0.into_iter()
205 }
206}