Skip to content

Commit 79e1ee2

Browse files
committed
Add wrapping ops for const generic integers
1 parent 6fe13c8 commit 79e1ee2

4 files changed

Lines changed: 466 additions & 38 deletions

File tree

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ pub use types::*;
114114
mod parse;
115115
pub use parse::{ParseError, ParseErrorKind};
116116

117+
mod prim_int;
118+
pub use prim_int::PrimInt;
119+
117120
#[doc(hidden)]
118121
#[cfg(feature = "macro")]
119122
pub mod __private {

src/prim_int.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use core::mem::transmute_copy;
2+
use core::num::NonZero;
3+
4+
/// Marker trait for primitive integers.
5+
pub trait PrimInt: sealed::Sealed {}
6+
7+
mod sealed {
8+
use super::PrimInt;
9+
10+
// Not public API.
11+
pub trait Sealed: 'static + Sized + Copy {
12+
type NonZero: Into<Self>;
13+
type Unsigned: Sealed;
14+
fn checked_add_unsigned(self, rhs: Self::Unsigned) -> Option<Self>;
15+
fn checked_sub_unsigned(self, rhs: Self::Unsigned) -> Option<Self>;
16+
fn rem_euclid_unsigned(self, rhs: <Self::Unsigned as Sealed>::NonZero) -> Self::Unsigned;
17+
fn truncate<T: PrimInt + Into<Self>>(self) -> T;
18+
fn nonzero_from<T: PrimInt + Into<Self>>(val: T::NonZero) -> Self::NonZero;
19+
}
20+
}
21+
pub(crate) use sealed::Sealed;
22+
23+
gen! {
24+
u8 i8,
25+
u16 i16,
26+
u32 i32,
27+
u64 i64,
28+
u128 i128,
29+
usize isize,
30+
}
31+
32+
macro_rules! gen {
33+
($($unsigned:ident $signed:ident,)*) => { $(
34+
pub(crate) mod $unsigned {
35+
use core::num::NonZero;
36+
37+
pub(crate) type Unsigned = $unsigned;
38+
pub(crate) type Signed = $signed;
39+
pub(crate) use super::$signed as signed;
40+
41+
pub(crate) const fn checked_add_unsigned(lhs: Unsigned, rhs: Unsigned) -> Option<Unsigned> {
42+
lhs.checked_add(rhs)
43+
}
44+
pub(crate) const fn checked_sub_unsigned(lhs: Unsigned, rhs: Unsigned) -> Option<Unsigned> {
45+
lhs.checked_sub(rhs)
46+
}
47+
pub(crate) const fn rem_euclid_unsigned(lhs: Unsigned, rhs: NonZero<Unsigned>) -> Unsigned {
48+
lhs.rem_euclid(rhs.get())
49+
}
50+
}
51+
pub(crate) mod $signed {
52+
use core::num::NonZero;
53+
54+
pub(crate) type Unsigned = $unsigned;
55+
pub(crate) type Signed = $signed;
56+
pub(crate) use super::$signed as signed;
57+
58+
pub(crate) const fn checked_add_unsigned(lhs: Signed, rhs: Unsigned) -> Option<Signed> {
59+
lhs.checked_add_unsigned(rhs)
60+
}
61+
pub(crate) const fn checked_sub_unsigned(lhs: Signed, rhs: Unsigned) -> Option<Signed> {
62+
lhs.checked_sub_unsigned(rhs)
63+
}
64+
pub(crate) const fn rem_euclid_unsigned(lhs: Signed, rhs: NonZero<Unsigned>) -> Unsigned {
65+
// In my benchmarks, this is faster than methods involving widening.
66+
if 0 <= lhs {
67+
// If `lhs` is nonnegative, just use regular unsigned remainder.
68+
(lhs as $unsigned).rem_euclid(rhs.get())
69+
} else if 0 <= rhs.get() as $signed {
70+
// If `rhs` is small enough to fit in a signed type, use regular `rem_euclid`.
71+
// We know the result is nonnegative, so we can cast to the unsigned type.
72+
lhs.rem_euclid(rhs.get() as $signed) as $unsigned
73+
} else {
74+
// Otherwise, `lhs` is negative and `rhs` is larger than all signed values. We
75+
// can therefore add `rhs` to `lhs` and get an unsigned value without overflow,
76+
// which won’t affect the result.
77+
rhs.get().checked_add_signed(lhs).unwrap().rem_euclid(rhs.get())
78+
}
79+
}
80+
}
81+
)* gen_impl!($($unsigned $signed)*); };
82+
}
83+
use gen;
84+
85+
macro_rules! gen_impl {
86+
($($ty:ident)*) => { $(
87+
impl PrimInt for $ty {}
88+
89+
impl Sealed for $ty {
90+
type NonZero = NonZero<$ty>;
91+
type Unsigned = $ty::Unsigned;
92+
fn checked_add_unsigned(self, rhs: Self::Unsigned) -> Option<Self> {
93+
$ty::checked_add_unsigned(self, rhs)
94+
}
95+
fn checked_sub_unsigned(self, rhs: Self::Unsigned) -> Option<Self> {
96+
$ty::checked_sub_unsigned(self, rhs)
97+
}
98+
fn rem_euclid_unsigned(self, rhs: NonZero<Self::Unsigned>) -> Self::Unsigned {
99+
$ty::rem_euclid_unsigned(self, rhs)
100+
}
101+
fn truncate<T: PrimInt + Into<Self>>(self) -> T {
102+
match size_of::<T>() {
103+
1 => unsafe { transmute_copy(&(self as u8)) },
104+
2 => unsafe { transmute_copy(&(self as u16)) },
105+
4 => unsafe { transmute_copy(&(self as u32)) },
106+
8 => unsafe { transmute_copy(&(self as u64)) },
107+
16 => unsafe { transmute_copy(&(self as u128)) },
108+
_ => unreachable!(),
109+
}
110+
}
111+
fn nonzero_from<T: PrimInt + Into<Self>>(val: T::NonZero) -> Self::NonZero {
112+
let val: T = val.into();
113+
let val: Self = val.into();
114+
unsafe { NonZero::new_unchecked(val) }
115+
}
116+
}
117+
)* };
118+
}
119+
use gen_impl;

src/types/indexing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Indexing operations on [T; N], Vec<T> and VecDeque<T> for BoundedUsize
1+
//! Indexing operations on `[T; N]`, `Vec<T>` and `VecDeque<T>` for BoundedUsize
22
33
use super::BoundedUsize;
44
use core::ops::Index;

0 commit comments

Comments
 (0)