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 ).
45#![allow(clippy::upper_case_acronyms)]
6//! ULE implementation for Plain Old Data types, including all sized integers.
78use super::*;
9use crate::impl_ule_from_array;
10use crate::ZeroSlice;
11use core::num::{NonZeroI8, NonZeroU8};
1213/// 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]);
1819impl<const N: usize> RawBytesULE<N> {
20#[inline]
21pub fn as_bytes(&self) -> &[u8] {
22&self.0
23}
2425#[inline]
26pub fn from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] {
27let data = bytes.as_mut_ptr();
28let len = bytes.len() / N;
29// Safe because Self is transparent over [u8; N]
30unsafe { slice::from_raw_parts_mut(dataas *mut Self, len) }
31 }
32}
3334// 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> ULEfor RawBytesULE<N> {
44#[inline]
45fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
46if bytes.len() % N == 0 {
47// Safe because Self is transparent over [u8; N]
48Ok(())
49 } else {
50Err(UleError::length::<Self>(bytes.len()))
51 }
52 }
53}
5455impl<const N: usize> From<[u8; N]> for RawBytesULE<N> {
56#[inline]
57fn from(le_bytes: [u8; N]) -> Self {
58Self(le_bytes)
59 }
60}
6162macro_rules! impl_numbers_with_raw_bytes_ule {
63 ($unsigned:ty, $signed:ty $(, $float:ty)?) => {
64const _: () = assert!(size_of::<$unsigned>() == size_of::<$signed>() $(&& size_of::<$unsigned>() == size_of::<$float>())?);
6566impl 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]
69pub const fn as_unsigned_int(&self) -> $unsigned {
70 <$unsigned>::from_le_bytes(self.0)
71 }
7273#[doc = concat!("Gets this `RawBytesULE` as a `", stringify!($unsigned), "`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`", stringify!($signed), "`].")]
74 #[inline]
75pub const fn as_signed_int(&self) -> $signed {
76 <$signed>::from_le_bytes(self.0)
77 }
7879 $(
80#[doc = concat!("Gets this `RawBytesULE` as a `", stringify!($float), "`. This is equivalent to calling [`AsULE::from_unaligned()`] on [`", stringify!($float), "`].")]
81 #[inline]
82pub const fn as_float(&self) -> $float {
83 <$float>::from_le_bytes(self.0)
84 }
85 )?
8687#[doc = concat!("Converts a `", stringify!($unsigned), "` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on [`", stringify!($unsigned), "`].")]
88 #[inline]
89pub const fn from_aligned(value: $unsigned) -> Self {
90Self(value.to_le_bytes())
91 }
9293impl_ule_from_array!(
94$unsigned,
95 RawBytesULE<{ size_of::<$unsigned>() }>,
96 RawBytesULE([0; { size_of::<$unsigned>() }])
97 );
98 }
99100impl_byte_slice_type!(from_unsigned, $unsigned);
101impl_const_constructors!($unsigned);
102103impl_byte_slice_type!(from_signed, $signed);
104impl_const_constructors!($signed);
105106 $(
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.
112113 // The from_bits documentation mentions that they have identical byte representations to integers
114 // and EqULE only cares about LE systems
115impl_byte_slice_type!(from_float, $float);
116impl_const_constructors!($float);
117 )?
118};
119}
120121macro_rules! impl_const_constructors {
122 ($base:ty) => {
123impl 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.
132pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
133let len = bytes.len();
134const STRIDE: usize = size_of::<$base>();
135#[allow(clippy::modulo_one)]
136if (if STRIDE <= 1 { len } else { len % STRIDE }) == 0 {
137Ok(unsafe { Self::from_bytes_unchecked(bytes) })
138 } else {
139Err(UleError::InvalidLength {
140 ty: concat!("<const construct: ", stringify!($base), ">"),
141 len,
142 })
143 }
144 }
145 }
146 };
147}
148149macro_rules! impl_byte_slice_type {
150 ($single_fn:ident, $type:ty) => {
151impl From<$type> for RawBytesULE<{ size_of::<$type>() }> {
152#[inline]
153fn from(value: $type) -> Self {
154Self(value.to_le_bytes())
155 }
156 }
157impl AsULE for $type {
158type ULE = RawBytesULE<{ size_of::<$type>() }>;
159#[inline]
160fn to_unaligned(self) -> Self::ULE {
161 RawBytesULE(self.to_le_bytes())
162 }
163#[inline]
164fn 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
170unsafe impl EqULE for $type {}
171172impl RawBytesULE<{ size_of::<$type>() }> {
173pub const fn $single_fn(v: $type) -> Self {
174 RawBytesULE(v.to_le_bytes())
175 }
176 }
177 };
178}
179180const _: () =
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);
184185// 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 ULEfor u8 {
193#[inline]
194fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> {
195Ok(())
196 }
197}
198199impl AsULEfor u8 {
200type ULE = Self;
201#[inline]
202fn to_unaligned(self) -> Self::ULE {
203self204 }
205#[inline]
206fn from_unaligned(unaligned: Self::ULE) -> Self {
207unaligned208 }
209}
210211// EqULE is true because u8 is its own ULE.
212unsafe impl EqULEfor u8 {}
213214impl 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);
215216// 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 ULEfor NonZeroU8 {
224#[inline]
225fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
226bytes.iter().try_for_each(|b| {
227if *b == 0x00 {
228Err(UleError::parse::<Self>())
229 } else {
230Ok(())
231 }
232 })
233 }
234}
235236impl AsULEfor NonZeroU8 {
237type ULE = Self;
238#[inline]
239fn to_unaligned(self) -> Self::ULE {
240self241 }
242#[inline]
243fn from_unaligned(unaligned: Self::ULE) -> Self {
244unaligned245 }
246}
247248unsafe impl EqULEfor NonZeroU8 {}
249250impl NicheBytes<1> for NonZeroU8 {
251const NICHE_BIT_PATTERN: [u8; 1] = [0x00];
252}
253254// 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 ULEfor i8 {
262#[inline]
263fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> {
264Ok(())
265 }
266}
267268impl AsULEfor i8 {
269type ULE = Self;
270#[inline]
271fn to_unaligned(self) -> Self::ULE {
272self273 }
274#[inline]
275fn from_unaligned(unaligned: Self::ULE) -> Self {
276unaligned277 }
278}
279280// EqULE is true because i8 is its own ULE.
281unsafe impl EqULEfor i8 {}
282283impl AsULEfor NonZeroI8 {
284type ULE = NonZeroU8;
285#[inline]
286fn to_unaligned(self) -> Self::ULE {
287// TODO: use cast_signed at 1.87 MSRV
288 // Safety: .get() is non-zero
289unsafe { NonZeroU8::new_unchecked(self.get() as u8) }
290 }
291292#[inline]
293fn from_unaligned(unaligned: Self::ULE) -> Self {
294// TODO: use cast_unsigned at 1.87 MSRV
295 // Safety: .get() is non-zero
296unsafe { NonZeroI8::new_unchecked(unaligned.get() as i8) }
297 }
298}
299300// 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
303304// 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 ULEfor bool {
312#[inline]
313fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
314for 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
317if *byte > 1 {
318return Err(UleError::parse::<Self>());
319 }
320 }
321Ok(())
322 }
323}
324325impl AsULEfor bool {
326type ULE = Self;
327#[inline]
328fn to_unaligned(self) -> Self::ULE {
329self330 }
331#[inline]
332fn from_unaligned(unaligned: Self::ULE) -> Self {
333unaligned334 }
335}
336337// EqULE is true because bool is its own ULE.
338unsafe impl EqULEfor bool {}
339340impl 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);
341342// 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 ULEfor () {
350#[inline]
351fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
352if bytes.is_empty() {
353Ok(())
354 } else {
355Err(UleError::length::<Self>(bytes.len()))
356 }
357 }
358}
359360impl AsULEfor () {
361type ULE = Self;
362#[inline]
363fn to_unaligned(self) -> Self::ULE {
364self365 }
366#[inline]
367fn from_unaligned(unaligned: Self::ULE) -> Self {
368unaligned369 }
370}
371372// EqULE is true because () is its own ULE.
373unsafe impl EqULEfor () {}
374375impl 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!(());