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 | |
26 | use super::*; |
27 | use core::fmt; |
28 | use core::mem; |
29 | |
30 | macro_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 | |
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: &[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 ] |
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: &[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 ] |
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: &[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 | |