int_conv/extend/
zero.rs

1//! Zero extension
2
3// Imports
4use crate::Signed;
5use core::mem;
6
7/// Zero extend
8///
9/// This trait serves to extend integers with `0`s,
10/// including signed ones.
11pub trait ZeroExtend<T>: Sized {
12	/// Zero extends this type
13	fn zero_extend(self) -> T;
14}
15
16/// Zero extending to the same type simply returns it
17impl<T> ZeroExtend<T> for T {
18	#[inline]
19	fn zero_extend(self) -> Self {
20		self
21	}
22}
23
24/// Macro to help implement [`ZeroExtend`]
25///
26/// Note: Regardless if `GAT`s are available, a `impl ZeroExtend<&'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_zero_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 ZeroExtend<$U> for $T
37			{
38				#[inline]
39				#[allow(clippy::as_conversions)]
40				fn zero_extend(self) -> $U {
41					// Casting between signedness is a no-op.
42					// Casting from a smaller to larger unsigned integer will zero-extend.
43					self
44						as <$T as Signed>::Unsigned
45						as <$U as Signed>::Unsigned
46						as $U
47				}
48			}
49
50			// TODO: Replace with generic version once specialization is stable
51			impl<'a> ZeroExtend<$U> for &'a $T
52			where
53				$T: ZeroExtend<$U>
54			{
55				#[inline]
56				fn zero_extend(self) -> $U {
57					<$T as ZeroExtend<$U>>::zero_extend(*self)
58				}
59			}
60		)+
61	};
62}
63
64// Unsigned
65impl_zero_extend! { u8   => u16, u32, u64, u128 }
66impl_zero_extend! { u16  =>      u32, u64, u128 }
67impl_zero_extend! { u32  =>           u64, u128 }
68impl_zero_extend! { u64  =>                u128 }
69
70// Signed
71impl_zero_extend! { i8   => i16, i32, i64, i128 }
72impl_zero_extend! { i16  =>      i32, i64, i128 }
73impl_zero_extend! { i32  =>           i64, i128 }
74impl_zero_extend! { i64  =>                i128 }
75
76/// Helper trait for [`ZeroExtend`] to be used with turbofish syntax
77pub trait ZeroExtended: Sized {
78	/// Zero extends this type
79	#[inline]
80	fn zero_extended<T>(self) -> T
81	where
82		Self: ZeroExtend<T>,
83	{
84		self.zero_extend()
85	}
86}
87impl<T> ZeroExtended for T {}
88
89// Check that all `ZeroExtend` impls exist
90static_assertions::assert_impl_all! { i8   : ZeroExtend<i8>, ZeroExtend<i16>, ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
91static_assertions::assert_impl_all! { i16  :                 ZeroExtend<i16>, ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
92static_assertions::assert_impl_all! { i32  :                                  ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
93static_assertions::assert_impl_all! { i64  :                                                   ZeroExtend<i64>, ZeroExtend<i128> }
94static_assertions::assert_impl_all! { i128 :                                                                    ZeroExtend<i128> }
95static_assertions::assert_impl_all! { u8   : ZeroExtend<u8>, ZeroExtend<u16>, ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
96static_assertions::assert_impl_all! { u16  :                 ZeroExtend<u16>, ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
97static_assertions::assert_impl_all! { u32  :                                  ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
98static_assertions::assert_impl_all! { u64  :                                                   ZeroExtend<u64>, ZeroExtend<u128> }
99static_assertions::assert_impl_all! { u128 :                                                                    ZeroExtend<u128> }
100
101#[cfg(test)]
102mod tests {
103	// Imports
104	use super::*;
105
106	#[test]
107	#[rustfmt::skip]
108	fn zero_extend_unsigned() {
109		assert_eq!(  u8::zero_extended::<  u8>(1), 1);
110		assert_eq!(  u8::zero_extended::< u16>(1), 1);
111		assert_eq!(  u8::zero_extended::< u32>(1), 1);
112		assert_eq!(  u8::zero_extended::< u64>(1), 1);
113		assert_eq!(  u8::zero_extended::<u128>(1), 1);
114		assert_eq!( u16::zero_extended::< u16>(1), 1);
115		assert_eq!( u16::zero_extended::< u32>(1), 1);
116		assert_eq!( u16::zero_extended::< u64>(1), 1);
117		assert_eq!( u16::zero_extended::<u128>(1), 1);
118		assert_eq!( u32::zero_extended::< u32>(1), 1);
119		assert_eq!( u32::zero_extended::< u64>(1), 1);
120		assert_eq!( u32::zero_extended::<u128>(1), 1);
121		assert_eq!( u64::zero_extended::< u64>(1), 1);
122		assert_eq!( u64::zero_extended::<u128>(1), 1);
123		assert_eq!(u128::zero_extended::<u128>(1), 1);
124	}
125
126	#[test]
127	#[rustfmt::skip]
128	fn zero_extend_unsigned_big() {
129		
130		assert_eq!( u8::zero_extended::< u16>( u8::MAX),  u16::from( u8::MAX));
131		assert_eq!( u8::zero_extended::< u32>( u8::MAX),  u32::from( u8::MAX));
132		assert_eq!( u8::zero_extended::< u64>( u8::MAX),  u64::from( u8::MAX));
133		assert_eq!( u8::zero_extended::<u128>( u8::MAX), u128::from( u8::MAX));
134		assert_eq!(u16::zero_extended::< u32>(u16::MAX),  u32::from(u16::MAX));
135		assert_eq!(u16::zero_extended::< u64>(u16::MAX),  u64::from(u16::MAX));
136		assert_eq!(u16::zero_extended::<u128>(u16::MAX), u128::from(u16::MAX));
137		assert_eq!(u32::zero_extended::< u64>(u32::MAX),  u64::from(u32::MAX));
138		assert_eq!(u32::zero_extended::<u128>(u32::MAX), u128::from(u32::MAX));
139		assert_eq!(u64::zero_extended::<u128>(u64::MAX), u128::from(u64::MAX));
140		
141		assert_eq!(  u8::zero_extended::<  u8>(  u8::MAX),   u8::MAX);
142		assert_eq!( u16::zero_extended::< u16>( u16::MAX),  u16::MAX);
143		assert_eq!( u32::zero_extended::< u32>( u32::MAX),  u32::MAX);
144		assert_eq!( u64::zero_extended::< u64>( u64::MAX),  u64::MAX);
145		assert_eq!(u128::zero_extended::<u128>(u128::MAX), u128::MAX);
146	}
147
148	#[test]
149	#[rustfmt::skip]
150	fn zero_extend_signed_positive() {
151		assert_eq!(  i8::zero_extended::<  i8>(1), 1);
152		assert_eq!(  i8::zero_extended::< i16>(1), 1);
153		assert_eq!(  i8::zero_extended::< i32>(1), 1);
154		assert_eq!(  i8::zero_extended::< i64>(1), 1);
155		assert_eq!(  i8::zero_extended::<i128>(1), 1);
156		assert_eq!( i16::zero_extended::< i16>(1), 1);
157		assert_eq!( i16::zero_extended::< i32>(1), 1);
158		assert_eq!( i16::zero_extended::< i64>(1), 1);
159		assert_eq!( i16::zero_extended::<i128>(1), 1);
160		assert_eq!( i32::zero_extended::< i32>(1), 1);
161		assert_eq!( i32::zero_extended::< i64>(1), 1);
162		assert_eq!( i32::zero_extended::<i128>(1), 1);
163		assert_eq!( i64::zero_extended::< i64>(1), 1);
164		assert_eq!( i64::zero_extended::<i128>(1), 1);
165		assert_eq!(i128::zero_extended::<i128>(1), 1);
166	}
167
168	#[test]
169	#[rustfmt::skip]
170	fn zero_extend_signed_negative() {
171		assert_eq!( i8::zero_extended::< i16>(-1),  i16::from(u8  ::MAX));
172		assert_eq!( i8::zero_extended::< i32>(-1),  i32::from(u8  ::MAX));
173		assert_eq!( i8::zero_extended::< i64>(-1),  i64::from(u8  ::MAX));
174		assert_eq!( i8::zero_extended::<i128>(-1), i128::from(u8  ::MAX));
175		assert_eq!(i16::zero_extended::< i32>(-1),  i32::from(u16 ::MAX));
176		assert_eq!(i16::zero_extended::< i64>(-1),  i64::from(u16 ::MAX));
177		assert_eq!(i16::zero_extended::<i128>(-1), i128::from(u16 ::MAX));
178		assert_eq!(i32::zero_extended::< i64>(-1),  i64::from(u32 ::MAX));
179		assert_eq!(i32::zero_extended::<i128>(-1), i128::from(u32 ::MAX));
180		assert_eq!(i64::zero_extended::<i128>(-1), i128::from(u64 ::MAX));
181		
182		assert_eq!(  i8::zero_extended::<  i8>(-1), -1);
183		assert_eq!( i16::zero_extended::< i16>(-1), -1);
184		assert_eq!( i32::zero_extended::< i32>(-1), -1);
185		assert_eq!( i64::zero_extended::< i64>(-1), -1);
186		assert_eq!(i128::zero_extended::<i128>(-1), -1);
187	}
188}