int_conv/extend/
sign.rs

1//! Sign extension
2
3// Imports
4use crate::Signed;
5use core::mem;
6
7/// Sign extends
8///
9/// This trait serves to extend integers with their
10/// sign signal.
11pub trait SignExtend<T>: Sized {
12	/// Sign extends this type
13	fn sign_extend(self) -> T;
14}
15
16/// Sign extending to the same type simply returns it
17impl<T> SignExtend<T> for T {
18	#[inline]
19	fn sign_extend(self) -> Self {
20		self
21	}
22}
23
24/// Macro to help implement [`SignExtend`]
25///
26/// Note: Regardless if `GAT`s are available, a `impl SignExtend<&'b U> for &'a T` isn't
27///       possible, as it would require memory available for `U` at `T`, which we don't
28///       know from just receiving a reference to `T`.
29macro_rules! impl_sign_extend {
30	($T:ty => $( $U:ty ),+ $(,)?) => {
31		$(
32			// Make sure `U` is bigger or equal to `T` so we don't truncate the integer
33			// Note: It is guaranteed that signed and unsigned variants have the same size
34			::static_assertions::const_assert!(mem::size_of::<$U>() >= mem::size_of::<$T>());
35
36			impl SignExtend<$U> for $T
37			{
38				#[inline]
39				#[allow(clippy::as_conversions)]
40				fn sign_extend(self) -> $U {
41					// Casting between signedness is a no-op.
42					// Casting from a smaller to larger signed integer will sign-extend.
43					self
44						as <$T as Signed>::Signed
45						as <$U as Signed>::Signed
46						as $U
47				}
48			}
49
50			// TODO: Replace with generic version once specialization is stable
51			impl<'a> SignExtend<$U> for &'a $T
52			where
53				$T: SignExtend<$U>
54			{
55				#[inline]
56				fn sign_extend(self) -> $U {
57					<$T as SignExtend<$U>>::sign_extend(*self)
58				}
59			}
60		)+
61	};
62}
63
64// Signed
65impl_sign_extend! { i8   => i16, i32, i64, i128 }
66impl_sign_extend! { i16  =>      i32, i64, i128 }
67impl_sign_extend! { i32  =>           i64, i128 }
68impl_sign_extend! { i64  =>                i128 }
69
70/// Helper trait for [`SignExtend`] to be used with turbofish syntax
71pub trait SignExtended {
72	/// Sign extends this type
73	#[inline]
74	fn sign_extended<T>(self) -> T
75	where
76		Self: SignExtend<T>,
77	{
78		self.sign_extend()
79	}
80}
81impl<T> SignExtended for T {}
82
83// Check that all `SignExtend` / `Extend` impls exist
84static_assertions::assert_impl_all! { i8   : SignExtend<i8>, SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
85static_assertions::assert_impl_all! { i16  :                 SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
86static_assertions::assert_impl_all! { i32  :                                  SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
87static_assertions::assert_impl_all! { i64  :                                                   SignExtend<i64>, SignExtend<i128> }
88static_assertions::assert_impl_all! { i128 :                                                                    SignExtend<i128> }
89
90#[cfg(test)]
91mod tests {
92	// Imports
93	use super::*;
94
95	#[test]
96	#[rustfmt::skip]
97	fn sign_extend_positive() {
98		assert_eq!(i8 ::sign_extended::< i16>(1), 1);
99		assert_eq!(i8 ::sign_extended::< i32>(1), 1);
100		assert_eq!(i8 ::sign_extended::< i64>(1), 1);
101		assert_eq!(i8 ::sign_extended::<i128>(1), 1);
102		assert_eq!(i16::sign_extended::< i32>(1), 1);
103		assert_eq!(i16::sign_extended::< i64>(1), 1);
104		assert_eq!(i16::sign_extended::<i128>(1), 1);
105		assert_eq!(i32::sign_extended::< i64>(1), 1);
106		assert_eq!(i32::sign_extended::<i128>(1), 1);
107		assert_eq!(i64::sign_extended::<i128>(1), 1);
108	}
109
110	#[test]
111	#[rustfmt::skip]
112	fn sign_extend_negative() {
113		assert_eq!( i8::sign_extended::< i16>(-1), -1);
114		assert_eq!( i8::sign_extended::< i32>(-1), -1);
115		assert_eq!( i8::sign_extended::< i64>(-1), -1);
116		assert_eq!( i8::sign_extended::<i128>(-1), -1);
117		assert_eq!(i16::sign_extended::< i32>(-1), -1);
118		assert_eq!(i16::sign_extended::< i64>(-1), -1);
119		assert_eq!(i16::sign_extended::<i128>(-1), -1);
120		assert_eq!(i32::sign_extended::< i64>(-1), -1);
121		assert_eq!(i32::sign_extended::<i128>(-1), -1);
122		assert_eq!(i64::sign_extended::<i128>(-1), -1);
123	}
124}