Coverage Report

Created: 2025-10-21 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}