//! [`VarULE`] impls for tuples. //! //! This module exports [`Tuple2VarULE`], [`Tuple3VarULE`], ..., the corresponding [`VarULE`] types //! of tuples containing purely [`VarULE`] types. //! //! This can be paired with [`VarTupleULE`] to make arbitrary combinations of [`ULE`] and [`VarULE`] types. //! //! [`VarTupleULE`]: crate::ule::vartuple::VarTupleULE
usesuper::*; usecrate::varzerovec::{Index16, VarZeroVecFormat}; use core::fmt; use core::marker::PhantomData; use core::mem; use zerofrom::ZeroFrom;
macro_rules! tuple_varule { // Invocation: Should be called like `tuple_ule!(Tuple2VarULE, 2, [ A a AX 0, B b BX 1 ])` // // $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 // to the same input field, $i is an index. // // $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 // (This macro code can rely on $i < $len)
($name:ident, $len:literal, [ $($T:ident $t:ident $T_alt: ident $i:tt),+ ]) => { #[doc = concat!("VarULE type for tuples with ", $len, " elements. See module docs for more information")] #[repr(transparent)] #[allow(clippy::exhaustive_structs)] // stable pubstruct $name<$($T: ?Sized,)+ Format: VarZeroVecFormat = Index16> {
$($t: PhantomData<$T>,)+ // Safety invariant: Each "field" $i of the MultiFieldsULE is a valid instance of $t // // In other words, calling `.get_field::<$T>($i)` is always safe. // // This invariant is upheld when this type is constructed during VarULE parsing/validation
multi: MultiFieldsULE<$len, Format>
}
impl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> $name<$($T,)+ Format> {
$( #[doc = concat!("Get field ", $i, "of this tuple")] pubfn $t(&self) -> &$T { // Safety: See invariant of `multi`. unsafe { self.multi.get_field::<$T>($i)
}
}
)+
}
// # Safety // // ## Checklist // // Safety checklist for `VarULE`: // // 1. align(1): repr(transparent) around an align(1) VarULE type: MultiFieldsULE // 2. No padding: see previous point // 3. `validate_bytes` validates that this type is a valid MultiFieldsULE, and that each field is the correct type from the tuple. // 4. `validate_bytes` checks length by deferring to the inner ULEs // 5. `from_bytes_unchecked` returns a fat pointer to the bytes. // 6. All other methods are left at their default impl. // 7. The inner ULEs have byte equality, so this composition has byte equality. unsafeimpl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> VarULE for $name<$($T,)+ Format>
{ fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) // as in the type def let multi = <MultiFieldsULE<$len, Format> as VarULE>::parse_bytes(bytes)?;
$( // Safety invariant: $i < $len, from the macro invocation unsafe {
multi.validate_field::<$T>($i)?;
}
)+
Ok(())
}
unsafefn from_bytes_unchecked(bytes: &[u8]) -> &Self { // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) // as in the type def let multi = <MultiFieldsULE<$len, Format> as VarULE>::from_bytes_unchecked(bytes);
// This type is repr(transparent) over MultiFieldsULE<$len>, so its slices can be transmuted // Field invariant upheld here: validate_bytes above validates every field for being the right type
mem::transmute::<&MultiFieldsULE<$len, Format>, &Self>(multi)
}
}
impl<$($T: Ord + VarULE + ?Sized,)+ Format: VarZeroVecFormat> Ord for $name<$($T,)+ Format> { fn cmp(&self, other: &Self) -> core::cmp::Ordering {
($(self.$t(),)+).cmp(&($(other.$t(),)+))
}
}
// # Safety // // encode_var_ule_len: returns the length of the individual VarULEs together. // // encode_var_ule_write: writes bytes by deferring to the inner VarULE impls. unsafeimpl<$($T,)+ $($T_alt,)+ Format> EncodeAsVarULE<$name<$($T,)+ Format>> for ( $($T_alt),+ ) where
$($T: VarULE + ?Sized,)+
$($T_alt: EncodeAsVarULE<$T>,)+
Format: VarZeroVecFormat,
{ fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R { // unnecessary if the other two are implemented
unreachable!()
}
#[inline] fn encode_var_ule_len(&self) -> usize { // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) // as in the type def
MultiFieldsULE::<$len, Format>::compute_encoded_len_for([$(self.$i.encode_var_ule_len()),+])
}
#[inline] fn encode_var_ule_write(&self, dst: &mut [u8]) { let lengths = [$(self.$i.encode_var_ule_len()),+]; // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) // as in the type def let multi = MultiFieldsULE::<$len, Format>::new_from_lengths_partially_initialized(lengths, dst);
$( // Safety: $i < $len, from the macro invocation, and field $i is supposed to be of type $T unsafe {
multi.set_field_at::<$T, $T_alt>($i, &self.$i);
}
)+
}
}
#[cfg(feature = "serde")] impl<$($T: serde::Serialize,)+ Format> serde::Serialize for $name<$($T,)+ Format> where
$($T: VarULE + ?Sized,)+ // This impl should be present on almost all VarULE types. if it isn't, that is a bug
$(for<'a> &'a $T: ZeroFrom<'a, $T>,)+
Format: VarZeroVecFormat
{ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> whereS: serde::Serializer { if serializer.is_human_readable() { let this = (
$(self.$t()),+
);
<($(&$T),+) as serde::Serialize>::serialize(&this, serializer)
} else {
serializer.serialize_bytes(self.multi.as_bytes())
}
}
}
#[cfg(feature = "serde")] impl<'de, $($T: VarULE + ?Sized,)+ Format> serde::Deserialize<'de> for alloc::boxed::Box<$name<$($T,)+ Format>> where // This impl should be present on almost all deserializable VarULE types
$( alloc::boxed::Box<$T>: serde::Deserialize<'de>,)+
Format: VarZeroVecFormat { fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> where Des: serde::Deserializer<'de> { if deserializer.is_human_readable() { let this = <( $(alloc::boxed::Box<$T>),+) as serde::Deserialize>::deserialize(deserializer)?; let this_ref = (
$(&*this.$i),+
);
Ok(crate::ule::encode_varule_to_box(&this_ref))
} else { // This branch should usually not be hit, since Cow-like use cases will hit the Deserialize impl for &'a TupleNVarULE instead.
let deserialized = <&$name<$($T,)+ Format>>::deserialize(deserializer)?;
Ok(deserialized.to_boxed())
}
}
}
#[cfg(feature = "serde")] impl<'a, 'de: 'a, $($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> serde::Deserialize<'de> for &'a $name<$($T,)+ Format> { fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> where Des: serde::Deserializer<'de> { if deserializer.is_human_readable() {
Err(serde::de::Error::custom(
concat!("&", stringify!($name), " can only deserialize in zero-copy ways"),
))
} else { let bytes = <&[u8]>::deserialize(deserializer)?;
$name::<$($T,)+ Format>::parse_bytes(bytes).map_err(serde::de::Error::custom)
}
}
}
};
}
tuple_varule!(Tuple2VarULE, 2, [ A a AE 0, B b BE 1 ]);
tuple_varule!(Tuple3VarULE, 3, [ A a AE 0, B b BE 1, C c CE 2 ]);
tuple_varule!(Tuple4VarULE, 4, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3 ]);
tuple_varule!(Tuple5VarULE, 5, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3, E e EE 4 ]);
tuple_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 ]);
#[test] fn test_pairvarule_validate() { let vec: Vec<(&str, &[u8])> = vec![("a", b"b"), ("foo", b"bar"), ("lorem", b"ipsum\xFF")]; let zerovec: VarZeroVec<Tuple2VarULE<str, [u8]>> = (&vec).into(); let bytes = zerovec.as_bytes(); let zerovec2 = VarZeroVec::parse_bytes(bytes).unwrap();
assert_eq!(zerovec, zerovec2);
// Test failed validation with a correctly sized but differently constrained tuple // Note: ipsum\xFF is not a valid str let zerovec3 = VarZeroVec::<Tuple2VarULE<str, str>>::parse_bytes(bytes);
assert!(zerovec3.is_err());
#[cfg(feature = "serde")] for val in zerovec.iter() { // Can't use inference due to https://github.com/rust-lang/rust/issues/130180 crate::ule::test_utils::assert_serde_roundtrips::<Tuple2VarULE<str, [u8]>>(val);
}
} fn test_tripleule_validate_inner<Format: VarZeroVecFormat>() { let vec: Vec<(&str, &[u8], VarZeroVec<str>)> = vec![
("a", b"b", (&vec!["a", "b", "c"]).into()),
("foo", b"bar", (&vec!["baz", "quux"]).into()),
( "lorem",
b"ipsum\xFF",
(&vec!["dolor", "sit", "amet"]).into(),
),
]; let zerovec: VarZeroVec<Tuple3VarULE<str, [u8], VarZeroSlice<str>, Format>> = (&vec).into(); let bytes = zerovec.as_bytes(); let zerovec2 = VarZeroVec::parse_bytes(bytes).unwrap();
assert_eq!(zerovec, zerovec2);
// Test failed validation with a correctly sized but differently constrained tuple // Note: the str is unlikely to be a valid varzerovec let zerovec3 = VarZeroVec::<Tuple3VarULE<VarZeroSlice<str>, [u8], VarZeroSlice<str>, Format>>::parse_bytes(bytes);
assert!(zerovec3.is_err());
#[cfg(feature = "serde")] for val in zerovec.iter() { // Can't use inference due to https://github.com/rust-lang/rust/issues/130180 crate::ule::test_utils::assert_serde_roundtrips::<
Tuple3VarULE<str, [u8], VarZeroSlice<str>, Format>,
>(val);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.