use kernel::{
macros::paste,
prelude::*, };
macro_rules! impl_safe_as {
($from:ty as { $($into:ty),* }) => {
$(
paste! {
#[doc = ::core::concat!(
"Losslessly converts a [`",
::core::stringify!($from),
"`] into a [`",
::core::stringify!($into),
"`].")]
#[doc = ::core::concat!(
"assert_eq!(num::",
::core::stringify!($from),
"_as_",
::core::stringify!($into),
"(1",
::core::stringify!($from),
"), 1",
::core::stringify!($into),
");")]
#[allow(unused)]
#[inline(always)]
pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into {
kernel::static_assert!(size_of::<$into>() >= size_of::<$from>());
value as $into
}
}
)*
};
}
impl_safe_as!(u8 as { u16, u32, u64, usize });
impl_safe_as!(u16 as { u32, u64, usize });
impl_safe_as!(u32 as { u64, usize } );
#[cfg(CONFIG_64BIT)]
impl_safe_as!(u64 as { usize } );
#[cfg(any(CONFIG_32BIT, CONFIG_64BIT))]
impl_safe_as!(usize as { u64 });
#[cfg(CONFIG_32BIT)]
impl_safe_as!(usize as { u32 });
pub(crate) trait FromSafeCast<T> {
fn from_safe_cast(value: T) -> Self;
}
impl FromSafeCast<usize> for u64 {
fn from_safe_cast(value: usize) -> Self {
usize_as_u64(value)
}
}
#[cfg(CONFIG_32BIT)]
impl FromSafeCast<usize> for u32 {
fn from_safe_cast(value: usize) -> Self {
usize_as_u32(value)
}
}
impl FromSafeCast<u32> for usize {
fn from_safe_cast(value: u32) -> Self {
u32_as_usize(value)
}
}
#[cfg(CONFIG_64BIT)]
impl FromSafeCast<u64> for usize {
fn from_safe_cast(value: u64) -> Self {
u64_as_usize(value)
}
}
pub(crate) trait IntoSafeCast<T> {
fn into_safe_cast(self) -> T;
}
impl<S, T> IntoSafeCast<T> for S
where
T: FromSafeCast<S>,
{
fn into_safe_cast(self) -> T {
T::from_safe_cast(self)
}
}
macro_rules! impl_const_into {
($from:ty => { $($into:ty),* }) => {
$(
paste! {
#[doc = ::core::concat!(
"Performs a build-time safe conversion of a [`",
::core::stringify!($from),
"`] constant value into a [`",
::core::stringify!($into),
"`].")]
#[doc = ::core::concat!(
"assert_eq!(num::",
::core::stringify!($from),
"_into_",
::core::stringify!($into),
"::<1",
::core::stringify!($from),
">(), 1",
::core::stringify!($into),
");")]
#[allow(unused)]
pub(crate) const fn [<$from _into_ $into>]<const N: $from>() -> $into {
static_assert!($from::BITS >= $into::BITS);
build_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);
N as $into
}
}
)*
};
}
impl_const_into!(usize => { u8, u16, u32 });
impl_const_into!(u64 => { u8, u16, u32 });
impl_const_into!(u32 => { u8, u16 });
impl_const_into!(u16 => { u8 });
#[macro_export]
macro_rules! bounded_enum {
(
$(#[$enum_meta:meta])*
$vis:vis enum $enum_type:ident with $from_impl:ident<Bounded<$width:ty, $length:literal>> {
$( $(#[doc = $variant_doc:expr])* $variant:ident = $value:expr),* $(,)*
}
) => {
$(#[$enum_meta])*
$vis enum $enum_type {
$(
$(#[doc = $variant_doc])*
$variant = $value
),*
}
impl core::convert::From<$enum_type> for kernel::num::Bounded<$width, $length> {
fn from(value: $enum_type) -> Self {
match value {
$($enum_type::$variant =>
kernel::num::Bounded::<$width, _>::new::<{ $value }>()),*
}
}
}
bounded_enum!(@impl_from $enum_type with $from_impl<Bounded<$width, $length>> {
$($variant = $value),*
});
};
(@impl_from $enum_type:ident with TryFrom<Bounded<$width:ty, $length:literal>> {
$($variant:ident = $value:expr),* $(,)*
}) => {
impl core::convert::TryFrom<kernel::num::Bounded<$width, $length>> for $enum_type {
type Error = kernel::error::Error;
fn try_from(
value: kernel::num::Bounded<$width, $length>
) -> kernel::error::Result<Self> {
match value.get() {
$(
$value => Ok($enum_type::$variant),
)*
_ => Err(kernel::error::code::EINVAL),
}
}
}
};
(@impl_from $enum_type:ident with From<Bounded<$width:ty, $length:literal>> {
$($variant:ident = $value:expr),* $(,)*
}) => {
impl core::convert::From<kernel::num::Bounded<$width, $length>> for $enum_type {
fn from(value: kernel::num::Bounded<$width, $length>) -> Self {
const MAX: $width = 1 << $length;
let value = value.get() & ((1 << $length) - 1);
match value {
$(
$value => $enum_type::$variant,
)*
MAX.. => unreachable!(),
}
}
}
}
}