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_receiver_is_total_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 { core::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_byte_slice_size {
63    ($unsigned:ty, $size:literal) => {
64        impl RawBytesULE<$size> {
65            #[doc = concat!("Gets this `RawBytesULE` as a `", stringify!($unsigned), "`. This is equivalent to calling [`AsULE::from_unaligned()`] on the appropriately sized type.")]
66            #[inline]
67            pub fn as_unsigned_int(&self) -> $unsigned {
68                <$unsigned as $crate::ule::AsULE>::from_unaligned(*self)
69            }
70
71            #[doc = concat!("Converts a `", stringify!($unsigned), "` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on the appropriately sized type.")]
72            #[inline]
73            pub const fn from_aligned(value: $unsigned) -> Self {
74                Self(value.to_le_bytes())
75            }
76
77            impl_ule_from_array!(
78                $unsigned,
79                RawBytesULE<$size>,
80                RawBytesULE([0; $size])
81            );
82        }
83    };
84}
85
86macro_rules! impl_const_constructors {
87    ($base:ty, $size:literal) => {
88        impl ZeroSlice<$base> {
89            /// This function can be used for constructing ZeroVecs in a const context, avoiding
90            /// parsing checks.
91            ///
92            /// This cannot be generic over T because of current limitations in `const`, but if
93            /// this method is needed in a non-const context, check out [`ZeroSlice::parse_bytes()`]
94            /// instead.
95            ///
96            /// See [`ZeroSlice::cast()`] for an example.
97            pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
98                let len = bytes.len();
99                #[allow(clippy::modulo_one)]
100                if len % $size == 0 {
101                    Ok(unsafe { Self::from_bytes_unchecked(bytes) })
102                } else {
103                    Err(UleError::InvalidLength {
104                        ty: concat!("<const construct: ", $size, ">"),
105                        len,
106                    })
107                }
108            }
109        }
110    };
111}
112
113macro_rules! impl_byte_slice_type {
114    ($single_fn:ident, $type:ty, $size:literal) => {
115        impl From<$type> for RawBytesULE<$size> {
116            #[inline]
117            fn from(value: $type) -> Self {
118                Self(value.to_le_bytes())
119            }
120        }
121        impl AsULE for $type {
122            type ULE = RawBytesULE<$size>;
123            #[inline]
124            fn to_unaligned(self) -> Self::ULE {
125                RawBytesULE(self.to_le_bytes())
126            }
127            #[inline]
128            fn from_unaligned(unaligned: Self::ULE) -> Self {
129                <$type>::from_le_bytes(unaligned.0)
130            }
131        }
132        // EqULE is true because $type and RawBytesULE<$size>
133        // have the same byte sequence on little-endian
134        unsafe impl EqULE for $type {}
135
136        impl RawBytesULE<$size> {
137            pub const fn $single_fn(v: $type) -> Self {
138                RawBytesULE(v.to_le_bytes())
139            }
140        }
141    };
142}
143
144macro_rules! impl_byte_slice_unsigned_type {
145    ($type:ty, $size:literal) => {
146        impl_byte_slice_type!(from_unsigned, $type, $size);
147    };
148}
149
150macro_rules! impl_byte_slice_signed_type {
151    ($type:ty, $size:literal) => {
152        impl_byte_slice_type!(from_signed, $type, $size);
153    };
154}
155
156impl RawBytesULE<2> {
    #[doc =
    "Gets this `RawBytesULE` as a `u16`. This is equivalent to calling [`AsULE::from_unaligned()`] on the appropriately sized type."]
    #[inline]
    pub fn as_unsigned_int(&self) -> u16 {
        <u16 as crate::ule::AsULE>::from_unaligned(*self)
    }
    #[doc =
    "Converts a `u16` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on the appropriately sized type."]
    #[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 < 2 >`."]
    pub const fn from_array<const N : usize>(arr: [u16; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; 2]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}impl_byte_slice_size!(u16, 2);
157impl RawBytesULE<4> {
    #[doc =
    "Gets this `RawBytesULE` as a `u32`. This is equivalent to calling [`AsULE::from_unaligned()`] on the appropriately sized type."]
    #[inline]
    pub fn as_unsigned_int(&self) -> u32 {
        <u32 as crate::ule::AsULE>::from_unaligned(*self)
    }
    #[doc =
    "Converts a `u32` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on the appropriately sized type."]
    #[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 < 4 >`."]
    pub const fn from_array<const N : usize>(arr: [u32; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; 4]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}impl_byte_slice_size!(u32, 4);
158impl RawBytesULE<8> {
    #[doc =
    "Gets this `RawBytesULE` as a `u64`. This is equivalent to calling [`AsULE::from_unaligned()`] on the appropriately sized type."]
    #[inline]
    pub fn as_unsigned_int(&self) -> u64 {
        <u64 as crate::ule::AsULE>::from_unaligned(*self)
    }
    #[doc =
    "Converts a `u64` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on the appropriately sized type."]
    #[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 < 8 >`."]
    pub const fn from_array<const N : usize>(arr: [u64; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; 8]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}impl_byte_slice_size!(u64, 8);
159impl RawBytesULE<16> {
    #[doc =
    "Gets this `RawBytesULE` as a `u128`. This is equivalent to calling [`AsULE::from_unaligned()`] on the appropriately sized type."]
    #[inline]
    pub fn as_unsigned_int(&self) -> u128 {
        <u128 as crate::ule::AsULE>::from_unaligned(*self)
    }
    #[doc =
    "Converts a `u128` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on the appropriately sized type."]
    #[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 < 16 >`."]
    pub const fn from_array<const N : usize>(arr: [u128; N]) -> [Self; N] {
        let mut result = [RawBytesULE([0; 16]); N];
        let mut i = 0;

        #[expect(clippy :: indexing_slicing)]
        while i < N { result[i] = Self::from_aligned(arr[i]); i += 1; }
        result
    }
}impl_byte_slice_size!(u128, 16);
160
161impl From<u16> for RawBytesULE<2> {
    #[inline]
    fn from(value: u16) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u16 {
    type ULE = RawBytesULE<2>;
    #[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<2> {
    pub const fn from_unsigned(v: u16) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}impl_byte_slice_unsigned_type!(u16, 2);
162impl From<u32> for RawBytesULE<4> {
    #[inline]
    fn from(value: u32) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u32 {
    type ULE = RawBytesULE<4>;
    #[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<4> {
    pub const fn from_unsigned(v: u32) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}impl_byte_slice_unsigned_type!(u32, 4);
163impl From<u64> for RawBytesULE<8> {
    #[inline]
    fn from(value: u64) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u64 {
    type ULE = RawBytesULE<8>;
    #[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<8> {
    pub const fn from_unsigned(v: u64) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}impl_byte_slice_unsigned_type!(u64, 8);
164impl From<u128> for RawBytesULE<16> {
    #[inline]
    fn from(value: u128) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for u128 {
    type ULE = RawBytesULE<16>;
    #[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<16> {
    pub const fn from_unsigned(v: u128) -> Self {
        RawBytesULE(v.to_le_bytes())
    }
}impl_byte_slice_unsigned_type!(u128, 16);
165
166impl From<i16> for RawBytesULE<2> {
    #[inline]
    fn from(value: i16) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i16 {
    type ULE = RawBytesULE<2>;
    #[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<2> {
    pub const fn from_signed(v: i16) -> Self { RawBytesULE(v.to_le_bytes()) }
}impl_byte_slice_signed_type!(i16, 2);
167impl From<i32> for RawBytesULE<4> {
    #[inline]
    fn from(value: i32) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i32 {
    type ULE = RawBytesULE<4>;
    #[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<4> {
    pub const fn from_signed(v: i32) -> Self { RawBytesULE(v.to_le_bytes()) }
}impl_byte_slice_signed_type!(i32, 4);
168impl From<i64> for RawBytesULE<8> {
    #[inline]
    fn from(value: i64) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i64 {
    type ULE = RawBytesULE<8>;
    #[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<8> {
    pub const fn from_signed(v: i64) -> Self { RawBytesULE(v.to_le_bytes()) }
}impl_byte_slice_signed_type!(i64, 8);
169impl From<i128> for RawBytesULE<16> {
    #[inline]
    fn from(value: i128) -> Self { Self(value.to_le_bytes()) }
}
impl AsULE for i128 {
    type ULE = RawBytesULE<16>;
    #[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<16> {
    pub const fn from_signed(v: i128) -> Self { RawBytesULE(v.to_le_bytes()) }
}impl_byte_slice_signed_type!(i128, 16);
170
171impl ZeroSlice<u8> {
    /// This function can be used for constructing ZeroVecs 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();

        #[allow(clippy :: modulo_one)]
        if len % 1 == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: 1>", len })
        }
    }
}impl_const_constructors!(u8, 1);
172impl ZeroSlice<u16> {
    /// This function can be used for constructing ZeroVecs 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();

        #[allow(clippy :: modulo_one)]
        if len % 2 == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: 2>", len })
        }
    }
}impl_const_constructors!(u16, 2);
173impl ZeroSlice<u32> {
    /// This function can be used for constructing ZeroVecs 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();

        #[allow(clippy :: modulo_one)]
        if len % 4 == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: 4>", len })
        }
    }
}impl_const_constructors!(u32, 4);
174impl ZeroSlice<u64> {
    /// This function can be used for constructing ZeroVecs 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();

        #[allow(clippy :: modulo_one)]
        if len % 8 == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: 8>", len })
        }
    }
}impl_const_constructors!(u64, 8);
175impl ZeroSlice<u128> {
    /// This function can be used for constructing ZeroVecs 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();

        #[allow(clippy :: modulo_one)]
        if len % 16 == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: 16>", len })
        }
    }
}impl_const_constructors!(u128, 16);
176
177// Note: The f32 and f64 const constructors currently have limited use because
178// `f32::to_le_bytes` is not yet const.
179
180impl ZeroSlice<bool> {
    /// This function can be used for constructing ZeroVecs 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();

        #[allow(clippy :: modulo_one)]
        if len % 1 == 0 {
            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
        } else {
            Err(UleError::InvalidLength { ty: "<const construct: 1>", len })
        }
    }
}impl_const_constructors!(bool, 1);
181
182// Safety (based on the safety checklist on the ULE trait):
183//  1. u8 does not include any uninitialized or padding bytes.
184//  2. u8 is aligned to 1 byte.
185//  3. The impl of validate_bytes() returns an error if any byte is not valid (never).
186//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
187//  5. The other ULE methods use the default impl.
188//  6. u8 byte equality is semantic equality
189unsafe impl ULE for u8 {
190    #[inline]
191    fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> {
192        Ok(())
193    }
194}
195
196impl AsULE for u8 {
197    type ULE = Self;
198    #[inline]
199    fn to_unaligned(self) -> Self::ULE {
200        self
201    }
202    #[inline]
203    fn from_unaligned(unaligned: Self::ULE) -> Self {
204        unaligned
205    }
206}
207
208// EqULE is true because u8 is its own ULE.
209unsafe impl EqULE for u8 {}
210
211// Safety (based on the safety checklist on the ULE trait):
212//  1. NonZeroU8 does not include any uninitialized or padding bytes.
213//  2. NonZeroU8 is aligned to 1 byte.
214//  3. The impl of validate_bytes() returns an error if any byte is not valid (0x00).
215//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
216//  5. The other ULE methods use the default impl.
217//  6. NonZeroU8 byte equality is semantic equality
218unsafe impl ULE for NonZeroU8 {
219    #[inline]
220    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
221        bytes.iter().try_for_each(|b| {
222            if *b == 0x00 {
223                Err(UleError::parse::<Self>())
224            } else {
225                Ok(())
226            }
227        })
228    }
229}
230
231impl AsULE for NonZeroU8 {
232    type ULE = Self;
233    #[inline]
234    fn to_unaligned(self) -> Self::ULE {
235        self
236    }
237    #[inline]
238    fn from_unaligned(unaligned: Self::ULE) -> Self {
239        unaligned
240    }
241}
242
243unsafe impl EqULE for NonZeroU8 {}
244
245impl NicheBytes<1> for NonZeroU8 {
246    const NICHE_BIT_PATTERN: [u8; 1] = [0x00];
247}
248
249// Safety (based on the safety checklist on the ULE trait):
250//  1. i8 does not include any uninitialized or padding bytes.
251//  2. i8 is aligned to 1 byte.
252//  3. The impl of validate_bytes() returns an error if any byte is not valid (never).
253//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
254//  5. The other ULE methods use the default impl.
255//  6. i8 byte equality is semantic equality
256unsafe impl ULE for i8 {
257    #[inline]
258    fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> {
259        Ok(())
260    }
261}
262
263impl AsULE for i8 {
264    type ULE = Self;
265    #[inline]
266    fn to_unaligned(self) -> Self::ULE {
267        self
268    }
269    #[inline]
270    fn from_unaligned(unaligned: Self::ULE) -> Self {
271        unaligned
272    }
273}
274
275// EqULE is true because i8 is its own ULE.
276unsafe impl EqULE for i8 {}
277
278impl AsULE for NonZeroI8 {
279    type ULE = NonZeroU8;
280    #[inline]
281    fn to_unaligned(self) -> Self::ULE {
282        // Safety: NonZeroU8 and NonZeroI8 have same size
283        unsafe { core::mem::transmute(self) }
284    }
285
286    #[inline]
287    fn from_unaligned(unaligned: Self::ULE) -> Self {
288        // Safety: NonZeroU8 and NonZeroI8 have same size
289        unsafe { core::mem::transmute(unaligned) }
290    }
291}
292
293// These impls are actually safe and portable due to Rust always using IEEE 754, see the documentation
294// on f32::from_bits: https://doc.rust-lang.org/stable/std/primitive.f32.html#method.from_bits
295//
296// The only potential problem is that some older platforms treat signaling NaNs differently. This is
297// still quite portable, signalingness is not typically super important.
298
299impl AsULE for f32 {
300    type ULE = RawBytesULE<4>;
301    #[inline]
302    fn to_unaligned(self) -> Self::ULE {
303        self.to_bits().to_unaligned()
304    }
305    #[inline]
306    fn from_unaligned(unaligned: Self::ULE) -> Self {
307        Self::from_bits(u32::from_unaligned(unaligned))
308    }
309}
310
311impl AsULE for f64 {
312    type ULE = RawBytesULE<8>;
313    #[inline]
314    fn to_unaligned(self) -> Self::ULE {
315        self.to_bits().to_unaligned()
316    }
317    #[inline]
318    fn from_unaligned(unaligned: Self::ULE) -> Self {
319        Self::from_bits(u64::from_unaligned(unaligned))
320    }
321}
322
323// The from_bits documentation mentions that they have identical byte representations to integers
324// and EqULE only cares about LE systems
325unsafe impl EqULE for f32 {}
326unsafe impl EqULE for f64 {}
327
328// The bool impl is not as efficient as it could be
329// We can, in the future, have https://github.com/unicode-org/icu4x/blob/main/utils/zerovec/design_doc.md#bitpacking
330// for better bitpacking
331
332// Safety (based on the safety checklist on the ULE trait):
333//  1. bool does not include any uninitialized or padding bytes (the remaining 7 bytes in bool are by definition zero)
334//  2. bool is aligned to 1 byte.
335//  3. The impl of validate_bytes() returns an error if any byte is not valid (bytes that are not 0 or 1).
336//  4. The impl of validate_bytes() returns an error if there are leftover bytes (never).
337//  5. The other ULE methods use the default impl.
338//  6. bool byte equality is semantic equality
339unsafe impl ULE for bool {
340    #[inline]
341    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
342        for byte in bytes {
343            // https://doc.rust-lang.org/reference/types/boolean.html
344            // Rust booleans are always size 1, align 1 values with valid bit patterns 0x0 or 0x1
345            if *byte > 1 {
346                return Err(UleError::parse::<Self>());
347            }
348        }
349        Ok(())
350    }
351}
352
353impl AsULE for bool {
354    type ULE = Self;
355    #[inline]
356    fn to_unaligned(self) -> Self::ULE {
357        self
358    }
359    #[inline]
360    fn from_unaligned(unaligned: Self::ULE) -> Self {
361        unaligned
362    }
363}
364
365// EqULE is true because bool is its own ULE.
366unsafe impl EqULE for bool {}
367
368// Safety (based on the safety checklist on the ULE trait):
369//  1. () does not include any uninitialized or padding bytes (it has no bytes)
370//  2. () is a ZST that is safe to construct
371//  3. The impl of validate_bytes() returns an error if any byte is not valid (any byte).
372//  4. The impl of validate_bytes() returns an error if there are leftover bytes (always).
373//  5. The other ULE methods use the default impl.
374//  6. () byte equality is semantic equality
375unsafe impl ULE for () {
376    #[inline]
377    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
378        if bytes.is_empty() {
379            Ok(())
380        } else {
381            Err(UleError::length::<Self>(bytes.len()))
382        }
383    }
384}
385
386impl AsULE for () {
387    type ULE = Self;
388    #[inline]
389    fn to_unaligned(self) -> Self::ULE {
390        self
391    }
392    #[inline]
393    fn from_unaligned(unaligned: Self::ULE) -> Self {
394        unaligned
395    }
396}
397
398// EqULE is true because () is its own ULE.
399unsafe impl EqULE for () {}