1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Sign extension

// Imports
use crate::Signed;
use core::mem;

/// Sign extends
///
/// This trait serves to extend integers with their
/// sign signal.
pub trait SignExtend<T>: Sized {
	/// Sign extends this type
	fn sign_extend(self) -> T;
}

/// Sign extending to the same type simply returns it
impl<T> SignExtend<T> for T {
	#[inline]
	fn sign_extend(self) -> Self {
		self
	}
}

/// Macro to help implement [`SignExtend`]
///
/// Note: Regardless if `GAT`s are available, a `impl SignExtend<&'b U> for &'a T` isn't
///       possible, as it would require memory available for `U` at `T`, which we don't
///       know from just receiving a reference to `T`.
macro_rules! impl_sign_extend {
	($T:ty => $( $U:ty ),+ $(,)?) => {
		$(
			// Make sure `U` is bigger or equal to `T` so we don't truncate the integer
			// Note: It is guaranteed that signed and unsigned variants have the same size
			::static_assertions::const_assert!(mem::size_of::<$U>() >= mem::size_of::<$T>());

			impl SignExtend<$U> for $T
			{
				#[inline]
				#[allow(clippy::as_conversions)]
				fn sign_extend(self) -> $U {
					// Casting between signedness is a no-op.
					// Casting from a smaller to larger signed integer will sign-extend.
					self
						as <$T as Signed>::Signed
						as <$U as Signed>::Signed
						as $U
				}
			}

			// TODO: Replace with generic version once specialization is stable
			impl<'a> SignExtend<$U> for &'a $T
			where
				$T: SignExtend<$U>
			{
				#[inline]
				fn sign_extend(self) -> $U {
					<$T as SignExtend<$U>>::sign_extend(*self)
				}
			}
		)+
	};
}

// Signed
impl_sign_extend! { i8   => i16, i32, i64, i128 }
impl_sign_extend! { i16  =>      i32, i64, i128 }
impl_sign_extend! { i32  =>           i64, i128 }
impl_sign_extend! { i64  =>                i128 }

/// Helper trait for [`SignExtend`] to be used with turbofish syntax
pub trait SignExtended {
	/// Sign extends this type
	#[inline]
	fn sign_extended<T>(self) -> T
	where
		Self: SignExtend<T>,
	{
		self.sign_extend()
	}
}
impl<T> SignExtended for T {}

// Check that all `SignExtend` / `Extend` impls exist
static_assertions::assert_impl_all! { i8   : SignExtend<i8>, SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
static_assertions::assert_impl_all! { i16  :                 SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
static_assertions::assert_impl_all! { i32  :                                  SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
static_assertions::assert_impl_all! { i64  :                                                   SignExtend<i64>, SignExtend<i128> }
static_assertions::assert_impl_all! { i128 :                                                                    SignExtend<i128> }

#[cfg(test)]
mod tests {
	// Imports
	use super::*;

	#[test]
	#[rustfmt::skip]
	fn sign_extend_positive() {
		assert_eq!(i8 ::sign_extended::< i16>(1), 1);
		assert_eq!(i8 ::sign_extended::< i32>(1), 1);
		assert_eq!(i8 ::sign_extended::< i64>(1), 1);
		assert_eq!(i8 ::sign_extended::<i128>(1), 1);
		assert_eq!(i16::sign_extended::< i32>(1), 1);
		assert_eq!(i16::sign_extended::< i64>(1), 1);
		assert_eq!(i16::sign_extended::<i128>(1), 1);
		assert_eq!(i32::sign_extended::< i64>(1), 1);
		assert_eq!(i32::sign_extended::<i128>(1), 1);
		assert_eq!(i64::sign_extended::<i128>(1), 1);
	}

	#[test]
	#[rustfmt::skip]
	fn sign_extend_negative() {
		assert_eq!( i8::sign_extended::< i16>(-1), -1);
		assert_eq!( i8::sign_extended::< i32>(-1), -1);
		assert_eq!( i8::sign_extended::< i64>(-1), -1);
		assert_eq!( i8::sign_extended::<i128>(-1), -1);
		assert_eq!(i16::sign_extended::< i32>(-1), -1);
		assert_eq!(i16::sign_extended::< i64>(-1), -1);
		assert_eq!(i16::sign_extended::<i128>(-1), -1);
		assert_eq!(i32::sign_extended::< i64>(-1), -1);
		assert_eq!(i32::sign_extended::<i128>(-1), -1);
		assert_eq!(i64::sign_extended::<i128>(-1), -1);
	}
}