add utility functions for getting smallest integer type that fits a range/value

This commit is contained in:
Jacob Lifshay 2024-07-21 20:46:23 -07:00
parent bdfa18e11d
commit 8080cc7b5c
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c

View file

@ -18,7 +18,10 @@ use std::{
fmt, fmt,
hash::Hash, hash::Hash,
marker::PhantomData, marker::PhantomData,
ops::{Add, BitAnd, BitOr, BitXor, Bound, Mul, Neg, Not, Range, RangeBounds, Shl, Shr, Sub}, ops::{
Add, BitAnd, BitOr, BitXor, Bound, Mul, Neg, Not, Range, RangeBounds, RangeInclusive, Shl,
Shr, Sub,
},
}; };
#[derive(Clone, Eq, PartialEq, Hash, Default)] #[derive(Clone, Eq, PartialEq, Hash, Default)]
@ -1003,6 +1006,66 @@ impl DynUIntType {
pub fn mask(self) -> BigUint { pub fn mask(self) -> BigUint {
self.modulo() - 1u8 self.modulo() - 1u8
} }
/// gets the smallest `UInt` that fits `v` losslessly
pub fn for_value(v: impl Into<BigUint>) -> Self {
let v: BigUint = v.into();
Self::new(v.bits().try_into().expect("too big"))
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range(r: Range<impl Into<BigUint>>) -> Self {
let start: BigUint = r.start.into();
let end: BigUint = r.end.into();
assert!(!end.is_zero(), "empty range");
Self::range_inclusive(start..=(end - 1u8))
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range_inclusive(r: RangeInclusive<impl Into<BigUint>>) -> Self {
let (start, end) = r.into_inner();
let start: BigUint = start.into();
let end: BigUint = end.into();
assert!(start <= end, "empty range");
// no need to check `start`` since it's no larger than `end`
// so must not take more bits than `end`
Self::for_value(end)
}
}
impl DynSIntType {
/// gets the smallest `SInt` that fits `v` losslessly
pub fn for_value(v: impl Into<BigInt>) -> Self {
let v: BigInt = v.into();
Self::new(
match v.sign() {
Sign::Minus => {
// account for sign bit and for the minimum value of an `SInt`
// being the negative of the maximum value minus one.
v.not().bits().checked_add(1).expect("too big")
}
Sign::NoSign => 0,
Sign::Plus => v.bits(),
}
.try_into()
.expect("too big"),
)
}
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range(r: Range<impl Into<BigInt>>) -> Self {
let start: BigInt = r.start.into();
let end: BigInt = r.end.into();
Self::range_inclusive(start..=(end - 1))
}
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range_inclusive(r: RangeInclusive<impl Into<BigInt>>) -> Self {
let (start, end) = r.into_inner();
let start: BigInt = start.into();
let end: BigInt = end.into();
assert!(start <= end, "empty range");
Self::new(Self::for_value(start).width.max(Self::for_value(end).width))
}
} }
impl<Signed: GenericConstBool> sealed::Sealed for DynIntType<Signed> {} impl<Signed: GenericConstBool> sealed::Sealed for DynIntType<Signed> {}