Coverage Report

Created: 2025-07-18 06:22

/rust/registry/src/index.crates.io-6f17d22bba15001f/zerovec-0.10.4/src/ule/niche.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 core::{marker::Copy, mem::size_of};
6
7
use super::{AsULE, ULE};
8
9
/// The [`ULE`] types implementing this trait guarantee that [`NicheBytes::NICHE_BIT_PATTERN`]
10
/// can never occur as a valid byte representation of the type.
11
///
12
/// Guarantees for a valid implementation.
13
/// 1. N must be equal to `core::mem::sizeo_of::<Self>()` or else it will
14
///    cause panics.
15
/// 2. The bit pattern [`NicheBytes::NICHE_BIT_PATTERN`] must not be incorrect as it would lead to
16
///    weird behaviour.
17
/// 3. The abstractions built on top of this trait must panic on an invalid N.
18
/// 4. The abstractions built on this trait that use type punning must ensure that type being
19
///    punned is [`ULE`].
20
pub trait NicheBytes<const N: usize> {
21
    const NICHE_BIT_PATTERN: [u8; N];
22
}
23
24
/// [`ULE`] type for [`NichedOption<U,N>`] where U implements [`NicheBytes`].
25
/// The invalid bit pattern is used as the niche.
26
///
27
/// This uses 1 byte less than [`crate::ule::OptionULE<U>`] to represent [`NichedOption<U,N>`].
28
///
29
/// # Example
30
///
31
/// ```
32
/// use core::num::NonZeroI8;
33
/// use zerovec::ule::NichedOption;
34
/// use zerovec::ZeroVec;
35
///
36
/// let bytes = &[0x00, 0x01, 0x02, 0x00];
37
/// let zv_no: ZeroVec<NichedOption<NonZeroI8, 1>> =
38
///     ZeroVec::parse_byte_slice(bytes)
39
///         .expect("Unable to parse as NichedOption.");
40
///
41
/// assert_eq!(zv_no.get(0).map(|e| e.0), Some(None));
42
/// assert_eq!(zv_no.get(1).map(|e| e.0), Some(NonZeroI8::new(1)));
43
/// assert_eq!(zv_no.get(2).map(|e| e.0), Some(NonZeroI8::new(2)));
44
/// assert_eq!(zv_no.get(3).map(|e| e.0), Some(None));
45
/// ```
46
// Invariants:
47
// The union stores [`NicheBytes::NICHE_BIT_PATTERN`] when None.
48
// Any other bit pattern is a valid.
49
#[repr(C)]
50
pub union NichedOptionULE<U: NicheBytes<N> + ULE, const N: usize> {
51
    /// Invariant: The value is `niche` only if the bytes equal NICHE_BIT_PATTERN.
52
    niche: [u8; N],
53
    /// Invariant: The value is `valid` if the `niche` field does not match NICHE_BIT_PATTERN.
54
    valid: U,
55
}
56
57
impl<U: NicheBytes<N> + ULE + core::fmt::Debug, const N: usize> core::fmt::Debug
58
    for NichedOptionULE<U, N>
59
{
60
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
61
0
        self.get().fmt(f)
62
0
    }
63
}
64
65
impl<U: NicheBytes<N> + ULE, const N: usize> NichedOptionULE<U, N> {
66
    /// New `NichedOptionULE<U, N>` from `Option<U>`
67
0
    pub fn new(opt: Option<U>) -> Self {
68
0
        assert!(N == core::mem::size_of::<U>());
69
0
        match opt {
70
0
            Some(u) => Self { valid: u },
71
0
            None => Self {
72
0
                niche: <U as NicheBytes<N>>::NICHE_BIT_PATTERN,
73
0
            },
74
        }
75
0
    }
76
77
    /// Convert to an `Option<U>`
78
0
    pub fn get(self) -> Option<U> {
79
0
        // Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U
80
0
        unsafe {
81
0
            if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
82
0
                None
83
            } else {
84
0
                Some(self.valid)
85
            }
86
        }
87
0
    }
88
}
89
90
impl<U: NicheBytes<N> + ULE, const N: usize> Copy for NichedOptionULE<U, N> {}
91
92
impl<U: NicheBytes<N> + ULE, const N: usize> Clone for NichedOptionULE<U, N> {
93
0
    fn clone(&self) -> Self {
94
0
        *self
95
0
    }
96
}
97
98
impl<U: NicheBytes<N> + ULE + PartialEq, const N: usize> PartialEq for NichedOptionULE<U, N> {
99
0
    fn eq(&self, other: &Self) -> bool {
100
0
        self.get().eq(&other.get())
101
0
    }
102
}
103
104
impl<U: NicheBytes<N> + ULE + Eq, const N: usize> Eq for NichedOptionULE<U, N> {}
105
106
/// Safety for ULE trait
107
/// 1. NichedOptionULE does not have any padding bytes due to `#[repr(C)]` on a struct
108
///    containing only ULE fields.
109
///    NichedOptionULE either contains NICHE_BIT_PATTERN or valid U byte sequences.
110
///    In both cases the data is initialized.
111
/// 2. NichedOptionULE is aligned to 1 byte due to `#[repr(C, packed)]` on a struct containing only
112
///    ULE fields.
113
/// 3. validate_byte_slice impl returns an error if invalid bytes are encountered.
114
/// 4. validate_byte_slice impl returns an error there are extra bytes.
115
/// 5. The other ULE methods are left to their default impl.
116
/// 6. NichedOptionULE equality is based on ULE equality of the subfield, assuming that NicheBytes
117
///    has been implemented correctly (this is a correctness but not a safety guarantee).
118
unsafe impl<U: NicheBytes<N> + ULE, const N: usize> ULE for NichedOptionULE<U, N> {
119
0
    fn validate_byte_slice(bytes: &[u8]) -> Result<(), crate::ZeroVecError> {
120
0
        let size = size_of::<Self>();
121
0
        // The implemention is only correct if NICHE_BIT_PATTERN has same number of bytes as the
122
0
        // type.
123
0
        debug_assert!(N == core::mem::size_of::<U>());
124
125
        // The bytes should fully transmute to a collection of Self
126
0
        if bytes.len() % size != 0 {
127
0
            return Err(crate::ZeroVecError::length::<Self>(bytes.len()));
128
0
        }
129
0
        bytes.chunks(size).try_for_each(|chunk| {
130
0
            // Associated const cannot be referenced in a pattern
131
0
            // https://doc.rust-lang.org/error-index.html#E0158
132
0
            if chunk == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
133
0
                Ok(())
134
            } else {
135
0
                U::validate_byte_slice(chunk)
136
            }
137
0
        })
138
0
    }
139
}
140
141
/// Optional type which uses [`NichedOptionULE<U,N>`] as ULE type.
142
/// The implementors guarantee that `N == core::mem::sizeo_of::<Self>()`
143
/// [`repr(transparent)`] guarantees that the layout is same as [`Option<U>`]
144
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
145
#[repr(transparent)]
146
#[non_exhaustive]
147
pub struct NichedOption<U, const N: usize>(pub Option<U>);
148
149
impl<U, const N: usize> NichedOption<U, N> {
150
0
    pub const fn new(o: Option<U>) -> Self {
151
0
        Self(o)
152
0
    }
153
}
154
155
impl<U, const N: usize> Default for NichedOption<U, N> {
156
0
    fn default() -> Self {
157
0
        Self(None)
158
0
    }
159
}
160
161
impl<U, const N: usize> From<Option<U>> for NichedOption<U, N> {
162
0
    fn from(o: Option<U>) -> Self {
163
0
        Self(o)
164
0
    }
165
}
166
167
impl<U: AsULE, const N: usize> AsULE for NichedOption<U, N>
168
where
169
    U::ULE: NicheBytes<N>,
170
{
171
    type ULE = NichedOptionULE<U::ULE, N>;
172
173
0
    fn to_unaligned(self) -> Self::ULE {
174
0
        NichedOptionULE::new(self.0.map(U::to_unaligned))
175
0
    }
176
177
0
    fn from_unaligned(unaligned: Self::ULE) -> Self {
178
0
        Self(unaligned.get().map(U::from_unaligned))
179
0
    }
180
}