Coverage Report

Created: 2026-05-16 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.6/src/ule/tuple.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
//! ULE impls for tuples.
6
//!
7
//! Rust does not guarantee the layout of tuples, so [`ZeroVec`](crate::ZeroVec) defines its own tuple ULE types.
8
//!
9
//! Impls are defined for tuples of up to 6 elements. For longer tuples, use a custom struct
10
//! with [`#[make_ule]`](crate::make_ule).
11
//!
12
//! # Examples
13
//!
14
//! ```
15
//! use zerovec::ZeroVec;
16
//!
17
//! // ZeroVec of tuples!
18
//! let zerovec: ZeroVec<(u32, char)> = [(1, 'a'), (1234901, '啊'), (100, 'अ')]
19
//!     .iter()
20
//!     .copied()
21
//!     .collect();
22
//!
23
//! assert_eq!(zerovec.get(1), Some((1234901, '啊')));
24
//! ```
25
26
use super::*;
27
use core::fmt;
28
29
macro_rules! tuple_ule {
30
    ($name:ident, $len:literal, [ $($t:ident $i:tt),+ ]) => {
31
        #[doc = concat!("ULE type for tuples with ", $len, " elements.")]
32
        #[repr(C, packed)]
33
        #[allow(clippy::exhaustive_structs)] // stable
34
        pub struct $name<$($t),+>($(pub $t),+);
35
36
        // Safety (based on the safety checklist on the ULE trait):
37
        //  1. TupleULE does not include any uninitialized or padding bytes.
38
        //     (achieved by `#[repr(C, packed)]` on a struct containing only ULE fields)
39
        //  2. TupleULE is aligned to 1 byte.
40
        //     (achieved by `#[repr(C, packed)]` on a struct containing only ULE fields)
41
        //  3. The impl of validate_bytes() returns an error if any byte is not valid.
42
        //  4. The impl of validate_bytes() returns an error if there are extra bytes.
43
        //  5. The other ULE methods use the default impl.
44
        //  6. TupleULE byte equality is semantic equality by relying on the ULE equality
45
        //     invariant on the subfields
46
        unsafe impl<$($t: ULE),+> ULE for $name<$($t),+> {
47
0
            fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
48
                // expands to: 0size + size_of::<A>() + size_of::<B>();
49
0
                let ule_bytes = 0usize $(+ size_of::<$t>())+;
50
0
                if bytes.len() % ule_bytes != 0 {
51
0
                    return Err(UleError::length::<Self>(bytes.len()));
52
0
                }
53
0
                for chunk in bytes.chunks(ule_bytes) {
54
0
                    let mut i = 0;
55
                    $(
56
0
                        let j = i;
57
0
                        i += size_of::<$t>();
58
                        #[expect(clippy::indexing_slicing)] // length checked
59
0
                        <$t>::validate_bytes(&chunk[j..i])?;
60
                    )+
61
                }
62
0
                Ok(())
63
0
            }
Unexecuted instantiation: <zerovec::ule::tuple::Tuple2ULE<_, _> as zerovec::ule::ULE>::validate_bytes
Unexecuted instantiation: <zerovec::ule::tuple::Tuple3ULE<_, _, _> as zerovec::ule::ULE>::validate_bytes
Unexecuted instantiation: <zerovec::ule::tuple::Tuple4ULE<_, _, _, _> as zerovec::ule::ULE>::validate_bytes
Unexecuted instantiation: <zerovec::ule::tuple::Tuple5ULE<_, _, _, _, _> as zerovec::ule::ULE>::validate_bytes
Unexecuted instantiation: <zerovec::ule::tuple::Tuple6ULE<_, _, _, _, _, _> as zerovec::ule::ULE>::validate_bytes
64
        }
65
66
        impl<$($t: AsULE),+> AsULE for ($($t),+) {
67
            type ULE = $name<$(<$t>::ULE),+>;
68
69
            #[inline]
70
0
            fn to_unaligned(self) -> Self::ULE {
71
0
                $name($(
72
0
                    self.$i.to_unaligned()
73
0
                ),+)
74
0
            }
Unexecuted instantiation: <(_, _, _) as zerovec::ule::AsULE>::to_unaligned
Unexecuted instantiation: <(_, _) as zerovec::ule::AsULE>::to_unaligned
Unexecuted instantiation: <(_, _, _, _) as zerovec::ule::AsULE>::to_unaligned
Unexecuted instantiation: <(_, _, _, _, _) as zerovec::ule::AsULE>::to_unaligned
Unexecuted instantiation: <(_, _, _, _, _, _) as zerovec::ule::AsULE>::to_unaligned
75
76
            #[inline]
77
0
            fn from_unaligned(unaligned: Self::ULE) -> Self {
78
0
                ($(
79
0
                    <$t>::from_unaligned(unaligned.$i)
80
0
                ),+)
81
0
            }
Unexecuted instantiation: <(_, _, _) as zerovec::ule::AsULE>::from_unaligned
Unexecuted instantiation: <(_, _) as zerovec::ule::AsULE>::from_unaligned
Unexecuted instantiation: <(_, _, _, _) as zerovec::ule::AsULE>::from_unaligned
Unexecuted instantiation: <(_, _, _, _, _) as zerovec::ule::AsULE>::from_unaligned
Unexecuted instantiation: <(_, _, _, _, _, _) as zerovec::ule::AsULE>::from_unaligned
82
        }
83
84
        impl<$($t: fmt::Debug + ULE),+> fmt::Debug for $name<$($t),+> {
85
0
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
86
0
                ($(self.$i),+).fmt(f)
87
0
            }
Unexecuted instantiation: <zerovec::ule::tuple::Tuple2ULE<_, _> as core::fmt::Debug>::fmt
Unexecuted instantiation: <zerovec::ule::tuple::Tuple3ULE<_, _, _> as core::fmt::Debug>::fmt
Unexecuted instantiation: <zerovec::ule::tuple::Tuple6ULE<_, _, _, _, _, _> as core::fmt::Debug>::fmt
Unexecuted instantiation: <zerovec::ule::tuple::Tuple4ULE<_, _, _, _> as core::fmt::Debug>::fmt
Unexecuted instantiation: <zerovec::ule::tuple::Tuple5ULE<_, _, _, _, _> as core::fmt::Debug>::fmt
88
        }
89
90
        // We need manual impls since `#[derive()]` is disallowed on packed types
91
        impl<$($t: PartialEq + ULE),+> PartialEq for $name<$($t),+> {
92
0
            fn eq(&self, other: &Self) -> bool {
93
0
                ($(self.$i),+).eq(&($(other.$i),+))
94
0
            }
Unexecuted instantiation: <zerovec::ule::tuple::Tuple2ULE<_, _> as core::cmp::PartialEq>::eq
Unexecuted instantiation: <zerovec::ule::tuple::Tuple6ULE<_, _, _, _, _, _> as core::cmp::PartialEq>::eq
Unexecuted instantiation: <zerovec::ule::tuple::Tuple3ULE<_, _, _> as core::cmp::PartialEq>::eq
Unexecuted instantiation: <zerovec::ule::tuple::Tuple4ULE<_, _, _, _> as core::cmp::PartialEq>::eq
Unexecuted instantiation: <zerovec::ule::tuple::Tuple5ULE<_, _, _, _, _> as core::cmp::PartialEq>::eq
95
        }
96
97
        impl<$($t: Eq + ULE),+> Eq for $name<$($t),+> {}
98
99
        impl<$($t: PartialOrd + ULE),+> PartialOrd for $name<$($t),+> {
100
0
            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
101
0
                ($(self.$i),+).partial_cmp(&($(other.$i),+))
102
0
            }
Unexecuted instantiation: <zerovec::ule::tuple::Tuple2ULE<_, _> as core::cmp::PartialOrd>::partial_cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple6ULE<_, _, _, _, _, _> as core::cmp::PartialOrd>::partial_cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple3ULE<_, _, _> as core::cmp::PartialOrd>::partial_cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple4ULE<_, _, _, _> as core::cmp::PartialOrd>::partial_cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple5ULE<_, _, _, _, _> as core::cmp::PartialOrd>::partial_cmp
103
        }
104
105
        impl<$($t: Ord + ULE),+> Ord for $name<$($t),+> {
106
0
            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
107
0
                ($(self.$i),+).cmp(&($(other.$i),+))
108
0
            }
Unexecuted instantiation: <zerovec::ule::tuple::Tuple2ULE<_, _> as core::cmp::Ord>::cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple6ULE<_, _, _, _, _, _> as core::cmp::Ord>::cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple3ULE<_, _, _> as core::cmp::Ord>::cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple4ULE<_, _, _, _> as core::cmp::Ord>::cmp
Unexecuted instantiation: <zerovec::ule::tuple::Tuple5ULE<_, _, _, _, _> as core::cmp::Ord>::cmp
109
        }
110
111
        impl<$($t: ULE),+> Clone for $name<$($t),+> {
112
0
            fn clone(&self) -> Self {
113
0
                *self
114
0
            }
Unexecuted instantiation: <zerovec::ule::tuple::Tuple2ULE<_, _> as core::clone::Clone>::clone
Unexecuted instantiation: <zerovec::ule::tuple::Tuple6ULE<_, _, _, _, _, _> as core::clone::Clone>::clone
Unexecuted instantiation: <zerovec::ule::tuple::Tuple3ULE<_, _, _> as core::clone::Clone>::clone
Unexecuted instantiation: <zerovec::ule::tuple::Tuple4ULE<_, _, _, _> as core::clone::Clone>::clone
Unexecuted instantiation: <zerovec::ule::tuple::Tuple5ULE<_, _, _, _, _> as core::clone::Clone>::clone
115
        }
116
117
        impl<$($t: ULE),+> Copy for $name<$($t),+> {}
118
119
        #[cfg(feature = "alloc")]
120
        impl<'a, $($t: Ord + AsULE + 'static),+> crate::map::ZeroMapKV<'a> for ($($t),+) {
121
            type Container = crate::ZeroVec<'a, ($($t),+)>;
122
            type Slice = crate::ZeroSlice<($($t),+)>;
123
            type GetType = $name<$(<$t>::ULE),+>;
124
            type OwnedType = ($($t),+);
125
        }
126
    };
127
}
128
129
tuple_ule!(Tuple2ULE, "2", [ A 0, B 1 ]);
130
tuple_ule!(Tuple3ULE, "3", [ A 0, B 1, C 2 ]);
131
tuple_ule!(Tuple4ULE, "4", [ A 0, B 1, C 2, D 3 ]);
132
tuple_ule!(Tuple5ULE, "5", [ A 0, B 1, C 2, D 3, E 4 ]);
133
tuple_ule!(Tuple6ULE, "6", [ A 0, B 1, C 2, D 3, E 4, F 5 ]);
134
135
#[test]
136
fn test_pairule_validate() {
137
    use crate::ZeroVec;
138
    let vec: Vec<(u32, char)> = vec![(1, 'a'), (1234901, '啊'), (100, 'अ')];
139
    let zerovec: ZeroVec<(u32, char)> = vec.iter().copied().collect();
140
    let bytes = zerovec.as_bytes();
141
    let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap();
142
    assert_eq!(zerovec, zerovec2);
143
144
    // Test failed validation with a correctly sized but differently constrained tuple
145
    // Note: 1234901 is not a valid char
146
    let zerovec3 = ZeroVec::<(char, u32)>::parse_bytes(bytes);
147
    assert!(zerovec3.is_err());
148
}
149
150
#[test]
151
fn test_tripleule_validate() {
152
    use crate::ZeroVec;
153
    let vec: Vec<(u32, char, i8)> = vec![(1, 'a', -5), (1234901, '啊', 3), (100, 'अ', -127)];
154
    let zerovec: ZeroVec<(u32, char, i8)> = vec.iter().copied().collect();
155
    let bytes = zerovec.as_bytes();
156
    let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap();
157
    assert_eq!(zerovec, zerovec2);
158
159
    // Test failed validation with a correctly sized but differently constrained tuple
160
    // Note: 1234901 is not a valid char
161
    let zerovec3 = ZeroVec::<(char, i8, u32)>::parse_bytes(bytes);
162
    assert!(zerovec3.is_err());
163
}
164
165
#[test]
166
fn test_quadule_validate() {
167
    use crate::ZeroVec;
168
    let vec: Vec<(u32, char, i8, u16)> =
169
        vec![(1, 'a', -5, 3), (1234901, '啊', 3, 11), (100, 'अ', -127, 0)];
170
    let zerovec: ZeroVec<(u32, char, i8, u16)> = vec.iter().copied().collect();
171
    let bytes = zerovec.as_bytes();
172
    let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap();
173
    assert_eq!(zerovec, zerovec2);
174
175
    // Test failed validation with a correctly sized but differently constrained tuple
176
    // Note: 1234901 is not a valid char
177
    let zerovec3 = ZeroVec::<(char, i8, u16, u32)>::parse_bytes(bytes);
178
    assert!(zerovec3.is_err());
179
}