// SPDX-License-Identifier: GPL-2.012//! Numerical helpers functions and traits.3//!4//! This is essentially a staging module for code to mature until it can be moved to the `kernel`5//! crate.67use kernel::{8macros::paste,9prelude::*, //10};1112/// Implements safe `as` conversion functions from a given type into a series of target types.13///14/// These functions can be used in place of `as`, with the guarantee that they will be lossless.15macro_rules! impl_safe_as {16($from:ty as { $($into:ty),* }) => {17$(18paste! {19#[doc = ::core::concat!(20"Losslessly converts a [`",21::core::stringify!($from),22"`] into a [`",23::core::stringify!($into),24"`].")]25///26/// This conversion is allowed as it is always lossless. Prefer this over the `as`27/// keyword to ensure no lossy casts are performed.28///29/// This is for use from a `const` context. For non `const` use, prefer the30/// [`FromSafeCast`] and [`IntoSafeCast`] traits.31///32/// # Examples33///34/// ```35/// use crate::num;36///37#[doc = ::core::concat!(38"assert_eq!(num::",39::core::stringify!($from),40"_as_",41::core::stringify!($into),42"(1",43::core::stringify!($from),44"), 1",45::core::stringify!($into),46");")]47/// ```48#[allow(unused)]49#[inline(always)]50pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into {51kernel::static_assert!(size_of::<$into>() >= size_of::<$from>());5253value as $into54}55}56)*57};58}5960impl_safe_as!(u8 as { u16, u32, u64, usize });61impl_safe_as!(u16 as { u32, u64, usize });62impl_safe_as!(u32 as { u64, usize } );63// `u64` and `usize` have the same size on 64-bit platforms.64#[cfg(CONFIG_64BIT)]65impl_safe_as!(u64 as { usize } );6667// A `usize` fits into a `u64` on 32 and 64-bit platforms.68#[cfg(any(CONFIG_32BIT, CONFIG_64BIT))]69impl_safe_as!(usize as { u64 });7071// A `usize` fits into a `u32` on 32-bit platforms.72#[cfg(CONFIG_32BIT)]73impl_safe_as!(usize as { u32 });7475/// Extension trait providing guaranteed lossless cast to `Self` from `T`.76///77/// The standard library's `From` implementations do not cover conversions that are not portable or78/// future-proof. For instance, even though it is safe today, `From<usize>` is not implemented for79/// [`u64`] because of the possibility to support larger-than-64bit architectures in the future.80///81/// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that82/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the83/// destination type is smaller than the source.84///85/// Both options are hardly acceptable for the kernel. It is also a much more architecture86/// dependent environment, supporting only 32 and 64 bit architectures, with some modules87/// explicitly depending on a specific bus width that could greatly benefit from infallible88/// conversion operations.89///90/// Thus this extension trait that provides, for the architecture the kernel is built for, safe91/// conversion between types for which such cast is lossless.92///93/// In other words, this trait is implemented if, for the current build target and with `t: T`, the94/// `t as Self` operation is completely lossless.95///96/// Prefer this over the `as` keyword to ensure no lossy casts are performed.97///98/// If you need to perform a conversion in `const` context, use [`u64_as_usize`], [`u32_as_usize`],99/// [`usize_as_u64`], etc.100///101/// # Examples102///103/// ```104/// use crate::num::FromSafeCast;105///106/// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00u32 as usize);107/// ```108pub(crate) trait FromSafeCast<T> {109/// Create a `Self` from `value`. This operation is guaranteed to be lossless.110fn from_safe_cast(value: T) -> Self;111}112113impl FromSafeCast<usize> for u64 {114fn from_safe_cast(value: usize) -> Self {115usize_as_u64(value)116}117}118119#[cfg(CONFIG_32BIT)]120impl FromSafeCast<usize> for u32 {121fn from_safe_cast(value: usize) -> Self {122usize_as_u32(value)123}124}125126impl FromSafeCast<u32> for usize {127fn from_safe_cast(value: u32) -> Self {128u32_as_usize(value)129}130}131132#[cfg(CONFIG_64BIT)]133impl FromSafeCast<u64> for usize {134fn from_safe_cast(value: u64) -> Self {135u64_as_usize(value)136}137}138139/// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`]140/// is to [`From`].141///142/// See the documentation of [`FromSafeCast`] for the motivation.143///144/// # Examples145///146/// ```147/// use crate::num::IntoSafeCast;148///149/// assert_eq!(0xf00u32.into_safe_cast(), 0xf00u32 as usize);150/// ```151pub(crate) trait IntoSafeCast<T> {152/// Convert `self` into a `T`. This operation is guaranteed to be lossless.153fn into_safe_cast(self) -> T;154}155156/// Reverse operation for types implementing [`FromSafeCast`].157impl<S, T> IntoSafeCast<T> for S158where159T: FromSafeCast<S>,160{161fn into_safe_cast(self) -> T {162T::from_safe_cast(self)163}164}165166/// Implements lossless conversion of a constant from a larger type into a smaller one.167macro_rules! impl_const_into {168($from:ty => { $($into:ty),* }) => {169$(170paste! {171#[doc = ::core::concat!(172"Performs a build-time safe conversion of a [`",173::core::stringify!($from),174"`] constant value into a [`",175::core::stringify!($into),176"`].")]177///178/// This checks at compile-time that the conversion is lossless, and triggers a build179/// error if it isn't.180///181/// # Examples182///183/// ```184/// use crate::num;185///186/// // Succeeds because the value of the source fits into the destination's type.187#[doc = ::core::concat!(188"assert_eq!(num::",189::core::stringify!($from),190"_into_",191::core::stringify!($into),192"::<1",193::core::stringify!($from),194">(), 1",195::core::stringify!($into),196");")]197/// ```198#[allow(unused)]199pub(crate) const fn [<$from _into_ $into>]<const N: $from>() -> $into {200// Make sure that the target type is smaller than the source one.201static_assert!($from::BITS >= $into::BITS);202// CAST: we statically enforced above that `$from` is larger than `$into`, so the203// `as` conversion will be lossless.204build_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);205206N as $into207}208}209)*210};211}212213impl_const_into!(usize => { u8, u16, u32 });214impl_const_into!(u64 => { u8, u16, u32 });215impl_const_into!(u32 => { u8, u16 });216impl_const_into!(u16 => { u8 });217218219