| 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(C, 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(C, packed)]` on a struct containing only ULE fields) |
| 40 | // 2. TupleULE is aligned to 1 byte. |
| 41 | // (achieved by `#[repr(C, 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 = zerovec.as_bytes(); |
| 141 | let zerovec2 = 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 = 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 = zerovec.as_bytes(); |
| 156 | let zerovec2 = 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 = 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 = zerovec.as_bytes(); |
| 172 | let zerovec2 = 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 = ZeroVec::<(char, i8, u16, u32)>::parse_byte_slice(bytes); |
| 178 | assert!(zerovec3.is_err()); |
| 179 | } |
| 180 | |