|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Helpers for performing lossless integer casts. |
| 4 | +//! |
| 5 | +//! The `as` keyword can be used to perform casts between integer types, but it unfortunately makes |
| 6 | +//! no distinction between casts that are lossless, and casts from a larger type into a smaller one |
| 7 | +//! that might silently strip data away. Thus, its use in the kernel is discouraged in favor of |
| 8 | +//! [`From`] implementations. |
| 9 | +//! |
| 10 | +//! Conversely, there are casts that are lossless depending on the build architecture (such as |
| 11 | +//! casting [`usize`] to [`u64`] on 32 or 64 bit archs), but not supported by [`From`] |
| 12 | +//! implementations in the standard library because they are not portable. It does however make |
| 13 | +//! sense for the kernel to support these, if only for code that is architecture-specific. |
| 14 | +//! |
| 15 | +//! This module provides ways to perform such conversions safely: |
| 16 | +//! |
| 17 | +//! - A series of const functions (e.g. [`usize_as_u64`]) supporting safe conversions in const |
| 18 | +//! context. Conversions supported by [`From`] implementations in the standard library are also |
| 19 | +//! covered as the [`From`] trait cannot be used in const context. |
| 20 | +//! - Two extension traits, [`FromSafeCast`] and [`IntoSafeCast`], providing conversion methods |
| 21 | +//! similar to [`From`] and [`Into`] for conversions that are safe to perform in the kernel, but |
| 22 | +//! not supported by the standard library. |
| 23 | +//! - Another series of const functions (e.g. [`u64_into_u8`]) supporting the conversion of a const |
| 24 | +//! value from a larger type into a smaller one, provided the value fits into the destination |
| 25 | +//! type. This is useful if a constant is defined as a larger type, but needs to be used as a |
| 26 | +//! smaller one. |
| 27 | +//! - An [`arch`] sub-module, defining more conversion functions that are only guaranteed to be |
| 28 | +//! lossless for a given pointer size. These can only be used in code that is specific to a |
| 29 | +//! given pointer size. |
| 30 | +//! |
| 31 | +//! # Examples |
| 32 | +//! |
| 33 | +//! ``` |
| 34 | +//! use kernel::num::casts::{self, FromSafeCast, IntoSafeCast}; |
| 35 | +//! |
| 36 | +//! // Conversion from const context. |
| 37 | +//! const USIZED_CONST: usize = casts::u8_as_usize(255u8); |
| 38 | +//! |
| 39 | +//! // Non-const conversions. |
| 40 | +//! let a = u64::from_safe_cast(4096usize); |
| 41 | +//! let b: u64 = 4096usize.into_safe_cast(); |
| 42 | +//! ``` |
| 43 | +
|
| 44 | +use crate::prelude::*; |
| 45 | + |
| 46 | +/// Implements safe `as` conversion functions from a given type into a series of target types. |
| 47 | +/// |
| 48 | +/// These functions can be used in place of `as`, with the guarantee that they will be lossless. |
| 49 | +macro_rules! impl_safe_as { |
| 50 | + ($from:ty as { $($into:ty),* }) => { |
| 51 | + $( |
| 52 | + $crate::macros::paste! { |
| 53 | + #[doc = ::core::concat!( |
| 54 | + "Losslessly converts a [`", |
| 55 | + ::core::stringify!($from), |
| 56 | + "`] into a [`", |
| 57 | + ::core::stringify!($into), |
| 58 | + "`].")] |
| 59 | + /// |
| 60 | + /// This conversion is allowed as it is always lossless. Prefer this over the `as` |
| 61 | + /// keyword to ensure no lossy casts are performed. |
| 62 | + /// |
| 63 | + /// This is for use from a `const` context. For non `const` use, prefer the |
| 64 | + /// [`FromSafeCast`] and [`IntoSafeCast`] traits. |
| 65 | + /// |
| 66 | + /// # Examples |
| 67 | + /// |
| 68 | + /// ``` |
| 69 | + /// use kernel::num::casts; |
| 70 | + /// |
| 71 | + #[doc = ::core::concat!( |
| 72 | + "assert_eq!(casts::", |
| 73 | + ::core::stringify!($from), |
| 74 | + "_as_", |
| 75 | + ::core::stringify!($into), |
| 76 | + "(1", |
| 77 | + ::core::stringify!($from), |
| 78 | + "), 1", |
| 79 | + ::core::stringify!($into), |
| 80 | + ");")] |
| 81 | + /// ``` |
| 82 | + #[inline] |
| 83 | + pub const fn [<$from _as_ $into>](value: $from) -> $into { |
| 84 | + $crate::static_assert!(size_of::<$into>() >= size_of::<$from>()); |
| 85 | + |
| 86 | + value as $into |
| 87 | + } |
| 88 | + } |
| 89 | + )* |
| 90 | + }; |
| 91 | +} |
| 92 | + |
| 93 | +// Valid `Into` transformations. |
| 94 | +impl_safe_as!(u8 as { u16, u32, u64, usize }); |
| 95 | +impl_safe_as!(u16 as { u32, u64, usize }); |
| 96 | +impl_safe_as!(u32 as { u64 }); |
| 97 | +// A `usize` fits into a `u64` on all supported platforms. |
| 98 | +impl_safe_as!(usize as { u64 }); |
| 99 | +// A `u32` fits into a `usize` on all supported platforms. |
| 100 | +impl_safe_as!(u32 as { usize }); |
| 101 | + |
| 102 | +/// Extension trait providing guaranteed lossless cast to [`Self`] from `T`. |
| 103 | +/// |
| 104 | +/// The standard library's [`From`] implementations do not cover conversions that are not portable |
| 105 | +/// or future-proof. For instance, even though it is safe today, [`From<usize>`] is not implemented |
| 106 | +/// for [`u64`] because of the possibility of needing to support larger-than-64bit architectures in |
| 107 | +/// the future. |
| 108 | +/// |
| 109 | +/// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that |
| 110 | +/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the |
| 111 | +/// destination type is smaller than the source. |
| 112 | +/// |
| 113 | +/// Both options are hardly acceptable for the kernel. It is also a much more architecture |
| 114 | +/// dependent environment, supporting only 32 and 64 bit architectures, with some modules |
| 115 | +/// explicitly depending on a specific bus width that could greatly benefit from infallible |
| 116 | +/// conversion operations. |
| 117 | +/// |
| 118 | +/// Thus this extension trait that provides, for all architectures supported by the kernel, |
| 119 | +/// conversion methods between types for which such a cast is lossless. |
| 120 | +/// |
| 121 | +/// In other words, this trait is implemented if, for all supported targets and with `t: T`, the |
| 122 | +/// `t as Self` operation is completely lossless. |
| 123 | +/// |
| 124 | +/// Prefer this over the `as` keyword to guarantee that no lossy casts are performed. |
| 125 | +/// |
| 126 | +/// If you need to perform a conversion in `const` context, use [`u32_as_usize`], [`usize_as_u64`], |
| 127 | +/// etc. |
| 128 | +/// |
| 129 | +/// # Examples |
| 130 | +/// |
| 131 | +/// ``` |
| 132 | +/// use kernel::num::casts::FromSafeCast; |
| 133 | +/// |
| 134 | +/// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00usize); |
| 135 | +/// ``` |
| 136 | +pub trait FromSafeCast<T> { |
| 137 | + /// Create a [`Self`] from `value`. This operation is guaranteed to be lossless. |
| 138 | + fn from_safe_cast(value: T) -> Self; |
| 139 | +} |
| 140 | + |
| 141 | +// A `usize` fits into a `u64` on all supported platforms. |
| 142 | +impl FromSafeCast<usize> for u64 { |
| 143 | + #[inline] |
| 144 | + fn from_safe_cast(value: usize) -> Self { |
| 145 | + usize_as_u64(value) |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +// A `u32` fits into a `usize` on all supported platforms. |
| 150 | +impl FromSafeCast<u32> for usize { |
| 151 | + #[inline] |
| 152 | + fn from_safe_cast(value: u32) -> Self { |
| 153 | + u32_as_usize(value) |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +/// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`] |
| 158 | +/// is to [`From`]. |
| 159 | +/// |
| 160 | +/// See the documentation of [`FromSafeCast`] for the motivation. |
| 161 | +/// |
| 162 | +/// # Examples |
| 163 | +/// |
| 164 | +/// ``` |
| 165 | +/// use kernel::num::casts::IntoSafeCast; |
| 166 | +/// |
| 167 | +/// assert_eq!(0xf00usize, 0xf00u32.into_safe_cast()); |
| 168 | +/// ``` |
| 169 | +pub trait IntoSafeCast<T> { |
| 170 | + /// Convert `self` into a `T`. This operation is guaranteed to be lossless. |
| 171 | + fn into_safe_cast(self) -> T; |
| 172 | +} |
| 173 | + |
| 174 | +/// Reverse operation for types implementing [`FromSafeCast`]. |
| 175 | +impl<S, T> IntoSafeCast<T> for S |
| 176 | +where |
| 177 | + T: FromSafeCast<S>, |
| 178 | +{ |
| 179 | + #[inline] |
| 180 | + fn into_safe_cast(self) -> T { |
| 181 | + T::from_safe_cast(self) |
| 182 | + } |
| 183 | +} |
| 184 | + |
| 185 | +/// Implements lossless conversion of a constant from a larger type into a smaller one. |
| 186 | +macro_rules! impl_const_into { |
| 187 | + ($from:ty => { $($into:ty),* }) => { |
| 188 | + $( |
| 189 | + $crate::macros::paste! { |
| 190 | + #[doc = ::core::concat!( |
| 191 | + "Performs a build-time safe conversion of a [`", |
| 192 | + ::core::stringify!($from), |
| 193 | + "`] constant value into a [`", |
| 194 | + ::core::stringify!($into), |
| 195 | + "`].")] |
| 196 | + /// |
| 197 | + /// This checks at compile-time that the conversion is lossless, and triggers a build |
| 198 | + /// error if it isn't. |
| 199 | + /// |
| 200 | + /// # Examples |
| 201 | + /// |
| 202 | + /// ``` |
| 203 | + /// use kernel::num::casts; |
| 204 | + /// |
| 205 | + /// // Succeeds because the value of the source fits into the destination's type. |
| 206 | + #[doc = ::core::concat!( |
| 207 | + "assert_eq!(casts::", |
| 208 | + ::core::stringify!($from), |
| 209 | + "_into_", |
| 210 | + ::core::stringify!($into), |
| 211 | + "::<1", |
| 212 | + ::core::stringify!($from), |
| 213 | + ">(), 1", |
| 214 | + ::core::stringify!($into), |
| 215 | + ");")] |
| 216 | + /// ``` |
| 217 | + #[inline] |
| 218 | + pub const fn [<$from _into_ $into>]<const N: $from>() -> $into { |
| 219 | + // Make sure that the target type is smaller than the source one. |
| 220 | + $crate::static_assert!($from::BITS >= $into::BITS); |
| 221 | + // CAST: we statically enforced above that `$from` is larger than `$into`, so the |
| 222 | + // `as` conversion will be lossless. |
| 223 | + $crate::const_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from); |
| 224 | + |
| 225 | + N as $into |
| 226 | + } |
| 227 | + } |
| 228 | + )* |
| 229 | + }; |
| 230 | +} |
| 231 | + |
| 232 | +impl_const_into!(usize => { u8, u16, u32 }); |
| 233 | +impl_const_into!(u64 => { u8, u16, u32 }); |
| 234 | +impl_const_into!(u32 => { u8, u16 }); |
| 235 | +impl_const_into!(u16 => { u8 }); |
| 236 | + |
| 237 | +/// Conversions that are only lossless for the current architecture. |
| 238 | +/// |
| 239 | +/// # Portability |
| 240 | +/// |
| 241 | +/// Callers of this module become dependent on the setting of `CONFIG_64BIT`. Use with caution, and |
| 242 | +/// never in code that is portable across pointer sizes. |
| 243 | +pub mod arch { |
| 244 | + /// Trait identical to [`FromSafeCast`](super::FromSafeCast), but for conversions that are not |
| 245 | + /// available on all architectures. |
| 246 | + pub trait FromSafeCastArch<T> { |
| 247 | + /// Create a [`Self`] from `value`. This operation is guaranteed to be lossless. |
| 248 | + fn from_safe_cast_arch(value: T) -> Self; |
| 249 | + } |
| 250 | + |
| 251 | + /// Trait identical to [`IntoSafeCast`](super::IntoSafeCast), but for conversions that are not |
| 252 | + /// available on all architectures. |
| 253 | + pub trait IntoSafeCastArch<T> { |
| 254 | + /// Convert `self` into a `T`. This operation is guaranteed to be lossless. |
| 255 | + fn into_safe_cast_arch(self) -> T; |
| 256 | + } |
| 257 | + |
| 258 | + /// Reverse operation for types implementing [`FromSafeCastArch`]. |
| 259 | + impl<S, T> IntoSafeCastArch<T> for S |
| 260 | + where |
| 261 | + T: FromSafeCastArch<S>, |
| 262 | + { |
| 263 | + #[inline] |
| 264 | + fn into_safe_cast_arch(self) -> T { |
| 265 | + T::from_safe_cast_arch(self) |
| 266 | + } |
| 267 | + } |
| 268 | + |
| 269 | + /// A [`u64`] fits into a [`usize`] on 64-bit platforms. |
| 270 | + #[cfg(CONFIG_64BIT)] |
| 271 | + #[inline] |
| 272 | + pub const fn u64_as_usize(value: u64) -> usize { |
| 273 | + value as usize |
| 274 | + } |
| 275 | + |
| 276 | + #[cfg(CONFIG_64BIT)] |
| 277 | + impl FromSafeCastArch<u64> for usize { |
| 278 | + #[inline] |
| 279 | + fn from_safe_cast_arch(value: u64) -> Self { |
| 280 | + u64_as_usize(value) |
| 281 | + } |
| 282 | + } |
| 283 | + |
| 284 | + /// A [`usize`] fits into a [`u32`] on 32-bit platforms. |
| 285 | + #[cfg(not(CONFIG_64BIT))] |
| 286 | + #[inline] |
| 287 | + pub const fn usize_as_u32(value: usize) -> u32 { |
| 288 | + value as u32 |
| 289 | + } |
| 290 | + |
| 291 | + #[cfg(not(CONFIG_64BIT))] |
| 292 | + impl FromSafeCastArch<usize> for u32 { |
| 293 | + #[inline] |
| 294 | + fn from_safe_cast_arch(value: usize) -> Self { |
| 295 | + usize_as_u32(value) |
| 296 | + } |
| 297 | + } |
| 298 | +} |
0 commit comments