zerovec/ule/
tuple.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//! 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(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_bytes() returns an error if any byte is not valid.
43        //  4. The impl of validate_bytes() 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_bytes(bytes: &[u8]) -> Result<(), UleError> {
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(UleError::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                        #[expect(clippy::indexing_slicing)] // length checked
60                        <$t>::validate_bytes(&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        #[cfg(feature = "alloc")]
121        impl<'a, $($t: Ord + AsULE + 'static),+> crate::map::ZeroMapKV<'a> for ($($t),+) {
122            type Container = crate::ZeroVec<'a, ($($t),+)>;
123            type Slice = crate::ZeroSlice<($($t),+)>;
124            type GetType = $name<$(<$t>::ULE),+>;
125            type OwnedType = ($($t),+);
126        }
127    };
128}
129
130#[doc = "ULE type for tuples with 2 elements."]
#[repr(C, packed)]
#[allow(clippy :: exhaustive_structs)]
pub struct Tuple2ULE<A, B>(pub A, pub B);
unsafe impl<A: ULE, B: ULE> ULE for Tuple2ULE<A, B> {
    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
        let ule_bytes = 0usize + mem::size_of::<A>() + mem::size_of::<B>();
        if bytes.len() % ule_bytes != 0 {
            return Err(UleError::length::<Self>(bytes.len()));
        }
        for chunk in bytes.chunks(ule_bytes) {
            let mut i = 0;
            let j = i;
            i += mem::size_of::<A>();

            #[expect(clippy :: indexing_slicing)]
            <A>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<B>();

            #[expect(clippy :: indexing_slicing)]
            <B>::validate_bytes(&chunk[j..i])?;
        }
        Ok(())
    }
}
impl<A: AsULE, B: AsULE> AsULE for (A, B) {
    type ULE = Tuple2ULE<<A>::ULE, <B>::ULE>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE {
        Tuple2ULE(self.0.to_unaligned(), self.1.to_unaligned())
    }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        (<A>::from_unaligned(unaligned.0), <B>::from_unaligned(unaligned.1))
    }
}
impl<A: fmt::Debug + ULE, B: fmt::Debug + ULE> fmt::Debug for Tuple2ULE<A, B>
    {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        (self.0, self.1).fmt(f)
    }
}
impl<A: PartialEq + ULE, B: PartialEq + ULE> PartialEq for Tuple2ULE<A, B> {
    fn eq(&self, other: &Self) -> bool {
        (self.0, self.1).eq(&(other.0, other.1))
    }
}
impl<A: Eq + ULE, B: Eq + ULE> Eq for Tuple2ULE<A, B> {}
impl<A: PartialOrd + ULE, B: PartialOrd + ULE> PartialOrd for Tuple2ULE<A, B>
    {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        (self.0, self.1).partial_cmp(&(other.0, other.1))
    }
}
impl<A: Ord + ULE, B: Ord + ULE> Ord for Tuple2ULE<A, B> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        (self.0, self.1).cmp(&(other.0, other.1))
    }
}
impl<A: ULE, B: ULE> Clone for Tuple2ULE<A, B> {
    fn clone(&self) -> Self { *self }
}
impl<A: ULE, B: ULE> Copy for Tuple2ULE<A, B> {}
impl<'a, A: Ord + AsULE + 'static, B: Ord + AsULE + 'static>
    crate::map::ZeroMapKV<'a> for (A, B) {
    type Container = crate::ZeroVec<'a, (A, B)>;
    type Slice = crate::ZeroSlice<(A, B)>;
    type GetType = Tuple2ULE<<A>::ULE, <B>::ULE>;
    type OwnedType = (A, B);
}tuple_ule!(Tuple2ULE, "2", [ A 0, B 1 ]);
131#[doc = "ULE type for tuples with 3 elements."]
#[repr(C, packed)]
#[allow(clippy :: exhaustive_structs)]
pub struct Tuple3ULE<A, B, C>(pub A, pub B, pub C);
unsafe impl<A: ULE, B: ULE, C: ULE> ULE for Tuple3ULE<A, B, C> {
    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
        let ule_bytes =
            0usize + mem::size_of::<A>() + mem::size_of::<B>() +
                mem::size_of::<C>();
        if bytes.len() % ule_bytes != 0 {
            return Err(UleError::length::<Self>(bytes.len()));
        }
        for chunk in bytes.chunks(ule_bytes) {
            let mut i = 0;
            let j = i;
            i += mem::size_of::<A>();

            #[expect(clippy :: indexing_slicing)]
            <A>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<B>();

            #[expect(clippy :: indexing_slicing)]
            <B>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<C>();

            #[expect(clippy :: indexing_slicing)]
            <C>::validate_bytes(&chunk[j..i])?;
        }
        Ok(())
    }
}
impl<A: AsULE, B: AsULE, C: AsULE> AsULE for (A, B, C) {
    type ULE = Tuple3ULE<<A>::ULE, <B>::ULE, <C>::ULE>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE {
        Tuple3ULE(self.0.to_unaligned(), self.1.to_unaligned(),
            self.2.to_unaligned())
    }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        (<A>::from_unaligned(unaligned.0), <B>::from_unaligned(unaligned.1),
            <C>::from_unaligned(unaligned.2))
    }
}
impl<A: fmt::Debug + ULE, B: fmt::Debug + ULE, C: fmt::Debug + ULE> fmt::Debug
    for Tuple3ULE<A, B, C> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        (self.0, self.1, self.2).fmt(f)
    }
}
impl<A: PartialEq + ULE, B: PartialEq + ULE, C: PartialEq + ULE> PartialEq for
    Tuple3ULE<A, B, C> {
    fn eq(&self, other: &Self) -> bool {
        (self.0, self.1, self.2).eq(&(other.0, other.1, other.2))
    }
}
impl<A: Eq + ULE, B: Eq + ULE, C: Eq + ULE> Eq for Tuple3ULE<A, B, C> {}
impl<A: PartialOrd + ULE, B: PartialOrd + ULE, C: PartialOrd + ULE> PartialOrd
    for Tuple3ULE<A, B, C> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        (self.0, self.1, self.2).partial_cmp(&(other.0, other.1, other.2))
    }
}
impl<A: Ord + ULE, B: Ord + ULE, C: Ord + ULE> Ord for Tuple3ULE<A, B, C> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        (self.0, self.1, self.2).cmp(&(other.0, other.1, other.2))
    }
}
impl<A: ULE, B: ULE, C: ULE> Clone for Tuple3ULE<A, B, C> {
    fn clone(&self) -> Self { *self }
}
impl<A: ULE, B: ULE, C: ULE> Copy for Tuple3ULE<A, B, C> {}
impl<'a, A: Ord + AsULE + 'static, B: Ord + AsULE + 'static, C: Ord + AsULE +
    'static> crate::map::ZeroMapKV<'a> for (A, B, C) {
    type Container = crate::ZeroVec<'a, (A, B, C)>;
    type Slice = crate::ZeroSlice<(A, B, C)>;
    type GetType = Tuple3ULE<<A>::ULE, <B>::ULE, <C>::ULE>;
    type OwnedType = (A, B, C);
}tuple_ule!(Tuple3ULE, "3", [ A 0, B 1, C 2 ]);
132#[doc = "ULE type for tuples with 4 elements."]
#[repr(C, packed)]
#[allow(clippy :: exhaustive_structs)]
pub struct Tuple4ULE<A, B, C, D>(pub A, pub B, pub C, pub D);
unsafe impl<A: ULE, B: ULE, C: ULE, D: ULE> ULE for Tuple4ULE<A, B, C, D> {
    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
        let ule_bytes =
            0usize + mem::size_of::<A>() + mem::size_of::<B>() +
                    mem::size_of::<C>() + mem::size_of::<D>();
        if bytes.len() % ule_bytes != 0 {
            return Err(UleError::length::<Self>(bytes.len()));
        }
        for chunk in bytes.chunks(ule_bytes) {
            let mut i = 0;
            let j = i;
            i += mem::size_of::<A>();

            #[expect(clippy :: indexing_slicing)]
            <A>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<B>();

            #[expect(clippy :: indexing_slicing)]
            <B>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<C>();

            #[expect(clippy :: indexing_slicing)]
            <C>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<D>();

            #[expect(clippy :: indexing_slicing)]
            <D>::validate_bytes(&chunk[j..i])?;
        }
        Ok(())
    }
}
impl<A: AsULE, B: AsULE, C: AsULE, D: AsULE> AsULE for (A, B, C, D) {
    type ULE = Tuple4ULE<<A>::ULE, <B>::ULE, <C>::ULE, <D>::ULE>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE {
        Tuple4ULE(self.0.to_unaligned(), self.1.to_unaligned(),
            self.2.to_unaligned(), self.3.to_unaligned())
    }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        (<A>::from_unaligned(unaligned.0), <B>::from_unaligned(unaligned.1),
            <C>::from_unaligned(unaligned.2),
            <D>::from_unaligned(unaligned.3))
    }
}
impl<A: fmt::Debug + ULE, B: fmt::Debug + ULE, C: fmt::Debug + ULE,
    D: fmt::Debug + ULE> fmt::Debug for Tuple4ULE<A, B, C, D> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        (self.0, self.1, self.2, self.3).fmt(f)
    }
}
impl<A: PartialEq + ULE, B: PartialEq + ULE, C: PartialEq + ULE,
    D: PartialEq + ULE> PartialEq for Tuple4ULE<A, B, C, D> {
    fn eq(&self, other: &Self) -> bool {
        (self.0, self.1, self.2,
                self.3).eq(&(other.0, other.1, other.2, other.3))
    }
}
impl<A: Eq + ULE, B: Eq + ULE, C: Eq + ULE, D: Eq + ULE> Eq for
    Tuple4ULE<A, B, C, D> {}
impl<A: PartialOrd + ULE, B: PartialOrd + ULE, C: PartialOrd + ULE,
    D: PartialOrd + ULE> PartialOrd for Tuple4ULE<A, B, C, D> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        (self.0, self.1, self.2,
                self.3).partial_cmp(&(other.0, other.1, other.2, other.3))
    }
}
impl<A: Ord + ULE, B: Ord + ULE, C: Ord + ULE, D: Ord + ULE> Ord for
    Tuple4ULE<A, B, C, D> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        (self.0, self.1, self.2,
                self.3).cmp(&(other.0, other.1, other.2, other.3))
    }
}
impl<A: ULE, B: ULE, C: ULE, D: ULE> Clone for Tuple4ULE<A, B, C, D> {
    fn clone(&self) -> Self { *self }
}
impl<A: ULE, B: ULE, C: ULE, D: ULE> Copy for Tuple4ULE<A, B, C, D> {}
impl<'a, A: Ord + AsULE + 'static, B: Ord + AsULE + 'static, C: Ord + AsULE +
    'static, D: Ord + AsULE + 'static> crate::map::ZeroMapKV<'a> for
    (A, B, C, D) {
    type Container = crate::ZeroVec<'a, (A, B, C, D)>;
    type Slice = crate::ZeroSlice<(A, B, C, D)>;
    type GetType = Tuple4ULE<<A>::ULE, <B>::ULE, <C>::ULE, <D>::ULE>;
    type OwnedType = (A, B, C, D);
}tuple_ule!(Tuple4ULE, "4", [ A 0, B 1, C 2, D 3 ]);
133#[doc = "ULE type for tuples with 5 elements."]
#[repr(C, packed)]
#[allow(clippy :: exhaustive_structs)]
pub struct Tuple5ULE<A, B, C, D, E>(pub A, pub B, pub C, pub D, pub E);
unsafe impl<A: ULE, B: ULE, C: ULE, D: ULE, E: ULE> ULE for
    Tuple5ULE<A, B, C, D, E> {
    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
        let ule_bytes =
            0usize + mem::size_of::<A>() + mem::size_of::<B>() +
                        mem::size_of::<C>() + mem::size_of::<D>() +
                mem::size_of::<E>();
        if bytes.len() % ule_bytes != 0 {
            return Err(UleError::length::<Self>(bytes.len()));
        }
        for chunk in bytes.chunks(ule_bytes) {
            let mut i = 0;
            let j = i;
            i += mem::size_of::<A>();

            #[expect(clippy :: indexing_slicing)]
            <A>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<B>();

            #[expect(clippy :: indexing_slicing)]
            <B>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<C>();

            #[expect(clippy :: indexing_slicing)]
            <C>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<D>();

            #[expect(clippy :: indexing_slicing)]
            <D>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<E>();

            #[expect(clippy :: indexing_slicing)]
            <E>::validate_bytes(&chunk[j..i])?;
        }
        Ok(())
    }
}
impl<A: AsULE, B: AsULE, C: AsULE, D: AsULE, E: AsULE> AsULE for
    (A, B, C, D, E) {
    type ULE = Tuple5ULE<<A>::ULE, <B>::ULE, <C>::ULE, <D>::ULE, <E>::ULE>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE {
        Tuple5ULE(self.0.to_unaligned(), self.1.to_unaligned(),
            self.2.to_unaligned(), self.3.to_unaligned(),
            self.4.to_unaligned())
    }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        (<A>::from_unaligned(unaligned.0), <B>::from_unaligned(unaligned.1),
            <C>::from_unaligned(unaligned.2),
            <D>::from_unaligned(unaligned.3),
            <E>::from_unaligned(unaligned.4))
    }
}
impl<A: fmt::Debug + ULE, B: fmt::Debug + ULE, C: fmt::Debug + ULE,
    D: fmt::Debug + ULE, E: fmt::Debug + ULE> fmt::Debug for
    Tuple5ULE<A, B, C, D, E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        (self.0, self.1, self.2, self.3, self.4).fmt(f)
    }
}
impl<A: PartialEq + ULE, B: PartialEq + ULE, C: PartialEq + ULE,
    D: PartialEq + ULE, E: PartialEq + ULE> PartialEq for
    Tuple5ULE<A, B, C, D, E> {
    fn eq(&self, other: &Self) -> bool {
        (self.0, self.1, self.2, self.3,
                self.4).eq(&(other.0, other.1, other.2, other.3, other.4))
    }
}
impl<A: Eq + ULE, B: Eq + ULE, C: Eq + ULE, D: Eq + ULE, E: Eq + ULE> Eq for
    Tuple5ULE<A, B, C, D, E> {}
impl<A: PartialOrd + ULE, B: PartialOrd + ULE, C: PartialOrd + ULE,
    D: PartialOrd + ULE, E: PartialOrd + ULE> PartialOrd for
    Tuple5ULE<A, B, C, D, E> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        (self.0, self.1, self.2, self.3,
                self.4).partial_cmp(&(other.0, other.1, other.2, other.3,
                    other.4))
    }
}
impl<A: Ord + ULE, B: Ord + ULE, C: Ord + ULE, D: Ord + ULE, E: Ord + ULE> Ord
    for Tuple5ULE<A, B, C, D, E> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        (self.0, self.1, self.2, self.3,
                self.4).cmp(&(other.0, other.1, other.2, other.3, other.4))
    }
}
impl<A: ULE, B: ULE, C: ULE, D: ULE, E: ULE> Clone for
    Tuple5ULE<A, B, C, D, E> {
    fn clone(&self) -> Self { *self }
}
impl<A: ULE, B: ULE, C: ULE, D: ULE, E: ULE> Copy for Tuple5ULE<A, B, C, D, E>
    {}
impl<'a, A: Ord + AsULE + 'static, B: Ord + AsULE + 'static, C: Ord + AsULE +
    'static, D: Ord + AsULE + 'static, E: Ord + AsULE + 'static>
    crate::map::ZeroMapKV<'a> for (A, B, C, D, E) {
    type Container = crate::ZeroVec<'a, (A, B, C, D, E)>;
    type Slice = crate::ZeroSlice<(A, B, C, D, E)>;
    type GetType =
        Tuple5ULE<<A>::ULE, <B>::ULE, <C>::ULE, <D>::ULE, <E>::ULE>;
    type OwnedType = (A, B, C, D, E);
}tuple_ule!(Tuple5ULE, "5", [ A 0, B 1, C 2, D 3, E 4 ]);
134#[doc = "ULE type for tuples with 6 elements."]
#[repr(C, packed)]
#[allow(clippy :: exhaustive_structs)]
pub struct Tuple6ULE<A, B, C, D, E,
    F>(pub A, pub B, pub C, pub D, pub E, pub F);
unsafe impl<A: ULE, B: ULE, C: ULE, D: ULE, E: ULE, F: ULE> ULE for
    Tuple6ULE<A, B, C, D, E, F> {
    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
        let ule_bytes =
            0usize + mem::size_of::<A>() + mem::size_of::<B>() +
                            mem::size_of::<C>() + mem::size_of::<D>() +
                    mem::size_of::<E>() + mem::size_of::<F>();
        if bytes.len() % ule_bytes != 0 {
            return Err(UleError::length::<Self>(bytes.len()));
        }
        for chunk in bytes.chunks(ule_bytes) {
            let mut i = 0;
            let j = i;
            i += mem::size_of::<A>();

            #[expect(clippy :: indexing_slicing)]
            <A>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<B>();

            #[expect(clippy :: indexing_slicing)]
            <B>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<C>();

            #[expect(clippy :: indexing_slicing)]
            <C>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<D>();

            #[expect(clippy :: indexing_slicing)]
            <D>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<E>();

            #[expect(clippy :: indexing_slicing)]
            <E>::validate_bytes(&chunk[j..i])?;
            let j = i;
            i += mem::size_of::<F>();

            #[expect(clippy :: indexing_slicing)]
            <F>::validate_bytes(&chunk[j..i])?;
        }
        Ok(())
    }
}
impl<A: AsULE, B: AsULE, C: AsULE, D: AsULE, E: AsULE, F: AsULE> AsULE for
    (A, B, C, D, E, F) {
    type ULE =
        Tuple6ULE<<A>::ULE, <B>::ULE, <C>::ULE, <D>::ULE, <E>::ULE, <F>::ULE>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE {
        Tuple6ULE(self.0.to_unaligned(), self.1.to_unaligned(),
            self.2.to_unaligned(), self.3.to_unaligned(),
            self.4.to_unaligned(), self.5.to_unaligned())
    }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        (<A>::from_unaligned(unaligned.0), <B>::from_unaligned(unaligned.1),
            <C>::from_unaligned(unaligned.2),
            <D>::from_unaligned(unaligned.3),
            <E>::from_unaligned(unaligned.4),
            <F>::from_unaligned(unaligned.5))
    }
}
impl<A: fmt::Debug + ULE, B: fmt::Debug + ULE, C: fmt::Debug + ULE,
    D: fmt::Debug + ULE, E: fmt::Debug + ULE, F: fmt::Debug + ULE> fmt::Debug
    for Tuple6ULE<A, B, C, D, E, F> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        (self.0, self.1, self.2, self.3, self.4, self.5).fmt(f)
    }
}
impl<A: PartialEq + ULE, B: PartialEq + ULE, C: PartialEq + ULE,
    D: PartialEq + ULE, E: PartialEq + ULE, F: PartialEq + ULE> PartialEq for
    Tuple6ULE<A, B, C, D, E, F> {
    fn eq(&self, other: &Self) -> bool {
        (self.0, self.1, self.2, self.3, self.4,
                self.5).eq(&(other.0, other.1, other.2, other.3, other.4,
                    other.5))
    }
}
impl<A: Eq + ULE, B: Eq + ULE, C: Eq + ULE, D: Eq + ULE, E: Eq + ULE, F: Eq +
    ULE> Eq for Tuple6ULE<A, B, C, D, E, F> {}
impl<A: PartialOrd + ULE, B: PartialOrd + ULE, C: PartialOrd + ULE,
    D: PartialOrd + ULE, E: PartialOrd + ULE, F: PartialOrd + ULE> PartialOrd
    for Tuple6ULE<A, B, C, D, E, F> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        (self.0, self.1, self.2, self.3, self.4,
                self.5).partial_cmp(&(other.0, other.1, other.2, other.3,
                    other.4, other.5))
    }
}
impl<A: Ord + ULE, B: Ord + ULE, C: Ord + ULE, D: Ord + ULE, E: Ord + ULE,
    F: Ord + ULE> Ord for Tuple6ULE<A, B, C, D, E, F> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        (self.0, self.1, self.2, self.3, self.4,
                self.5).cmp(&(other.0, other.1, other.2, other.3, other.4,
                    other.5))
    }
}
impl<A: ULE, B: ULE, C: ULE, D: ULE, E: ULE, F: ULE> Clone for
    Tuple6ULE<A, B, C, D, E, F> {
    fn clone(&self) -> Self { *self }
}
impl<A: ULE, B: ULE, C: ULE, D: ULE, E: ULE, F: ULE> Copy for
    Tuple6ULE<A, B, C, D, E, F> {}
impl<'a, A: Ord + AsULE + 'static, B: Ord + AsULE + 'static, C: Ord + AsULE +
    'static, D: Ord + AsULE + 'static, E: Ord + AsULE + 'static, F: Ord +
    AsULE + 'static> crate::map::ZeroMapKV<'a> for (A, B, C, D, E, F) {
    type Container = crate::ZeroVec<'a, (A, B, C, D, E, F)>;
    type Slice = crate::ZeroSlice<(A, B, C, D, E, F)>;
    type GetType =
        Tuple6ULE<<A>::ULE, <B>::ULE, <C>::ULE, <D>::ULE, <E>::ULE, <F>::ULE>;
    type OwnedType = (A, B, C, D, E, F);
}tuple_ule!(Tuple6ULE, "6", [ A 0, B 1, C 2, D 3, E 4, F 5 ]);
135
136#[test]
137fn test_pairule_validate() {
138    use crate::ZeroVec;
139    let vec: Vec<(u32, char)> = vec![(1, 'a'), (1234901, '啊'), (100, 'अ')];
140    let zerovec: ZeroVec<(u32, char)> = vec.iter().copied().collect();
141    let bytes = zerovec.as_bytes();
142    let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap();
143    assert_eq!(zerovec, zerovec2);
144
145    // Test failed validation with a correctly sized but differently constrained tuple
146    // Note: 1234901 is not a valid char
147    let zerovec3 = ZeroVec::<(char, u32)>::parse_bytes(bytes);
148    assert!(zerovec3.is_err());
149}
150
151#[test]
152fn test_tripleule_validate() {
153    use crate::ZeroVec;
154    let vec: Vec<(u32, char, i8)> = vec![(1, 'a', -5), (1234901, '啊', 3), (100, 'अ', -127)];
155    let zerovec: ZeroVec<(u32, char, i8)> = vec.iter().copied().collect();
156    let bytes = zerovec.as_bytes();
157    let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap();
158    assert_eq!(zerovec, zerovec2);
159
160    // Test failed validation with a correctly sized but differently constrained tuple
161    // Note: 1234901 is not a valid char
162    let zerovec3 = ZeroVec::<(char, i8, u32)>::parse_bytes(bytes);
163    assert!(zerovec3.is_err());
164}
165
166#[test]
167fn test_quadule_validate() {
168    use crate::ZeroVec;
169    let vec: Vec<(u32, char, i8, u16)> =
170        vec![(1, 'a', -5, 3), (1234901, '啊', 3, 11), (100, 'अ', -127, 0)];
171    let zerovec: ZeroVec<(u32, char, i8, u16)> = vec.iter().copied().collect();
172    let bytes = zerovec.as_bytes();
173    let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap();
174    assert_eq!(zerovec, zerovec2);
175
176    // Test failed validation with a correctly sized but differently constrained tuple
177    // Note: 1234901 is not a valid char
178    let zerovec3 = ZeroVec::<(char, i8, u16, u32)>::parse_bytes(bytes);
179    assert!(zerovec3.is_err());
180}