1165 lines · 33.6 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 //! Implementation of [`Bounded`], a wrapper around integer types limiting the number of bits//! usable for value representation. use core::{    cmp,    fmt,    ops::{        self,        Deref, //    }, //,}; use kernel::{    num::{        Integer,        Unsigned, //    },    prelude::*, //}; /// Evaluates to `true` if `$value` can be represented using at most `$n` bits in a `$type`.////// `expr` must be of type `type`, or the result will be incorrect.////// Can be used in const context.macro_rules! fits_within {    ($value:expr, $type:ty, $n:expr) => {{        let shift: u32 = <$type>::BITS - $n;         // `value` fits within `$n` bits if shifting it left by the number of unused bits, then        // right by the same number, doesn't change it.        //        // This method has the benefit of working for both unsigned and signed values.        ($value << shift) >> shift == $value    }};} /// Returns `true` if `value` can be represented with at most `N` bits in a `T`.#[inline(always)]fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {    fits_within!(value, T, num_bits)} /// An integer value that requires only the `N` least significant bits of the wrapped type to be/// encoded.////// This limits the number of usable bits in the wrapped integer type, and thus the stored value to/// a narrower range, which provides guarantees that can be useful when working within e.g./// bitfields.////// # Invariants////// - `N` is greater than `0`./// - `N` is less than or equal to `T::BITS`./// - Stored values can be represented with at most `N` bits.////// # Examples////// The preferred way to create values is through constants and the [`Bounded::new`] family of/// constructors, as they trigger a build error if the type invariants cannot be upheld.////// ```/// use kernel::num::Bounded;////// // An unsigned 8-bit integer, of which only the 4 LSBs are used./// // The value `15` is statically validated to fit that constraint at build time./// let v = Bounded::<u8, 4>::new::<15>();/// assert_eq!(v.get(), 15);////// // Same using signed values./// let v = Bounded::<i8, 4>::new::<-8>();/// assert_eq!(v.get(), -8);////// // This doesn't build: a `u8` is smaller than the requested 9 bits./// // let _ = Bounded::<u8, 9>::new::<10>();////// // This also doesn't build: the requested value doesn't fit within 4 signed bits./// // let _ = Bounded::<i8, 4>::new::<8>();/// ```////// Values can also be validated at runtime with [`Bounded::try_new`].////// ```/// use kernel::num::Bounded;////// // This succeeds because `15` can be represented with 4 unsigned bits./// assert!(Bounded::<u8, 4>::try_new(15).is_some());////// // This fails because `16` cannot be represented with 4 unsigned bits./// assert!(Bounded::<u8, 4>::try_new(16).is_none());/// ```////// Non-constant expressions can be validated at build-time thanks to compiler optimizations. This/// should be used with caution, on simple expressions only.////// ```/// use kernel::num::Bounded;/// # fn some_number() -> u32 { 0xffffffff }////// // Here the compiler can infer from the mask that the type invariants are not violated, even/// // though the value returned by `some_number` is not statically known./// let v = Bounded::<u32, 4>::from_expr(some_number() & 0xf);/// ```////// Comparison and arithmetic operations are supported on [`Bounded`]s with a compatible backing/// type, regardless of their number of valid bits.////// ```/// use kernel::num::Bounded;////// let v1 = Bounded::<u32, 8>::new::<4>();/// let v2 = Bounded::<u32, 4>::new::<15>();////// assert!(v1 != v2);/// assert!(v1 < v2);/// assert_eq!(v1 + v2, 19);/// assert_eq!(v2 % v1, 3);/// ```////// These operations are also supported between a [`Bounded`] and its backing type.////// ```/// use kernel::num::Bounded;////// let v = Bounded::<u8, 4>::new::<15>();////// assert!(v == 15);/// assert!(v > 12);/// assert_eq!(v + 5, 20);/// assert_eq!(v / 3, 5);/// ```////// A change of backing types is possible using [`Bounded::cast`], and the number of valid bits can/// be extended or reduced with [`Bounded::extend`] and [`Bounded::try_shrink`].////// ```/// use kernel::num::Bounded;////// let v = Bounded::<u32, 12>::new::<127>();////// // Changes backing type from `u32` to `u16`./// let _: Bounded<u16, 12> = v.cast();////// // This does not build, as `u8` is smaller than 12 bits./// // let _: Bounded<u8, 12> = v.cast();////// // We can safely extend the number of bits.../// let _ = v.extend::<15>();////// // ... to the limits of the backing type. This doesn't build as a `u32` cannot contain 33 bits./// // let _ = v.extend::<33>();////// // Reducing the number of bits is validated at runtime. This works because `127` can be/// // represented with 8 bits./// assert!(v.try_shrink::<8>().is_some());////// // ... but not with 6, so this fails./// assert!(v.try_shrink::<6>().is_none());/// ```////// Infallible conversions from a primitive integer to a large-enough [`Bounded`] are supported.////// ```/// use kernel::num::Bounded;////// // This unsigned `Bounded` has 8 bits, so it can represent any `u8`./// let v = Bounded::<u32, 8>::from(128u8);/// assert_eq!(v.get(), 128);////// // This signed `Bounded` has 8 bits, so it can represent any `i8`./// let v = Bounded::<i32, 8>::from(-128i8);/// assert_eq!(v.get(), -128);////// // This doesn't build, as this 6-bit `Bounded` does not have enough capacity to represent a/// // `u8` (regardless of the passed value)./// // let _ = Bounded::<u32, 6>::from(10u8);////// // Booleans can be converted into unsigned `Bounded`s.////// let v = Bounded::<u64, 1>::from(false);/// assert_eq!(v.get(), 0);////// let v = Bounded::<u64, 1>::from(true);/// assert_eq!(v.get(), 1);////// // This does not build because `i8` is signed./// // let _ = Bounded::<i8, 2>::from(true);/// ```////// Infallible conversions from a [`Bounded`] to a primitive integer are also supported, and/// dependent on the number of bits used for value representation, not on the backing type.////// ```/// use kernel::num::Bounded;////// // Even though its backing type is `u32`, this `Bounded` only uses 6 bits and thus can safely/// // be converted to a `u8`./// let v = Bounded::<u32, 6>::new::<63>();/// assert_eq!(u8::from(v), 63);////// // Same using signed values./// let v = Bounded::<i32, 8>::new::<-128>();/// assert_eq!(i8::from(v), -128);////// // This however does not build, as 10 bits won't fit into a `u8` (regardless of the actually/// // contained value)./// let _v = Bounded::<u32, 10>::new::<10>();/// // assert_eq!(u8::from(_v), 10);////// // Unsigned single-bit `Bounded`s can be converted into a boolean./// let v = Bounded::<u8, 1>::new::<1>();/// assert_eq!(bool::from(v), true);////// let v = Bounded::<u8, 1>::new::<0>();/// assert_eq!(bool::from(v), false);////// // This does not build because `i8` is signed./// // let v = Bounded::<i8, 1>::new::<-1>();/// // let _ = bool::from(v);/// ```////// Fallible conversions from any primitive integer to any [`Bounded`] are also supported using the/// [`TryIntoBounded`] trait.////// ```/// use kernel::num::{Bounded, TryIntoBounded};////// // Succeeds because `128` fits into 8 bits./// let v: Option<Bounded<u16, 8>> = 128u32.try_into_bounded();/// assert_eq!(v.as_deref().copied(), Some(128));////// // Fails because `128` doesn't fit into 6 bits./// let v: Option<Bounded<u16, 6>> = 128u32.try_into_bounded();/// assert_eq!(v, None);/// ```#[repr(transparent)]#[derive(Clone, Copy, Debug, Default, Hash)]pub struct Bounded<T: Integer, const N: u32>(T); /// Validating the value as a const expression cannot be done as a regular method, as the/// arithmetic operations we rely on to check the bounds are not const. Thus, implement/// [`Bounded::new`] using a macro.macro_rules! impl_const_new {    ($($type:ty)*) => {        $(        impl<const N: u32> Bounded<$type, N> {            /// Creates a [`Bounded`] for the constant `VALUE`.            ///            /// Fails at build time if `VALUE` cannot be represented with `N` bits.            ///            /// This method should be preferred to [`Self::from_expr`] whenever possible.            ///            /// # Examples            ///            /// ```            /// use kernel::num::Bounded;            ///            #[doc = ::core::concat!(                "let v = Bounded::<",                ::core::stringify!($type),                ", 4>::new::<7>();")]            /// assert_eq!(v.get(), 7);            /// ```            pub const fn new<const VALUE: $type>() -> Self {                // Statically assert that `VALUE` fits within the set number of bits.                const_assert!(fits_within!(VALUE, $type, N));                 // SAFETY: `fits_within` confirmed that `VALUE` can be represented within                // `N` bits.                unsafe { Self::__new(VALUE) }            }        }        )*    };} impl_const_new!(    u8 u16 u32 u64 usize    i8 i16 i32 i64 isize); impl<T, const N: u32> Bounded<T, N>where    T: Integer,{    /// Private constructor enforcing the type invariants.    ///    /// All instances of [`Bounded`] must be created through this method as it enforces most of the    /// type invariants.    ///    /// # Safety    ///    /// The caller must ensure that `value` can be represented within `N` bits.    const unsafe fn __new(value: T) -> Self {        // Enforce the type invariants.        // `N` cannot be zero.        const_assert!(N != 0);        // The backing type is at least as large as `N` bits.        const_assert!(N <= T::BITS);         // INVARIANT: The caller ensures `value` fits within `N` bits.        Self(value)    }     /// Attempts to turn `value` into a `Bounded` using `N` bits.    ///    /// Returns [`None`] if `value` doesn't fit within `N` bits.    ///    /// # Examples    ///    /// ```    /// use kernel::num::Bounded;    ///    /// let v = Bounded::<u8, 1>::try_new(1);    /// assert_eq!(v.as_deref().copied(), Some(1));    ///    /// let v = Bounded::<i8, 4>::try_new(-2);    /// assert_eq!(v.as_deref().copied(), Some(-2));    ///    /// // `0x1ff` doesn't fit into 8 unsigned bits.    /// let v = Bounded::<u32, 8>::try_new(0x1ff);    /// assert_eq!(v, None);    ///    /// // The range of values representable with 4 bits is `[-8..=7]`. The following tests these    /// // limits.    /// let v = Bounded::<i8, 4>::try_new(-8);    /// assert_eq!(v.map(Bounded::get), Some(-8));    /// let v = Bounded::<i8, 4>::try_new(-9);    /// assert_eq!(v, None);    /// let v = Bounded::<i8, 4>::try_new(7);    /// assert_eq!(v.map(Bounded::get), Some(7));    /// let v = Bounded::<i8, 4>::try_new(8);    /// assert_eq!(v, None);    /// ```    pub fn try_new(value: T) -> Option<Self> {        fits_within(value, N).then(|| {            // SAFETY: `fits_within` confirmed that `value` can be represented within `N` bits.            unsafe { Self::__new(value) }        })    }     /// Checks that `expr` is valid for this type at compile-time and build a new value.    ///    /// This relies on [`build_assert!`] and guaranteed optimization to perform validation at    /// compile-time. If `expr` cannot be proved to be within the requested bounds at compile-time,    /// use the fallible [`Self::try_new`] instead.    ///    /// Limit this to simple, easily provable expressions, and prefer one of the [`Self::new`]    /// constructors whenever possible as they statically validate the value instead of relying on    /// compiler optimizations.    ///    /// # Examples    ///    /// ```    /// use kernel::num::Bounded;    /// # fn some_number() -> u32 { 0xffffffff }    ///    /// // Some undefined number.    /// let v: u32 = some_number();    ///    /// // Triggers a build error as `v` cannot be asserted to fit within 4 bits...    /// // let _ = Bounded::<u32, 4>::from_expr(v);    ///    /// // ... but this works as the compiler can assert the range from the mask.    /// let _ = Bounded::<u32, 4>::from_expr(v & 0xf);    ///    /// // These expressions are simple enough to be proven correct, but since they are static the    /// // `new` constructor should be preferred.    /// assert_eq!(Bounded::<u8, 1>::from_expr(1).get(), 1);    /// assert_eq!(Bounded::<u16, 8>::from_expr(0xff).get(), 0xff);    /// ```    // Always inline to optimize out error path of `build_assert`.    #[inline(always)]    pub fn from_expr(expr: T) -> Self {        crate::build_assert::build_assert!(            fits_within(expr, N),            "Requested value larger than maximal representable value."        );         // SAFETY: `fits_within` confirmed that `expr` can be represented within `N` bits.        unsafe { Self::__new(expr) }    }     /// Returns the wrapped value as the backing type.    ///    /// This is similar to the [`Deref`] implementation, but doesn't enforce the size invariant of    /// the [`Bounded`], which might produce slightly less optimal code.    ///    /// # Examples    ///    /// ```    /// use kernel::num::Bounded;    ///    /// let v = Bounded::<u32, 4>::new::<7>();    /// assert_eq!(v.get(), 7u32);    /// ```    pub const fn get(self) -> T {        self.0    }     /// Increases the number of bits usable for `self`.    ///    /// This operation cannot fail.    ///    /// # Examples    ///    /// ```    /// use kernel::num::Bounded;    ///    /// let v = Bounded::<u32, 4>::new::<7>();    /// let larger_v = v.extend::<12>();    /// // The contained values are equal even though `larger_v` has a bigger capacity.    /// assert_eq!(larger_v, v);    /// ```    pub const fn extend<const M: u32>(self) -> Bounded<T, M> {        const_assert!(            M >= N,            "Requested number of bits is less than the current representation."        );         // SAFETY: The value did fit within `N` bits, so it will all the more fit within        // the larger `M` bits.        unsafe { Bounded::__new(self.0) }    }     /// Attempts to shrink the number of bits usable for `self`.    ///    /// Returns [`None`] if the value of `self` cannot be represented within `M` bits.    ///    /// # Examples    ///    /// ```    /// use kernel::num::Bounded;    ///    /// let v = Bounded::<u32, 12>::new::<7>();    ///    /// // `7` can be represented using 3 unsigned bits...    /// let smaller_v = v.try_shrink::<3>();    /// assert_eq!(smaller_v.as_deref().copied(), Some(7));    ///    /// // ... but doesn't fit within `2` bits.    /// assert_eq!(v.try_shrink::<2>(), None);    /// ```    pub fn try_shrink<const M: u32>(self) -> Option<Bounded<T, M>> {        Bounded::<T, M>::try_new(self.get())    }     /// Casts `self` into a [`Bounded`] backed by a different storage type, but using the same    /// number of valid bits.    ///    /// Both `T` and `U` must be of same signedness, and `U` must be at least as large as    /// `N` bits, or a build error will occur.    ///    /// # Examples    ///    /// ```    /// use kernel::num::Bounded;    ///    /// let v = Bounded::<u32, 12>::new::<127>();    ///    /// let u16_v: Bounded<u16, 12> = v.cast();    /// assert_eq!(u16_v.get(), 127);    ///    /// // This won't build: a `u8` is smaller than the required 12 bits.    /// // let _: Bounded<u8, 12> = v.cast();    /// ```    pub fn cast<U>(self) -> Bounded<U, N>    where        U: TryFrom<T> + Integer,        T: Integer,        U: Integer<Signedness = T::Signedness>,    {        // SAFETY: The converted value is represented using `N` bits, `U` can contain `N` bits, and        // `U` and `T` have the same sign, hence this conversion cannot fail.        let value = unsafe { U::try_from(self.get()).unwrap_unchecked() };         // SAFETY: Although the backing type has changed, the value is still represented within        // `N` bits, and with the same signedness.        unsafe { Bounded::__new(value) }    }     /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >=    /// N - SHIFT`.    ///    /// # Examples    ///    /// ```    /// use kernel::num::Bounded;    ///    /// let v = Bounded::<u32, 16>::new::<0xff00>();    /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();    ///    /// assert_eq!(v_shifted.get(), 0xff);    /// ```    pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {        const_assert!(SHIFT < T::BITS);        const_assert!(RES + SHIFT >= N); 

Lines 1–500 of 1165.