Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 7 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading