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