zerovec/ule/
tuplevar.rs

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//! [`VarULE`] impls for tuples.
6//!
7//! This module exports [`Tuple2VarULE`], [`Tuple3VarULE`], ..., the corresponding [`VarULE`] types
8//! of tuples containing purely [`VarULE`] types.
9//!
10//! This can be paired with [`VarTupleULE`] to make arbitrary combinations of [`ULE`] and [`VarULE`] types.
11//!
12//! [`VarTupleULE`]: crate::ule::vartuple::VarTupleULE
13
14use super::*;
15use crate::varzerovec::{Index16, VarZeroVecFormat};
16use core::fmt;
17use core::marker::PhantomData;
18use core::mem;
19use zerofrom::ZeroFrom;
20
21macro_rules! tuple_varule {
22    // Invocation: Should be called like `tuple_ule!(Tuple2VarULE, 2, [ A a AX 0, B b BX 1 ])`
23    //
24    // $T is a generic name, $t is a lowercase version of it, $T_alt is an "alternate" name to use when we need two types referring
25    // to the same input field, $i is an index.
26    //
27    // $name is the name of the type, $len MUST be the total number of fields, and then $i must be an integer going from 0 to (n - 1) in sequence
28    // (This macro code can rely on $i < $len)
29    ($name:ident, $len:literal, [ $($T:ident $t:ident $T_alt: ident $i:tt),+ ]) => {
30        #[doc = concat!("VarULE type for tuples with ", $len, " elements. See module docs for more information")]
31        #[repr(transparent)]
32        #[allow(clippy::exhaustive_structs)] // stable
33        pub struct $name<$($T: ?Sized,)+ Format: VarZeroVecFormat = Index16> {
34            $($t: PhantomData<$T>,)+
35            // Safety invariant: Each "field" $i of the MultiFieldsULE is a valid instance of $t
36            //
37            // In other words, calling `.get_field::<$T>($i)` is always safe.
38            //
39            // This invariant is upheld when this type is constructed during VarULE parsing/validation
40            multi: MultiFieldsULE<$len, Format>
41        }
42
43        impl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> $name<$($T,)+ Format> {
44            $(
45                #[doc = concat!("Get field ", $i, "of this tuple")]
46                pub fn $t(&self) -> &$T {
47                     // Safety: See invariant of `multi`.
48                    unsafe {
49                        self.multi.get_field::<$T>($i)
50                    }
51                }
52
53
54            )+
55        }
56
57        // # Safety
58        //
59        // ## Checklist
60        //
61        // Safety checklist for `VarULE`:
62        //
63        // 1. align(1): repr(transparent) around an align(1) VarULE type: MultiFieldsULE
64        // 2. No padding: see previous point
65        // 3. `validate_bytes` validates that this type is a valid MultiFieldsULE, and that each field is the correct type from the tuple.
66        // 4. `validate_bytes` checks length by deferring to the inner ULEs
67        // 5. `from_bytes_unchecked` returns a fat pointer to the bytes.
68        // 6. All other methods are left at their default impl.
69        // 7. The inner ULEs have byte equality, so this composition has byte equality.
70        unsafe impl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> VarULE for $name<$($T,)+ Format>
71        {
72            fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
73                // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format)
74                // as in the type def
75                let multi = <MultiFieldsULE<$len, Format> as VarULE>::parse_bytes(bytes)?;
76                $(
77                    // Safety invariant: $i < $len, from the macro invocation
78                    unsafe {
79                        multi.validate_field::<$T>($i)?;
80                    }
81                )+
82                Ok(())
83            }
84
85            unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
86                 // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format)
87                // as in the type def
88                let multi = <MultiFieldsULE<$len, Format> as VarULE>::from_bytes_unchecked(bytes);
89
90                // This type is repr(transparent) over MultiFieldsULE<$len>, so its slices can be transmuted
91                // Field invariant upheld here: validate_bytes above validates every field for being the right type
92                mem::transmute::<&MultiFieldsULE<$len, Format>, &Self>(multi)
93            }
94        }
95
96        impl<$($T: fmt::Debug + VarULE + ?Sized,)+ Format: VarZeroVecFormat> fmt::Debug for $name<$($T,)+ Format> {
97            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
98                ($(self.$t(),)+).fmt(f)
99            }
100        }
101
102        // We need manual impls since `#[derive()]` is disallowed on packed types
103        impl<$($T: PartialEq + VarULE + ?Sized,)+ Format: VarZeroVecFormat> PartialEq for $name<$($T,)+ Format> {
104            fn eq(&self, other: &Self) -> bool {
105
106                ($(self.$t(),)+).eq(&($(other.$t(),)+))
107            }
108        }
109
110        impl<$($T: Eq + VarULE + ?Sized,)+ Format: VarZeroVecFormat> Eq for $name<$($T,)+ Format> {}
111
112        impl<$($T: PartialOrd + VarULE + ?Sized,)+ Format: VarZeroVecFormat> PartialOrd for $name<$($T,)+ Format> {
113            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
114                ($(self.$t(),)+).partial_cmp(&($(other.$t(),)+))
115            }
116        }
117
118        impl<$($T: Ord + VarULE + ?Sized,)+ Format: VarZeroVecFormat> Ord for $name<$($T,)+ Format>  {
119            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
120                ($(self.$t(),)+).cmp(&($(other.$t(),)+))
121            }
122        }
123
124        // # Safety
125        //
126        // encode_var_ule_len: returns the length of the individual VarULEs together.
127        //
128        // encode_var_ule_write: writes bytes by deferring to the inner VarULE impls.
129        unsafe impl<$($T,)+ $($T_alt,)+ Format> EncodeAsVarULE<$name<$($T,)+ Format>> for ( $($T_alt),+ )
130        where
131            $($T: VarULE + ?Sized,)+
132            $($T_alt: EncodeAsVarULE<$T>,)+
133            Format: VarZeroVecFormat,
134        {
135            fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R {
136                // unnecessary if the other two are implemented
137                unreachable!()
138            }
139
140            #[inline]
141            fn encode_var_ule_len(&self) -> usize {
142                // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format)
143                // as in the type def
144                MultiFieldsULE::<$len, Format>::compute_encoded_len_for([$(self.$i.encode_var_ule_len()),+])
145            }
146
147            #[inline]
148            fn encode_var_ule_write(&self, dst: &mut [u8]) {
149                let lengths = [$(self.$i.encode_var_ule_len()),+];
150                // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format)
151                // as in the type def
152                let multi = MultiFieldsULE::<$len, Format>::new_from_lengths_partially_initialized(lengths, dst);
153                $(
154                    // Safety: $i < $len, from the macro invocation, and field $i is supposed to be of type $T
155                    unsafe {
156                        multi.set_field_at::<$T, $T_alt>($i, &self.$i);
157                    }
158                )+
159            }
160        }
161
162        #[cfg(feature = "alloc")]
163        impl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> alloc::borrow::ToOwned for $name<$($T,)+ Format> {
164            type Owned = alloc::boxed::Box<Self>;
165            fn to_owned(&self) -> Self::Owned {
166                encode_varule_to_box(self)
167            }
168        }
169
170        impl<'a, $($T,)+ $($T_alt,)+ Format> ZeroFrom <'a, $name<$($T,)+ Format>> for ($($T_alt),+)
171        where
172                    $($T: VarULE + ?Sized,)+
173                    $($T_alt: ZeroFrom<'a, $T>,)+
174                    Format: VarZeroVecFormat {
175            fn zero_from(other: &'a $name<$($T,)+ Format>) -> Self {
176                (
177                    $($T_alt::zero_from(other.$t()),)+
178                )
179            }
180        }
181
182        #[cfg(feature = "serde")]
183        impl<$($T: serde::Serialize,)+ Format> serde::Serialize for $name<$($T,)+ Format>
184        where
185            $($T: VarULE + ?Sized,)+
186            // This impl should be present on almost all VarULE types. if it isn't, that is a bug
187            $(for<'a> &'a $T: ZeroFrom<'a, $T>,)+
188            Format: VarZeroVecFormat
189        {
190            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
191                if serializer.is_human_readable() {
192                    let this = (
193                        $(self.$t()),+
194                    );
195                    <($(&$T),+) as serde::Serialize>::serialize(&this, serializer)
196                } else {
197                    serializer.serialize_bytes(self.multi.as_bytes())
198                }
199            }
200        }
201
202        #[cfg(feature = "serde")]
203        impl<'de, $($T: VarULE + ?Sized,)+ Format> serde::Deserialize<'de> for alloc::boxed::Box<$name<$($T,)+ Format>>
204            where
205                // This impl should be present on almost all deserializable VarULE types
206                $( alloc::boxed::Box<$T>: serde::Deserialize<'de>,)+
207                Format: VarZeroVecFormat {
208            fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> where Des: serde::Deserializer<'de> {
209                if deserializer.is_human_readable() {
210                    let this = <( $(alloc::boxed::Box<$T>),+) as serde::Deserialize>::deserialize(deserializer)?;
211                    let this_ref = (
212                        $(&*this.$i),+
213                    );
214                    Ok(crate::ule::encode_varule_to_box(&this_ref))
215                } else {
216                    // This branch should usually not be hit, since Cow-like use cases will hit the Deserialize impl for &'a TupleNVarULE instead.
217
218                    let deserialized = <&$name<$($T,)+ Format>>::deserialize(deserializer)?;
219                    Ok(deserialized.to_boxed())
220                }
221            }
222        }
223
224        #[cfg(feature = "serde")]
225        impl<'a, 'de: 'a, $($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> serde::Deserialize<'de> for &'a $name<$($T,)+ Format> {
226            fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> where Des: serde::Deserializer<'de> {
227                if deserializer.is_human_readable() {
228                    Err(serde::de::Error::custom(
229                        concat!("&", stringify!($name), " can only deserialize in zero-copy ways"),
230                    ))
231                } else {
232                    let bytes = <&[u8]>::deserialize(deserializer)?;
233                    $name::<$($T,)+ Format>::parse_bytes(bytes).map_err(serde::de::Error::custom)
234                }
235            }
236        }
237    };
238}
239
240tuple_varule!(Tuple2VarULE, 2, [ A a AE 0, B b BE 1 ]);
241tuple_varule!(Tuple3VarULE, 3, [ A a AE 0, B b BE 1, C c CE 2 ]);
242tuple_varule!(Tuple4VarULE, 4, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3 ]);
243tuple_varule!(Tuple5VarULE, 5, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3, E e EE 4 ]);
244tuple_varule!(Tuple6VarULE, 6, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3, E e EE 4, F f FE 5 ]);
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249    use crate::varzerovec::{Index16, Index32, Index8, VarZeroVecFormat};
250    use crate::VarZeroSlice;
251    use crate::VarZeroVec;
252
253    #[test]
254    fn test_pairvarule_validate() {
255        let vec: Vec<(&str, &[u8])> = vec![("a", b"b"), ("foo", b"bar"), ("lorem", b"ipsum\xFF")];
256        let zerovec: VarZeroVec<Tuple2VarULE<str, [u8]>> = (&vec).into();
257        let bytes = zerovec.as_bytes();
258        let zerovec2 = VarZeroVec::parse_bytes(bytes).unwrap();
259        assert_eq!(zerovec, zerovec2);
260
261        // Test failed validation with a correctly sized but differently constrained tuple
262        // Note: ipsum\xFF is not a valid str
263        let zerovec3 = VarZeroVec::<Tuple2VarULE<str, str>>::parse_bytes(bytes);
264        assert!(zerovec3.is_err());
265
266        #[cfg(feature = "serde")]
267        for val in zerovec.iter() {
268            // Can't use inference due to https://github.com/rust-lang/rust/issues/130180
269            crate::ule::test_utils::assert_serde_roundtrips::<Tuple2VarULE<str, [u8]>>(val);
270        }
271    }
272    fn test_tripleule_validate_inner<Format: VarZeroVecFormat>() {
273        let vec: Vec<(&str, &[u8], VarZeroVec<str>)> = vec![
274            ("a", b"b", (&vec!["a", "b", "c"]).into()),
275            ("foo", b"bar", (&vec!["baz", "quux"]).into()),
276            (
277                "lorem",
278                b"ipsum\xFF",
279                (&vec!["dolor", "sit", "amet"]).into(),
280            ),
281        ];
282        let zerovec: VarZeroVec<Tuple3VarULE<str, [u8], VarZeroSlice<str>, Format>> = (&vec).into();
283        let bytes = zerovec.as_bytes();
284        let zerovec2 = VarZeroVec::parse_bytes(bytes).unwrap();
285        assert_eq!(zerovec, zerovec2);
286
287        // Test failed validation with a correctly sized but differently constrained tuple
288        // Note: the str is unlikely to be a valid varzerovec
289        let zerovec3 = VarZeroVec::<Tuple3VarULE<VarZeroSlice<str>, [u8], VarZeroSlice<str>, Format>>::parse_bytes(bytes);
290        assert!(zerovec3.is_err());
291
292        #[cfg(feature = "serde")]
293        for val in zerovec.iter() {
294            // Can't use inference due to https://github.com/rust-lang/rust/issues/130180
295            crate::ule::test_utils::assert_serde_roundtrips::<
296                Tuple3VarULE<str, [u8], VarZeroSlice<str>, Format>,
297            >(val);
298        }
299    }
300
301    #[test]
302    fn test_tripleule_validate() {
303        test_tripleule_validate_inner::<Index8>();
304        test_tripleule_validate_inner::<Index16>();
305        test_tripleule_validate_inner::<Index32>();
306    }
307}