298 lines · 10.7 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 //! Helpers for performing lossless integer casts.//!//! The `as` keyword can be used to perform casts between integer types, but it unfortunately makes//! no distinction between casts that are lossless, and casts from a larger type into a smaller one//! that might silently strip data away. Thus, its use in the kernel is discouraged in favor of//! [`From`] implementations.//!//! Conversely, there are casts that are lossless depending on the build architecture (such as//! casting [`usize`] to [`u64`] on 32 or 64 bit archs), but not supported by [`From`]//! implementations in the standard library because they are not portable. It does however make//! sense for the kernel to support these, if only for code that is architecture-specific.//!//! This module provides ways to perform such conversions safely://!//! - A series of const functions (e.g. [`usize_as_u64`]) supporting safe conversions in const//!   context. Conversions supported by [`From`] implementations in the standard library are also//!   covered as the [`From`] trait cannot be used in const context.//! - Two extension traits, [`FromSafeCast`] and [`IntoSafeCast`], providing conversion methods//!   similar to [`From`] and [`Into`] for conversions that are safe to perform in the kernel, but//!   not supported by the standard library.//! - Another series of const functions (e.g. [`u64_into_u8`]) supporting the conversion of a const//!   value from a larger type into a smaller one, provided the value fits into the destination//!   type. This is useful if a constant is defined as a larger type, but needs to be used as a//!   smaller one.//! - An [`arch`] sub-module, defining more conversion functions that are only guaranteed to be//!   lossless for a given pointer size. These can only be used in code that is specific to a//!   given pointer size.//!//! # Examples//!//! ```//! use kernel::num::casts::{self, FromSafeCast, IntoSafeCast};//!//! // Conversion from const context.//! const USIZED_CONST: usize = casts::u8_as_usize(255u8);//!//! // Non-const conversions.//! let a = u64::from_safe_cast(4096usize);//! let b: u64 = 4096usize.into_safe_cast();//! ``` use crate::prelude::*; /// Implements safe `as` conversion functions from a given type into a series of target types.////// These functions can be used in place of `as`, with the guarantee that they will be lossless.macro_rules! impl_safe_as {    ($from:ty as { $($into:ty),* }) => {        $(        $crate::macros::paste! {            #[doc = ::core::concat!(                "Losslessly converts a [`",                ::core::stringify!($from),                "`] into a [`",                ::core::stringify!($into),                "`].")]            ///            /// This conversion is allowed as it is always lossless. Prefer this over the `as`            /// keyword to ensure no lossy casts are performed.            ///            /// This is for use from a `const` context. For non `const` use, prefer the            /// [`FromSafeCast`] and [`IntoSafeCast`] traits.            ///            /// # Examples            ///            /// ```            /// use kernel::num::casts;            ///            #[doc = ::core::concat!(                "assert_eq!(casts::",                ::core::stringify!($from),                "_as_",                ::core::stringify!($into),                "(1",                ::core::stringify!($from),                "), 1",                ::core::stringify!($into),                ");")]            /// ```            #[inline]            pub const fn [<$from _as_ $into>](value: $from) -> $into {                $crate::static_assert!(size_of::<$into>() >= size_of::<$from>());                 value as $into            }        }        )*    };} // Valid `Into` transformations.impl_safe_as!(u8 as { u16, u32, u64, usize });impl_safe_as!(u16 as { u32, u64, usize });impl_safe_as!(u32 as { u64 });// A `usize` fits into a `u64` on all supported platforms.impl_safe_as!(usize as { u64 });// A `u32` fits into a `usize` on all supported platforms.impl_safe_as!(u32 as { usize }); /// Extension trait providing guaranteed lossless cast to [`Self`] from `T`.////// The standard library's [`From`] implementations do not cover conversions that are not portable/// or future-proof. For instance, even though it is safe today, [`From<usize>`] is not implemented/// for [`u64`] because of the possibility of needing to support larger-than-64bit architectures in/// the future.////// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the/// destination type is smaller than the source.////// Both options are hardly acceptable for the kernel. It is also a much more architecture/// dependent environment, supporting only 32 and 64 bit architectures, with some modules/// explicitly depending on a specific bus width that could greatly benefit from infallible/// conversion operations.////// Thus this extension trait that provides, for all architectures supported by the kernel,/// conversion methods between types for which such a cast is lossless.////// In other words, this trait is implemented if, for all supported targets and with `t: T`, the/// `t as Self` operation is completely lossless.////// Prefer this over the `as` keyword to guarantee that no lossy casts are performed.////// If you need to perform a conversion in `const` context, use [`u32_as_usize`], [`usize_as_u64`],/// etc.////// # Examples////// ```/// use kernel::num::casts::FromSafeCast;////// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00usize);/// ```pub trait FromSafeCast<T> {    /// Create a [`Self`] from `value`. This operation is guaranteed to be lossless.    fn from_safe_cast(value: T) -> Self;} // A `usize` fits into a `u64` on all supported platforms.impl FromSafeCast<usize> for u64 {    #[inline]    fn from_safe_cast(value: usize) -> Self {        usize_as_u64(value)    }} // A `u32` fits into a `usize` on all supported platforms.impl FromSafeCast<u32> for usize {    #[inline]    fn from_safe_cast(value: u32) -> Self {        u32_as_usize(value)    }} /// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`]/// is to [`From`].////// See the documentation of [`FromSafeCast`] for the motivation.////// # Examples////// ```/// use kernel::num::casts::IntoSafeCast;////// assert_eq!(0xf00usize, 0xf00u32.into_safe_cast());/// ```pub trait IntoSafeCast<T> {    /// Convert `self` into a `T`. This operation is guaranteed to be lossless.    fn into_safe_cast(self) -> T;} /// Reverse operation for types implementing [`FromSafeCast`].impl<S, T> IntoSafeCast<T> for Swhere    T: FromSafeCast<S>,{    #[inline]    fn into_safe_cast(self) -> T {        T::from_safe_cast(self)    }} /// Implements lossless conversion of a constant from a larger type into a smaller one.macro_rules! impl_const_into {    ($from:ty => { $($into:ty),* }) => {        $(        $crate::macros::paste! {            #[doc = ::core::concat!(                "Performs a build-time safe conversion of a [`",                ::core::stringify!($from),                "`] constant value into a [`",                ::core::stringify!($into),                "`].")]            ///            /// This checks at compile-time that the conversion is lossless, and triggers a build            /// error if it isn't.            ///            /// # Examples            ///            /// ```            /// use kernel::num::casts;            ///            /// // Succeeds because the value of the source fits into the destination's type.            #[doc = ::core::concat!(                "assert_eq!(casts::",                ::core::stringify!($from),                "_into_",                ::core::stringify!($into),                "::<1",                ::core::stringify!($from),                ">(), 1",                ::core::stringify!($into),                ");")]            /// ```            #[inline]            pub const fn [<$from _into_ $into>]<const N: $from>() -> $into {                // Make sure that the target type is smaller than the source one.                $crate::static_assert!($from::BITS >= $into::BITS);                // CAST: we statically enforced above that `$from` is larger than `$into`, so the                // `as` conversion will be lossless.                $crate::const_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 }); /// Conversions that are only lossless for the current architecture.////// # Portability////// Callers of this module become dependent on the setting of `CONFIG_64BIT`. Use with caution, and/// never in code that is portable across pointer sizes.pub mod arch {    /// Trait identical to [`FromSafeCast`](super::FromSafeCast), but for conversions that are not    /// available on all architectures.    pub trait FromSafeCastArch<T> {        /// Create a [`Self`] from `value`. This operation is guaranteed to be lossless.        fn from_safe_cast_arch(value: T) -> Self;    }     /// Trait identical to [`IntoSafeCast`](super::IntoSafeCast), but for conversions that are not    /// available on all architectures.    pub trait IntoSafeCastArch<T> {        /// Convert `self` into a `T`. This operation is guaranteed to be lossless.        fn into_safe_cast_arch(self) -> T;    }     /// Reverse operation for types implementing [`FromSafeCastArch`].    impl<S, T> IntoSafeCastArch<T> for S    where        T: FromSafeCastArch<S>,    {        #[inline]        fn into_safe_cast_arch(self) -> T {            T::from_safe_cast_arch(self)        }    }     /// A [`u64`] fits into a [`usize`] on 64-bit platforms.    #[cfg(CONFIG_64BIT)]    #[inline]    pub const fn u64_as_usize(value: u64) -> usize {        value as usize    }     #[cfg(CONFIG_64BIT)]    impl FromSafeCastArch<u64> for usize {        #[inline]        fn from_safe_cast_arch(value: u64) -> Self {            u64_as_usize(value)        }    }     /// A [`usize`] fits into a [`u32`] on 32-bit platforms.    #[cfg(not(CONFIG_64BIT))]    #[inline]    pub const fn usize_as_u32(value: usize) -> u32 {        value as u32    }     #[cfg(not(CONFIG_64BIT))]    impl FromSafeCastArch<usize> for u32 {        #[inline]        fn from_safe_cast_arch(value: usize) -> Self {            usize_as_u32(value)        }    }}