Skip to content

Commit

Permalink
bounded-collections: Add Const(Int|Uint) for generic const getter sup…
Browse files Browse the repository at this point in the history
…port (#878)

* bounded-collections: Add Const(Int|Uint) for generic const getter support

* bounded-collections: Update changelog

* bounded-collections: Clean up helper types for ConstUint

* bounded-collections: Fix broken compile-time check

* bounded-collections: Add compile_fail doc test for overflown ConstUint

* bounded-collections: Address review comments

---------

Co-authored-by: ordian <[email protected]>
  • Loading branch information
conr2d and ordian authored Nov 4, 2024
1 parent 315299d commit 31ae234
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
3 changes: 3 additions & 0 deletions bounded-collections/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog].

[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/

## [Unreleased]
- Added `ConstInt` and `ConstUint` types. [#878](https://github.com/paritytech/parity-common/pull/878)

## [0.2.1] - 2024-10-08
- Added `serde` support for `BoundedBTreeMap`. [#870](https://github.com/paritytech/parity-common/pull/870)

Expand Down
153 changes: 153 additions & 0 deletions bounded-collections/src/const_int.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright (C) Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::{Get, TypedGet};
use core::marker::PhantomData;

// Numbers which have constant upper and lower bounds.
trait ConstBounded<T> {
const MIN: T;
const MAX: T;
}

macro_rules! impl_const_bounded {
($bound:ty, $t:ty) => {
impl ConstBounded<$bound> for $t {
const MIN: $bound = <$t>::MIN as $bound;
const MAX: $bound = <$t>::MAX as $bound;
}
};
}

impl_const_bounded!(u128, u8);
impl_const_bounded!(u128, u16);
impl_const_bounded!(u128, u32);
impl_const_bounded!(u128, u64);
impl_const_bounded!(u128, u128);
impl_const_bounded!(u128, usize);

impl_const_bounded!(i128, i8);
impl_const_bounded!(i128, i16);
impl_const_bounded!(i128, i32);
impl_const_bounded!(i128, i64);
impl_const_bounded!(i128, i128);

// Check whether a unsigned integer is within the bounds of a type.
struct CheckOverflowU128<T: ConstBounded<u128>, const N: u128>(PhantomData<T>);

impl<T: ConstBounded<u128>, const N: u128> CheckOverflowU128<T, N> {
const ASSERTION: () = assert!(N >= T::MIN && N <= T::MAX);
}

// Check whether an integer is within the bounds of a type.
struct CheckOverflowI128<T: ConstBounded<i128>, const N: i128>(PhantomData<T>);

impl<T: ConstBounded<i128>, const N: i128> CheckOverflowI128<T, N> {
const ASSERTION: () = assert!(N >= T::MIN && N <= T::MAX);
}

/// Const getter for unsigned integers.
///
/// # Compile-time checks
///
/// ```compile_fail
/// # use bounded_collections::{ConstUint, Get};
/// let _ = <ConstUint<256> as Get<u8>>::get();
/// ```
#[derive(Default, Clone)]
pub struct ConstUint<const N: u128>;

impl<const N: u128> core::fmt::Debug for ConstUint<N> {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
fmt.write_str(&alloc::format!("ConstUint<{}>", N))
}
}

impl<const N: u128> TypedGet for ConstUint<N> {
type Type = u128;
fn get() -> u128 {
N
}
}

/// Const getter for signed integers.
#[derive(Default, Clone)]
pub struct ConstInt<const N: i128>;

impl<const N: i128> core::fmt::Debug for ConstInt<N> {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
fmt.write_str(&alloc::format!("ConstInt<{}>", N))
}
}

impl<const N: i128> TypedGet for ConstInt<N> {
type Type = i128;
fn get() -> i128 {
N
}
}

macro_rules! impl_const_int {
($t:ident, $check:ident, $bound:ty, $target:ty) => {
impl<const N: $bound> Get<$target> for $t<N> {
fn get() -> $target {
let _ = <$check<$target, N>>::ASSERTION;
N as $target
}
}
impl<const N: $bound> Get<Option<$target>> for $t<N> {
fn get() -> Option<$target> {
let _ = <$check<$target, N>>::ASSERTION;
Some(N as $target)
}
}
};
}

impl_const_int!(ConstUint, CheckOverflowU128, u128, u8);
impl_const_int!(ConstUint, CheckOverflowU128, u128, u16);
impl_const_int!(ConstUint, CheckOverflowU128, u128, u32);
impl_const_int!(ConstUint, CheckOverflowU128, u128, u64);
impl_const_int!(ConstUint, CheckOverflowU128, u128, u128);
impl_const_int!(ConstUint, CheckOverflowU128, u128, usize);

impl_const_int!(ConstInt, CheckOverflowI128, i128, i8);
impl_const_int!(ConstInt, CheckOverflowI128, i128, i16);
impl_const_int!(ConstInt, CheckOverflowI128, i128, i32);
impl_const_int!(ConstInt, CheckOverflowI128, i128, i64);
impl_const_int!(ConstInt, CheckOverflowI128, i128, i128);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn const_uint_works() {
assert_eq!(<ConstUint<42> as Get<u8>>::get(), 42);
assert_eq!(<ConstUint<42> as Get<Option<u8>>>::get(), Some(42));
assert_eq!(<ConstUint<42> as Get<u16>>::get(), 42);
assert_eq!(<ConstUint<42> as Get<u32>>::get(), 42);
assert_eq!(<ConstUint<42> as Get<u64>>::get(), 42);
assert_eq!(<ConstUint<42> as Get<u128>>::get(), 42);
assert_eq!(<ConstUint<42> as Get<usize>>::get(), 42);
assert_eq!(<ConstUint<42> as TypedGet>::get(), 42);
// compile-time error
// assert_eq!(<ConstUint<256> as Get<u8>>::get() as u128, 256);
}

#[test]
fn const_int_works() {
assert_eq!(<ConstInt<-42> as Get<i8>>::get(), -42);
assert_eq!(<ConstInt<-42> as Get<Option<i8>>>::get(), Some(-42));
assert_eq!(<ConstInt<-42> as Get<i16>>::get(), -42);
assert_eq!(<ConstInt<-42> as Get<i32>>::get(), -42);
assert_eq!(<ConstInt<-42> as Get<i64>>::get(), -42);
assert_eq!(<ConstInt<-42> as Get<i128>>::get(), -42);
assert_eq!(<ConstInt<-42> as TypedGet>::get(), -42);
}
}
2 changes: 2 additions & 0 deletions bounded-collections/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ pub extern crate alloc;
pub mod bounded_btree_map;
pub mod bounded_btree_set;
pub mod bounded_vec;
pub mod const_int;
pub mod weak_bounded_vec;

mod test;

pub use bounded_btree_map::BoundedBTreeMap;
pub use bounded_btree_set::BoundedBTreeSet;
pub use bounded_vec::{BoundedSlice, BoundedVec};
pub use const_int::{ConstInt, ConstUint};
pub use weak_bounded_vec::WeakBoundedVec;

/// A trait for querying a single value from a type defined in the trait.
Expand Down

0 comments on commit 31ae234

Please sign in to comment.