use core::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
use crate::internal::Sealed;
#[allow(private_bounds)]
pub trait Int:
Sealed
+ Sized
+ Copy
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Not<Output = Self>
+ BitAnd<Output = Self>
+ BitAndAssign
+ BitOr<Output = Self>
+ BitOrAssign
+ BitXor<Output = Self>
+ BitXorAssign
+ Shl<Output = Self>
+ Shl<u32, Output = Self>
+ ShlAssign
+ ShlAssign<u32>
+ Shr<Output = Self>
+ Shr<u32, Output = Self>
+ ShrAssign
+ ShrAssign<u32>
+ Add<Output = Self>
+ AddAssign
+ Sub<Output = Self>
+ SubAssign
+ Mul<Output = Self>
+ MulAssign
+ Div<Output = Self>
+ DivAssign
+ Rem<Output = Self>
+ RemAssign
{
const ZERO: Self;
const ONE: Self;
const MIN: Self;
const MAX: Self;
fn to_usize(&self) -> usize {
const MSG: &str = "Value out of bounds for usize";
#[cfg(not(coverage))]
{
self.checked_to_usize().unwrap_or_else(|| {
let val = *self;
debug_assert!(val >= Self::ZERO && val <= Self::MAX as Self, "{MSG}");
self.saturating_to_usize()
})
}
#[cfg(coverage)]
{
self.checked_to_usize().expect(MSG)
}
}
fn saturating_to_usize(&self) -> usize {
self.checked_to_usize().unwrap_or_else(|| {
if *self < Self::ZERO { 0 } else { usize::MAX }
})
}
fn checked_to_usize(&self) -> Option<usize>;
#[must_use]
fn from_usize(value: usize) -> Self {
const MSG: &str = "Value out of bounds for integer type";
#[cfg(not(coverage))]
{
Self::checked_from_usize(value).unwrap_or_else(|| {
debug_assert!(value <= Self::MAX.to_usize(), "{MSG}",);
Self::saturating_from_usize(value)
})
}
#[cfg(coverage)]
{
Self::checked_from_usize(value).expect(MSG)
}
}
#[must_use]
fn saturating_from_usize(value: usize) -> Self {
Self::checked_from_usize(value).unwrap_or(Self::MAX)
}
fn checked_from_usize(value: usize) -> Option<Self>;
#[must_use]
fn abs(self) -> Self;
#[must_use]
fn trailing_zeros(self) -> u32;
}
pub trait SignedInt: Int + Neg<Output = Self> {
const NEG_ONE: Self;
}
macro_rules! impl_unsigned_int {
($($t:ty),*) => {
$(
impl Sealed for $t {}
impl Int for $t {
const ZERO: Self = 0;
const ONE: Self = 1;
const MIN: Self = <$t>::MIN;
const MAX: Self = <$t>::MAX;
fn checked_to_usize(&self) -> Option<usize> {
usize::try_from(*self).ok()
}
fn checked_from_usize(value: usize) -> Option<Self> {
Self::try_from(value).ok()
}
fn abs(self) -> Self {
self
}
fn trailing_zeros(self) -> u32 {
self.trailing_zeros()
}
}
)*
};
}
macro_rules! impl_signed_int {
($($t:ty),*) => {
$(
impl Sealed for $t {}
impl Int for $t {
const ZERO: Self = 0;
const ONE: Self = 1;
const MIN: Self = <$t>::MIN;
const MAX: Self = <$t>::MAX;
fn checked_to_usize(&self) -> Option<usize> {
usize::try_from(*self).ok()
}
fn checked_from_usize(value: usize) -> Option<Self> {
Self::try_from(value).ok()
}
fn abs(self) -> Self {
if self < Self::ZERO {
-self
} else {
self
}
}
fn trailing_zeros(self) -> u32 {
self.trailing_zeros()
}
}
impl SignedInt for $t {
const NEG_ONE: Self = -1;
}
)*
};
}
#[rustfmt::skip]
impl_unsigned_int!(
u8,
u16,
u32,
u64,
u128,
usize
);
#[rustfmt::skip]
impl_signed_int!(
i8,
i16,
i32,
i64,
i128,
isize
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn u8_to_usize() {
let value = 255u8;
assert_eq!(value.to_usize(), 255);
assert_eq!(value.checked_to_usize(), Some(255));
assert_eq!(value.saturating_to_usize(), 255);
}
#[test]
fn u8_from_usize_ok() {
let value = 255usize;
assert_eq!(u8::from_usize(value), 255);
assert_eq!(u8::checked_from_usize(value), Some(255));
assert_eq!(u8::saturating_from_usize(value), 255);
}
#[test]
fn u8_from_usize_out_of_bounds() {
let value = 256usize;
assert_eq!(u8::checked_from_usize(value), None);
assert_eq!(u8::saturating_from_usize(value), 255);
}
#[test]
#[should_panic(expected = "Value out of bounds for integer type")]
fn u8_from_usize_out_of_bounds_panic() {
let _ = u8::from_usize(256usize); }
#[test]
fn i8_to_usize_ok() {
let value = 127i8;
assert_eq!(value.checked_to_usize(), Some(127));
assert_eq!(value.saturating_to_usize(), 127);
assert_eq!(value.to_usize(), 127);
}
#[test]
fn i8_to_usize_negative_err() {
let value = -1i8;
assert_eq!(value.checked_to_usize(), None);
assert_eq!(value.saturating_to_usize(), 0);
}
#[test]
#[should_panic(expected = "Value out of bounds for usize")]
fn i8_to_usize_negative_panic() {
let _ = (-1i8).to_usize(); }
#[test]
fn i8_from_usize_ok() {
let value = 127usize;
assert_eq!(i8::from_usize(value), 127);
assert_eq!(i8::checked_from_usize(value), Some(127));
assert_eq!(i8::saturating_from_usize(value), 127);
}
#[test]
fn i8_from_usize_out_of_bounds() {
let value = 128usize;
assert_eq!(i8::checked_from_usize(value), None);
assert_eq!(i8::saturating_from_usize(value), 127);
}
#[test]
#[should_panic(expected = "Value out of bounds for integer type")]
fn i8_from_usize_out_of_bounds_panic() {
let _ = i8::from_usize(128usize); }
#[test]
fn i128_to_usize_saturating_out_of_bounds() {
let value = i128::MAX;
assert_eq!(value.checked_to_usize(), None);
assert_eq!(value.saturating_to_usize(), usize::MAX);
}
}