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 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
26use super::*;
27use core::fmt;
28use core::mem;
29
30macro_rules! tuple_ule {
31 ($name:ident, $len:literal, [ $($t:ident $i:tt),+ ]) => {
32 #[doc = concat!("ULE type for tuples with ", $len, " elements.")]
33 #[repr(packed)]
34 #[allow(clippy::exhaustive_structs)] // stable
35 pub struct $name<$($t),+>($(pub $t),+);
36
37 // Safety (based on the safety checklist on the ULE trait):
38 // 1. TupleULE does not include any uninitialized or padding bytes.
39 // (achieved by `#[repr(packed)]` on a struct containing only ULE fields)
40 // 2. TupleULE is aligned to 1 byte.
41 // (achieved by `#[repr(packed)]` on a struct containing only ULE fields)
42 // 3. The impl of validate_byte_slice() returns an error if any byte is not valid.
43 // 4. The impl of validate_byte_slice() returns an error if there are extra bytes.
44 // 5. The other ULE methods use the default impl.
45 // 6. TupleULE byte equality is semantic equality by relying on the ULE equality
46 // invariant on the subfields
47 unsafe impl<$($t: ULE),+> ULE for $name<$($t),+> {
48 fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> {
49 // expands to: 0size + mem::size_of::<A>() + mem::size_of::<B>();
50 let ule_bytes = 0usize $(+ mem::size_of::<$t>())+;
51 if bytes.len() % ule_bytes != 0 {
52 return Err(ZeroVecError::length::<Self>(bytes.len()));
53 }
54 for chunk in bytes.chunks(ule_bytes) {
55 let mut i = 0;
56 $(
57 let j = i;
58 i += mem::size_of::<$t>();
59 #[allow(clippy::indexing_slicing)] // length checked
60 <$t>::validate_byte_slice(&chunk[j..i])?;
61 )+
62 }
63 Ok(())
64 }
65 }
66
67 impl<$($t: AsULE),+> AsULE for ($($t),+) {
68 type ULE = $name<$(<$t>::ULE),+>;
69
70 #[inline]
71 fn to_unaligned(self) -> Self::ULE {
72 $name($(
73 self.$i.to_unaligned()
74 ),+)
75 }
76
77 #[inline]
78 fn from_unaligned(unaligned: Self::ULE) -> Self {
79 ($(
80 <$t>::from_unaligned(unaligned.$i)
81 ),+)
82 }
83 }
84
85 impl<$($t: fmt::Debug + ULE),+> fmt::Debug for $name<$($t),+> {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
87 ($(self.$i),+).fmt(f)
88 }
89 }
90
91 // We need manual impls since `#[derive()]` is disallowed on packed types
92 impl<$($t: PartialEq + ULE),+> PartialEq for $name<$($t),+> {
93 fn eq(&self, other: &Self) -> bool {
94 ($(self.$i),+).eq(&($(other.$i),+))
95 }
96 }
97
98 impl<$($t: Eq + ULE),+> Eq for $name<$($t),+> {}
99
100 impl<$($t: PartialOrd + ULE),+> PartialOrd for $name<$($t),+> {
101 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
102 ($(self.$i),+).partial_cmp(&($(other.$i),+))
103 }
104 }
105
106 impl<$($t: Ord + ULE),+> Ord for $name<$($t),+> {
107 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
108 ($(self.$i),+).cmp(&($(other.$i),+))
109 }
110 }
111
112 impl<$($t: ULE),+> Clone for $name<$($t),+> {
113 fn clone(&self) -> Self {
114 *self
115 }
116 }
117
118 impl<$($t: ULE),+> Copy for $name<$($t),+> {}
119
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
129tuple_ule!(Tuple2ULE, "2", [ A 0, B 1 ]);
130tuple_ule!(Tuple3ULE, "3", [ A 0, B 1, C 2 ]);
131tuple_ule!(Tuple4ULE, "4", [ A 0, B 1, C 2, D 3 ]);
132tuple_ule!(Tuple5ULE, "5", [ A 0, B 1, C 2, D 3, E 4 ]);
133tuple_ule!(Tuple6ULE, "6", [ A 0, B 1, C 2, D 3, E 4, F 5 ]);
134
135#[test]
136fn 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: &[u8] = zerovec.as_bytes();
141 let zerovec2: ZeroVec<'_, (u32, char)> = ZeroVec::parse_byte_slice(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: Result, …> = ZeroVec::<(char, u32)>::parse_byte_slice(bytes);
147 assert!(zerovec3.is_err());
148}
149
150#[test]
151fn 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: &[u8] = zerovec.as_bytes();
156 let zerovec2: ZeroVec<'_, (u32, char, i8)> = ZeroVec::parse_byte_slice(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: Result, …> = ZeroVec::<(char, i8, u32)>::parse_byte_slice(bytes);
162 assert!(zerovec3.is_err());
163}
164
165#[test]
166fn 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: &[u8] = zerovec.as_bytes();
172 let zerovec2: ZeroVec<'_, (u32, char, i8, …)> = ZeroVec::parse_byte_slice(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: Result, …> = ZeroVec::<(char, i8, u16, u32)>::parse_byte_slice(bytes);
178 assert!(zerovec3.is_err());
179}
180