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