Skip to main content

zerovec/ule/
plain.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#![allow(clippy::upper_case_acronyms)]
6//! ULE implementation for Plain Old Data types, including all sized integers.
7
8use super::*;
9use crate::impl_ule_from_array;
10use crate::ZeroSlice;
11use core::num::{NonZeroI8, NonZeroU8};
12
13/// A u8 array of little-endian data with infallible conversions to and from &[u8].
14#[repr(transparent)]
15#[derive(#[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::fmt::Debug for RawBytesULE<N> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "RawBytesULE",
            &&self.0)
    }
}Debug, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::cmp::PartialEq for RawBytesULE<N> {
    #[inline]
    fn eq(&self, other: &RawBytesULE<N>) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::cmp::Eq for RawBytesULE<N> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<[u8; N]>;
    }
}Eq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::clone::Clone for RawBytesULE<N> {
    #[inline]
    fn clone(&self) -> RawBytesULE<N> {
        let _: ::core::clone::AssertParamIsClone<[u8; N]>;
        *self
    }
}Clone, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::marker::Copy for RawBytesULE<N> { }Copy, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::cmp::PartialOrd for RawBytesULE<N> {
    #[inline]
    fn partial_cmp(&self, other: &RawBytesULE<N>)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
    }
}PartialOrd, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::cmp::Ord for RawBytesULE<N> {
    #[inline]
    fn cmp(&self, other: &RawBytesULE<N>) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<const N : usize> ::core::hash::Hash for RawBytesULE<N> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash)]
16#[allow(clippy::exhaustive_structs)] // newtype
17pub struct RawBytesULE<const N: usize>(pub [u8; N]);
18
19impl<const N: usize> RawBytesULE<N> {
20    #[inline]
21    pub fn as_bytes(&self) -> &[u8] {
22        &self.0
23    }
24
25    #[inline]
26    pub fn from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] {
27        let data = bytes.as_mut_ptr();
28        let len = bytes.len() / N;
29        // Safe because Self is transparent over [u8; N]
30        unsafe { slice::from_raw_parts_mut(data as *mut Self, len) }
31    }
32}
33
34// Safety (based on the safety checklist on the ULE trait):
35//  1. RawBytesULE does not include any uninitialized or padding bytes.
36//     (achieved by `#[repr(transparent)]` on a type that satisfies this invariant)
37//  2. RawBytesULE is aligned to 1 byte.
38//     (achieved by `#[repr(transparent)]` on a type that satisfies this invariant)
39//  3. The impl of validate_bytes() returns an error if any byte is not valid (never).
40//  4. The impl of validate_bytes() returns an error if there are leftover bytes.
41//  5. The other ULE methods use the default impl.
42//  6. RawBytesULE byte equality is semantic equality
43unsafe impl<const N: usize> ULE for RawBytesULE<N> {
44    #[inline]
45    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
46        if bytes.len() % N == 0 {
47            // Safe because Self is transparent over [u8; N]
48            Ok(())
49        } else {
50            Err(UleError::length::<Self>(bytes.len()))
51        }
52    }
53}
54
55impl<const N: usize> From<[u8; N]> for RawBytesULE<N> {
56    #[inline]
57    fn from(le_bytes: [u8; N]) -> Self {
58        Self(le_bytes)
59    }
60}
61
62macro_rules! impl_numbers_with_raw_bytes_ule {
63    ($unsigned:ty, $signed:ty $(, $float:ty)?) => {
64        const _: () = assert!(size_of::<$unsigned>() == size_of::<$signed>() $(&& size_of::<$unsigned>() == size_of::<$float>())?);
65
66        impl RawBytesULE<{ size_of::<$unsigned>() }> {
67            #[doc = concat!("Gets this `RawBytesULE` as a `", stringify!($unsigned), "`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`", stringify!($unsigned), "`].")]
68            #[inline]
69            pub const fn as_unsigned_int(&self) -> $unsigned {
70                <$unsigned>::from_le_bytes(self.0)
71            }
72
73            #[doc = concat!("Gets this `RawBytesULE` as a `", stringify!($unsigned), "`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`", stringify!($signed), "`].")]
74            #[inline]
75            pub const fn as_signed_int(&self) -> $signed {
76                <$signed>::from_le_bytes(self.0)
77            }
78
79            $(
80                #[doc = concat!("Gets this `RawBytesULE` as a `", stringify!($float), "`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`", stringify!($float), "`].")]
81                #[inline]
82                pub const fn as_float(&self) -> $float {
83                    <$float>::from_le_bytes(self.0)
84                }
85            )?
86
87            #[doc = concat!("Converts a `", stringify!($unsigned), "` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on [`", stringify!($unsigned), "`].")]
88            #[inline]
89            pub const fn from_aligned(value: $unsigned) -> Self {
90                Self(value.to_le_bytes())
91            }
92
93            impl_ule_from_array!(
94                $unsigned,
95                RawBytesULE<{ size_of::<$unsigned>() }>,
96                RawBytesULE([0; { size_of::<$unsigned>() }])
97            );
98        }
99
100        impl_byte_slice_type!(from_unsigned, $unsigned);
101        impl_const_constructors!($unsigned);
102
103        impl_byte_slice_type!(from_signed, $signed);
104        impl_const_constructors!($signed);
105
106        $(
107            // These impls are actually safe and portable due to Rust always using IEEE 754, see the documentation
108            // on f32::from_le_bytes: https://doc.rust-lang.org/stable/std/primitive.f32.html#method.from_le_bytes
109            //
110            // The only potential problem is that some older platforms treat signaling NaNs differently. This is
111            // still quite portable, signalingness is not typically super important.
112
113            // The from_bits documentation mentions that they have identical byte representations to integers
114            // and EqULE only cares about LE systems
115            impl_byte_slice_type!(from_float, $float);
116            impl_const_constructors!($float);
117        )?
118};
119}
120
121macro_rules! impl_const_constructors {
122    ($base:ty) => {
123        impl ZeroSlice<$base> {
124            /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
125            /// parsing checks.
126            ///
127            /// This cannot be generic over `T` because of current limitations in `const`, but if
128            /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
129            /// instead.
130            ///
131            /// See [`ZeroSlice::cast()`] for an example.
132            pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
133                let len = bytes.len();
134                const STRIDE: usize = size_of::<$base>();
135                #[allow(clippy::modulo_one)]
136                if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
137                    Ok(unsafe { Self::from_bytes_unchecked(bytes) })
138                } else {
139                    Err(UleError::InvalidLength {
140                        ty: concat!("<const construct: ", stringify!($base), ">"),
141                        len,
142                    })
143                }
144            }
145        }
146    };
147}
148
149macro_rules! impl_byte_slice_type {
150    ($single_fn:ident, $type:ty) => {
151        impl From<$type> for RawBytesULE<{ size_of::<$type>() }> {
152            #[inline]
153            fn from(value: $type) -> Self {
154                Self(value.to_le_bytes())
155            }
156        }
157        impl AsULE for $type {
158            type ULE = RawBytesULE<{ size_of::<$type>() }>;
159            #[inline]
160            fn to_unaligned(self) -> Self::ULE {
161                RawBytesULE(self.to_le_bytes())
162            }
163            #[inline]
164            fn from_unaligned(unaligned: Self::ULE) -> Self {
165                <$type>::from_le_bytes(unaligned.0)
166            }
167        }
168        // EqULE is true because $type and RawBytesULE<{ size_of::<$type> }>
169        // have the same byte sequence on little-endian
170        unsafe impl EqULE for $type {}
171
172        impl RawBytesULE<{ size_of::<$type>() }> {
173            pub const fn $single_fn(v: $type) -> Self {
174                RawBytesULE(v.to_le_bytes())
175            }
176        }
177    };
178}
179
180const _: () =
    if !(size_of::<u16>() == size_of::<i16>()) {
        ::core::panicking::panic("assertion failed: size_of::<u16>() == size_of::<i16>()")
    };
impl RawBytesULE<{ size_of::<u16>() }> {
    #[doc =
    "Gets this `RawBytesULE` as a `u16`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`u16`]."]
    #[inline]
    pub const fn as_unsigned_int(&self) -> u16 {
        <u16>::from_le_bytes(self.0)
    }
    #[doc =
    "Gets this `RawBytesULE` as a `u16`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`i16`]."]
    #[inline]
    pub const fn as_signed_int(&self) -> i16 { <i16>::from_le_bytes(self.0) }
    #[doc =
    "Converts a `u16` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on [`u16`]."]
    #[inline]
    pub const fn from_aligned(value: u16) -> Self {
        Self(value.to_le_bytes())
    }
    #[doc =
    "Convert an array of `u16` to an array of `RawBytesULE < { size_of :: < u16 > () }>`."]
    pub const fn from_array<const N : usize>(arr: [u16; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; { size_of::<u16>() }]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}
impl From<u16> for RawBytesULE<{ size_of::<u16>() }> {
    #[inline]
    fn from(value: u16) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u16 {
    type ULE = RawBytesULE<{ size_of::<u16>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <u16>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for u16 {}
impl RawBytesULE<{ size_of::<u16>() }> {
    pub const fn from_unsigned(v: u16) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}
impl ZeroSlice<u16> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<u16>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: u16>", len })
        }
    }
}
impl From<i16> for RawBytesULE<{ size_of::<i16>() }> {
    #[inline]
    fn from(value: i16) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i16 {
    type ULE = RawBytesULE<{ size_of::<i16>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <i16>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for i16 {}
impl RawBytesULE<{ size_of::<i16>() }> {
    pub const fn from_signed(v: i16) -> Self { RawBytesULE(v.to_le_bytes()) }
}
impl ZeroSlice<i16> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<i16>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: i16>", len })
        }
    }
}impl_numbers_with_raw_bytes_ule!(u16, i16);
181const _: () =
    if !(size_of::<u32>() == size_of::<i32>() &&
                size_of::<u32>() == size_of::<f32>()) {
        ::core::panicking::panic("assertion failed: size_of::<u32>() == size_of::<i32>() && size_of::<u32>() == size_of::<f32>()")
    };
impl RawBytesULE<{ size_of::<u32>() }> {
    #[doc =
    "Gets this `RawBytesULE` as a `u32`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`u32`]."]
    #[inline]
    pub const fn as_unsigned_int(&self) -> u32 {
        <u32>::from_le_bytes(self.0)
    }
    #[doc =
    "Gets this `RawBytesULE` as a `u32`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`i32`]."]
    #[inline]
    pub const fn as_signed_int(&self) -> i32 { <i32>::from_le_bytes(self.0) }
    #[doc =
    "Gets this `RawBytesULE` as a `f32`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`f32`]."]
    #[inline]
    pub const fn as_float(&self) -> f32 { <f32>::from_le_bytes(self.0) }
    #[doc =
    "Converts a `u32` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on [`u32`]."]
    #[inline]
    pub const fn from_aligned(value: u32) -> Self {
        Self(value.to_le_bytes())
    }
    #[doc =
    "Convert an array of `u32` to an array of `RawBytesULE < { size_of :: < u32 > () }>`."]
    pub const fn from_array<const N : usize>(arr: [u32; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; { size_of::<u32>() }]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}
impl From<u32> for RawBytesULE<{ size_of::<u32>() }> {
    #[inline]
    fn from(value: u32) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u32 {
    type ULE = RawBytesULE<{ size_of::<u32>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <u32>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for u32 {}
impl RawBytesULE<{ size_of::<u32>() }> {
    pub const fn from_unsigned(v: u32) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}
impl ZeroSlice<u32> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<u32>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: u32>", len })
        }
    }
}
impl From<i32> for RawBytesULE<{ size_of::<i32>() }> {
    #[inline]
    fn from(value: i32) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i32 {
    type ULE = RawBytesULE<{ size_of::<i32>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <i32>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for i32 {}
impl RawBytesULE<{ size_of::<i32>() }> {
    pub const fn from_signed(v: i32) -> Self { RawBytesULE(v.to_le_bytes()) }
}
impl ZeroSlice<i32> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<i32>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: i32>", len })
        }
    }
}
impl From<f32> for RawBytesULE<{ size_of::<f32>() }> {
    #[inline]
    fn from(value: f32) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for f32 {
    type ULE = RawBytesULE<{ size_of::<f32>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <f32>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for f32 {}
impl RawBytesULE<{ size_of::<f32>() }> {
    pub const fn from_float(v: f32) -> Self { RawBytesULE(v.to_le_bytes()) }
}
impl ZeroSlice<f32> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<f32>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: f32>", len })
        }
    }
}impl_numbers_with_raw_bytes_ule!(u32, i32, f32);
182const _: () =
    if !(size_of::<u64>() == size_of::<i64>() &&
                size_of::<u64>() == size_of::<f64>()) {
        ::core::panicking::panic("assertion failed: size_of::<u64>() == size_of::<i64>() && size_of::<u64>() == size_of::<f64>()")
    };
impl RawBytesULE<{ size_of::<u64>() }> {
    #[doc =
    "Gets this `RawBytesULE` as a `u64`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`u64`]."]
    #[inline]
    pub const fn as_unsigned_int(&self) -> u64 {
        <u64>::from_le_bytes(self.0)
    }
    #[doc =
    "Gets this `RawBytesULE` as a `u64`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`i64`]."]
    #[inline]
    pub const fn as_signed_int(&self) -> i64 { <i64>::from_le_bytes(self.0) }
    #[doc =
    "Gets this `RawBytesULE` as a `f64`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`f64`]."]
    #[inline]
    pub const fn as_float(&self) -> f64 { <f64>::from_le_bytes(self.0) }
    #[doc =
    "Converts a `u64` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on [`u64`]."]
    #[inline]
    pub const fn from_aligned(value: u64) -> Self {
        Self(value.to_le_bytes())
    }
    #[doc =
    "Convert an array of `u64` to an array of `RawBytesULE < { size_of :: < u64 > () }>`."]
    pub const fn from_array<const N : usize>(arr: [u64; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; { size_of::<u64>() }]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}
impl From<u64> for RawBytesULE<{ size_of::<u64>() }> {
    #[inline]
    fn from(value: u64) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u64 {
    type ULE = RawBytesULE<{ size_of::<u64>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <u64>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for u64 {}
impl RawBytesULE<{ size_of::<u64>() }> {
    pub const fn from_unsigned(v: u64) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}
impl ZeroSlice<u64> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<u64>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: u64>", len })
        }
    }
}
impl From<i64> for RawBytesULE<{ size_of::<i64>() }> {
    #[inline]
    fn from(value: i64) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i64 {
    type ULE = RawBytesULE<{ size_of::<i64>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <i64>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for i64 {}
impl RawBytesULE<{ size_of::<i64>() }> {
    pub const fn from_signed(v: i64) -> Self { RawBytesULE(v.to_le_bytes()) }
}
impl ZeroSlice<i64> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<i64>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: i64>", len })
        }
    }
}
impl From<f64> for RawBytesULE<{ size_of::<f64>() }> {
    #[inline]
    fn from(value: f64) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for f64 {
    type ULE = RawBytesULE<{ size_of::<f64>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <f64>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for f64 {}
impl RawBytesULE<{ size_of::<f64>() }> {
    pub const fn from_float(v: f64) -> Self { RawBytesULE(v.to_le_bytes()) }
}
impl ZeroSlice<f64> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<f64>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: f64>", len })
        }
    }
}impl_numbers_with_raw_bytes_ule!(u64, i64, f64);
183const _: () =
    if !(size_of::<u128>() == size_of::<i128>()) {
        ::core::panicking::panic("assertion failed: size_of::<u128>() == size_of::<i128>()")
    };
impl RawBytesULE<{ size_of::<u128>() }> {
    #[doc =
    "Gets this `RawBytesULE` as a `u128`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`u128`]."]
    #[inline]
    pub const fn as_unsigned_int(&self) -> u128 {
        <u128>::from_le_bytes(self.0)
    }
    #[doc =
    "Gets this `RawBytesULE` as a `u128`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`i128`]."]
    #[inline]
    pub const fn as_signed_int(&self) -> i128 {
        <i128>::from_le_bytes(self.0)
    }
    #[doc =
    "Converts a `u128` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on [`u128`]."]
    #[inline]
    pub const fn from_aligned(value: u128) -> Self {
        Self(value.to_le_bytes())
    }
    #[doc =
    "Convert an array of `u128` to an array of `RawBytesULE < { size_of :: < u128 > () }>`."]
    pub const fn from_array<const N : usize>(arr: [u128; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; { size_of::<u128>() }]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}
impl From<u128> for RawBytesULE<{ size_of::<u128>() }> {
    #[inline]
    fn from(value: u128) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u128 {
    type ULE = RawBytesULE<{ size_of::<u128>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <u128>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for u128 {}
impl RawBytesULE<{ size_of::<u128>() }> {
    pub const fn from_unsigned(v: u128) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}
impl ZeroSlice<u128> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<u128>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength {
                    ty: "<const construct: u128>",
                    len,
                })
        }
    }
}
impl From<i128> for RawBytesULE<{ size_of::<i128>() }> {
    #[inline]
    fn from(value: i128) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i128 {
    type ULE = RawBytesULE<{ size_of::<i128>() }>;
    #[inline]
    fn to_unaligned(self) -> Self::ULE { RawBytesULE(self.to_le_bytes()) }
    #[inline]
    fn from_unaligned(unaligned: Self::ULE) -> Self {
        <i128>::from_le_bytes(unaligned.0)
    }
}
unsafe impl EqULE for i128 {}
impl RawBytesULE<{ size_of::<i128>() }> {
    pub const fn from_signed(v: i128) -> Self { RawBytesULE(v.to_le_bytes()) }
}
impl ZeroSlice<i128> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<i128>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength {
                    ty: "<const construct: i128>",
                    len,
                })
        }
    }
}impl_numbers_with_raw_bytes_ule!(u128, i128);
184
185// Safety (based on the safety checklist on the ULE trait):
186//  1. u8 does not include any uninitialized or padding bytes.
187//  2. u8 is aligned to 1 byte.
188//  3. The impl of validate_bytes() returns an error if any byte is not valid (never).
189//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
190//  5. The other ULE methods use the default impl.
191//  6. u8 byte equality is semantic equality
192unsafe impl ULE for u8 {
193    #[inline]
194    fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> {
195        Ok(())
196    }
197}
198
199impl AsULE for u8 {
200    type ULE = Self;
201    #[inline]
202    fn to_unaligned(self) -> Self::ULE {
203        self
204    }
205    #[inline]
206    fn from_unaligned(unaligned: Self::ULE) -> Self {
207        unaligned
208    }
209}
210
211// EqULE is true because u8 is its own ULE.
212unsafe impl EqULE for u8 {}
213
214impl ZeroSlice<u8> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<u8>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: u8>", len })
        }
    }
}impl_const_constructors!(u8);
215
216// Safety (based on the safety checklist on the ULE trait):
217//  1. NonZeroU8 does not include any uninitialized or padding bytes.
218//  2. NonZeroU8 is aligned to 1 byte.
219//  3. The impl of validate_bytes() returns an error if any byte is not valid (0x00).
220//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
221//  5. The other ULE methods use the default impl.
222//  6. NonZeroU8 byte equality is semantic equality
223unsafe impl ULE for NonZeroU8 {
224    #[inline]
225    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
226        bytes.iter().try_for_each(|b| {
227            if *b == 0x00 {
228                Err(UleError::parse::<Self>())
229            } else {
230                Ok(())
231            }
232        })
233    }
234}
235
236impl AsULE for NonZeroU8 {
237    type ULE = Self;
238    #[inline]
239    fn to_unaligned(self) -> Self::ULE {
240        self
241    }
242    #[inline]
243    fn from_unaligned(unaligned: Self::ULE) -> Self {
244        unaligned
245    }
246}
247
248unsafe impl EqULE for NonZeroU8 {}
249
250impl NicheBytes<1> for NonZeroU8 {
251    const NICHE_BIT_PATTERN: [u8; 1] = [0x00];
252}
253
254// Safety (based on the safety checklist on the ULE trait):
255//  1. i8 does not include any uninitialized or padding bytes.
256//  2. i8 is aligned to 1 byte.
257//  3. The impl of validate_bytes() returns an error if any byte is not valid (never).
258//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
259//  5. The other ULE methods use the default impl.
260//  6. i8 byte equality is semantic equality
261unsafe impl ULE for i8 {
262    #[inline]
263    fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> {
264        Ok(())
265    }
266}
267
268impl AsULE for i8 {
269    type ULE = Self;
270    #[inline]
271    fn to_unaligned(self) -> Self::ULE {
272        self
273    }
274    #[inline]
275    fn from_unaligned(unaligned: Self::ULE) -> Self {
276        unaligned
277    }
278}
279
280// EqULE is true because i8 is its own ULE.
281unsafe impl EqULE for i8 {}
282
283impl AsULE for NonZeroI8 {
284    type ULE = NonZeroU8;
285    #[inline]
286    fn to_unaligned(self) -> Self::ULE {
287        // TODO: use cast_signed at 1.87 MSRV
288        // Safety: .get() is non-zero
289        unsafe { NonZeroU8::new_unchecked(self.get() as u8) }
290    }
291
292    #[inline]
293    fn from_unaligned(unaligned: Self::ULE) -> Self {
294        // TODO: use cast_unsigned at 1.87 MSRV
295        // Safety: .get() is non-zero
296        unsafe { NonZeroI8::new_unchecked(unaligned.get() as i8) }
297    }
298}
299
300// The bool impl is not as efficient as it could be
301// We can, in the future, have https://github.com/unicode-org/icu4x/blob/main/utils/zerovec/design_doc.md#bitpacking
302// for better bitpacking
303
304// Safety (based on the safety checklist on the ULE trait):
305//  1. bool does not include any uninitialized or padding bytes (the remaining 7 bytes in bool are by definition zero)
306//  2. bool is aligned to 1 byte.
307//  3. The impl of validate_bytes() returns an error if any byte is not valid (bytes that are not 0 or 1).
308//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
309//  5. The other ULE methods use the default impl.
310//  6. bool byte equality is semantic equality
311unsafe impl ULE for bool {
312    #[inline]
313    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
314        for byte in bytes {
315            // https://doc.rust-lang.org/reference/types/boolean.html
316            // Rust booleans are always size 1, align 1 values with valid bit patterns 0x0 or 0x1
317            if *byte > 1 {
318                return Err(UleError::parse::<Self>());
319            }
320        }
321        Ok(())
322    }
323}
324
325impl AsULE for bool {
326    type ULE = Self;
327    #[inline]
328    fn to_unaligned(self) -> Self::ULE {
329        self
330    }
331    #[inline]
332    fn from_unaligned(unaligned: Self::ULE) -> Self {
333        unaligned
334    }
335}
336
337// EqULE is true because bool is its own ULE.
338unsafe impl EqULE for bool {}
339
340impl ZeroSlice<bool> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<bool>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength {
                    ty: "<const construct: bool>",
                    len,
                })
        }
    }
}impl_const_constructors!(bool);
341
342// Safety (based on the safety checklist on the ULE trait):
343//  1. () does not include any uninitialized or padding bytes (it has no bytes)
344//  2. () is a ZST that is safe to construct
345//  3. The impl of validate_bytes() returns an error if any byte is not valid (any byte).
346//  4. The impl of validate_bytes() returns an error if there are leftover bytes (always).
347//  5. The other ULE methods use the default impl.
348//  6. () byte equality is semantic equality
349unsafe impl ULE for () {
350    #[inline]
351    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
352        if bytes.is_empty() {
353            Ok(())
354        } else {
355            Err(UleError::length::<Self>(bytes.len()))
356        }
357    }
358}
359
360impl AsULE for () {
361    type ULE = Self;
362    #[inline]
363    fn to_unaligned(self) -> Self::ULE {
364        self
365    }
366    #[inline]
367    fn from_unaligned(unaligned: Self::ULE) -> Self {
368        unaligned
369    }
370}
371
372// EqULE is true because () is its own ULE.
373unsafe impl EqULE for () {}
374
375impl ZeroSlice<()> {
    /// This function can be used for constructing [`ZeroVec`](crate::ZeroVec)s in a `const` context, avoiding
    /// parsing checks.
    ///
    /// This cannot be generic over `T` because of current limitations in `const`, but if
    /// this method is needed in a non-`const` context, check out [`ZeroSlice::parse_bytes()`]
    /// instead.
    ///
    /// See [`ZeroSlice::cast()`] for an example.
    pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
        let len = bytes.len();
        const STRIDE: usize = size_of::<()>();

        #[allow(clippy :: modulo_one)]
        if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: ()>", len })
        }
    }
}impl_const_constructors!(());