int_conv/
trunc.rs

1//! Integer truncation
2//!
3//! This module contains the [`Truncate`] trait used for
4//! truncating integers to a smaller integer
5
6// Imports
7use core::mem;
8
9/// Truncates this integer to a lower size
10pub trait Truncate<T>: Sized {
11	/// Performs the truncation
12	fn truncate(self) -> T;
13}
14
15/// Truncating to the same type simply returns it
16impl<T> Truncate<T> for T {
17	#[inline]
18	fn truncate(self) -> T {
19		self
20	}
21}
22
23/// Macro to help implement `Truncate`
24///
25/// Note: We don't currently `Truncate<&'b U> for &'a T` due
26///       to requiring `GAT`s, but we do implement `Truncate<U> for &'a T`
27///       by simply copying the underlying type.
28macro_rules! impl_truncate {
29	($T:ty => $($U:ty),* $(,)?) => {
30		$(
31			// Make sure `T` is larger than `U`, so we don't extend it.
32			::static_assertions::const_assert!(mem::size_of::<$T>() > mem::size_of::<$U>());
33
34			impl Truncate<$U> for $T {
35				#[inline]
36				#[allow(clippy::as_conversions)]
37				fn truncate(self) -> $U {
38					// Casting from a larger to a smaller integer truncates
39					self as $U
40				}
41			}
42
43			impl<'a> Truncate<$U> for &'a $T {
44				#[inline]
45				fn truncate(self) -> $U {
46					<$T as Truncate<$U>>::truncate(*self)
47				}
48			}
49		)*
50	};
51}
52
53// Unsigned
54impl_truncate! { u128 => u64, u32, u16, u8 }
55impl_truncate! { u64  =>      u32, u16, u8 }
56impl_truncate! { u32  =>           u16, u8 }
57impl_truncate! { u16  =>                u8 }
58
59// Signed
60impl_truncate! { i128 => i64, i32, i16, i8 }
61impl_truncate! { i64  =>      i32, i16, i8 }
62impl_truncate! { i32  =>           i16, i8 }
63impl_truncate! { i16  =>                i8 }
64
65/// Helper trait for [`Truncate`] to be used with turbofish syntax
66pub trait Truncated {
67	/// Truncates this type
68	#[inline]
69	fn truncated<T>(self) -> T
70	where
71		Self: Truncate<T>,
72	{
73		self.truncate()
74	}
75}
76impl<T> Truncated for T {}
77
78// Check that all `Truncate` impls exist
79static_assertions::assert_impl_all! { i128 : Truncate<i128>, Truncate<i64>, Truncate<i32>, Truncate<i16>, Truncate<i8> }
80static_assertions::assert_impl_all! { i64  :                 Truncate<i64>, Truncate<i32>, Truncate<i16>, Truncate<i8> }
81static_assertions::assert_impl_all! { i32  :                                Truncate<i32>, Truncate<i16>, Truncate<i8> }
82static_assertions::assert_impl_all! { i16  :                                               Truncate<i16>, Truncate<i8> }
83static_assertions::assert_impl_all! { i8   :                                                              Truncate<i8> }
84static_assertions::assert_impl_all! { u128 : Truncate<u128>, Truncate<u64>, Truncate<u32>, Truncate<u16>, Truncate<u8> }
85static_assertions::assert_impl_all! { u64  :                 Truncate<u64>, Truncate<u32>, Truncate<u16>, Truncate<u8> }
86static_assertions::assert_impl_all! { u32  :                                Truncate<u32>, Truncate<u16>, Truncate<u8> }
87static_assertions::assert_impl_all! { u16  :                                               Truncate<u16>, Truncate<u8> }
88static_assertions::assert_impl_all! { u8   :                                                              Truncate<u8> }
89
90#[cfg(test)]
91mod tests {
92	use super::*;
93
94	#[test]
95	#[rustfmt::skip]
96	fn truncate_unsigned() {
97		assert_eq!(u128::truncated::<u128>(1), 1);
98		assert_eq!(u128::truncated::< u64>(1), 1);
99		assert_eq!(u128::truncated::< u32>(1), 1);
100		assert_eq!(u128::truncated::< u16>(1), 1);
101		assert_eq!(u128::truncated::<  u8>(1), 1);
102		assert_eq!( u64::truncated::< u64>(1), 1);
103		assert_eq!( u64::truncated::< u32>(1), 1);
104		assert_eq!( u64::truncated::< u16>(1), 1);
105		assert_eq!( u64::truncated::<  u8>(1), 1);
106		assert_eq!( u32::truncated::< u32>(1), 1);
107		assert_eq!( u32::truncated::< u16>(1), 1);
108		assert_eq!( u32::truncated::<  u8>(1), 1);
109		assert_eq!( u16::truncated::< u16>(1), 1);
110		assert_eq!( u16::truncated::<  u8>(1), 1);
111		assert_eq!(  u8::truncated::<  u8>(1), 1);
112	}
113
114	#[test]
115	#[rustfmt::skip]
116	fn truncate_signed() {
117		assert_eq!(i128::truncated::<i128>(-1), -1);
118		assert_eq!(i128::truncated::< i64>(-1), -1);
119		assert_eq!(i128::truncated::< i32>(-1), -1);
120		assert_eq!(i128::truncated::< i16>(-1), -1);
121		assert_eq!(i128::truncated::<  i8>(-1), -1);
122		assert_eq!( i64::truncated::< i64>(-1), -1);
123		assert_eq!( i64::truncated::< i32>(-1), -1);
124		assert_eq!( i64::truncated::< i16>(-1), -1);
125		assert_eq!( i64::truncated::<  i8>(-1), -1);
126		assert_eq!( i32::truncated::< i32>(-1), -1);
127		assert_eq!( i32::truncated::< i16>(-1), -1);
128		assert_eq!( i32::truncated::<  i8>(-1), -1);
129		assert_eq!( i16::truncated::< i16>(-1), -1);
130		assert_eq!( i16::truncated::<  i8>(-1), -1);
131		assert_eq!(  i8::truncated::<  i8>(-1), -1);
132	}
133}