int_conv/
split.rs

1//! Integer splitting
2//!
3//! This module provides ways to split bigger integers
4//! into smaller integers and then join them back together.
5
6// Imports
7use super::{Truncate, ZeroExtend};
8use core::{
9	mem,
10	ops::{Shl, Shr},
11};
12
13/// Splits an integer into it's low and high part
14pub trait Split: Sized {
15	/// Output type for higher part
16	type Hi;
17
18	/// Output type for lower part
19	type Lo;
20
21	/// Returns the high part of this integer
22	fn hi(self) -> Self::Hi;
23
24	/// Returns the low part of this integer
25	fn lo(self) -> Self::Lo;
26
27	/// Returns the low and high part of this integer
28	fn lo_hi(self) -> (Self::Lo, Self::Hi);
29}
30
31/// Joins two integers into a larger one.
32pub trait Join: Split {
33	/// Joins two parts of an integer
34	fn join(lo: <Self as Split>::Lo, hi: <Self as Split>::Lo) -> Self;
35}
36
37/// Macro to help implement `Split` / `Join`
38///
39/// Note: We don't currently implement `Split` for references, as
40///       we'd have `Hi` and `Lo` be references as well, but we
41///       cannot express this relationship without `GAT`s.
42///       As for join, we don't implement it on references, as
43///       it does not make sense for the function.
44macro_rules! impl_split_join {
45	($T:ty => $Hi:ty : $Lo:ty) => {
46		// Make sure that `T` is made up of `Lo` and `Hi`
47		::static_assertions::assert_eq_size!($T, ($Lo, $Hi));
48
49		impl Split for $T {
50			type Hi = $Hi;
51			type Lo = $Lo;
52
53			#[inline]
54			fn lo(self) -> Self::Lo {
55				<Self as Truncate<Self::Lo>>::truncate(self)
56			}
57
58			#[inline]
59			fn hi(self) -> Self::Hi {
60				<Self as Truncate<Self::Lo>>::truncate(self.shr(8 * mem::size_of::<Self::Lo>()))
61			}
62
63			#[inline]
64			fn lo_hi(self) -> (Self::Lo, Self::Hi) {
65				let lo = self.lo();
66				let hi = self.hi();
67				(lo, hi)
68			}
69		}
70
71		impl Join for $T {
72			#[inline]
73			fn join(lo: <Self as Split>::Lo, hi: <Self as Split>::Lo) -> Self {
74				<$Hi as ZeroExtend<$T>>::zero_extend(hi).shl(8 * mem::size_of::<Self::Lo>()) | <$Lo as ZeroExtend<$T>>::zero_extend(lo)
75			}
76		}
77	};
78}
79
80// Unsigned
81impl_split_join! { u128 => u64 : u64 }
82impl_split_join! { u64  => u32 : u32 }
83impl_split_join! { u32  => u16 : u16 }
84impl_split_join! { u16  => u8  : u8  }
85
86// Signed
87// TODO: Confirm these, should they even exist? Should `Lo` be unsigned?
88//impl_split_join! { i128 => i64 : i64 }
89//impl_split_join! { i64  => i32 : i32 }
90//impl_split_join! { i32  => i16 : i16 }
91//impl_split_join! { i16  => i8  : i8  }
92
93// Check that they all implement `Split` / `Join`
94//static_assertions::assert_impl_all! { i16  : Split, Join }
95//static_assertions::assert_impl_all! { i32  : Split, Join }
96//static_assertions::assert_impl_all! { i64  : Split, Join }
97//static_assertions::assert_impl_all! { i128 : Split, Join }
98static_assertions::assert_impl_all! { u16  : Split, Join }
99static_assertions::assert_impl_all! { u32  : Split, Join }
100static_assertions::assert_impl_all! { u64  : Split, Join }
101static_assertions::assert_impl_all! { u128 : Split, Join }
102
103// Check that all associated types are correct
104//static_assertions::assert_type_eq_all! { <i16   as Split>::Lo, <i16   as Split>::Hi, i8  }
105//static_assertions::assert_type_eq_all! { <i32   as Split>::Lo, <i32   as Split>::Hi, i16 }
106//static_assertions::assert_type_eq_all! { <i64   as Split>::Lo, <i64   as Split>::Hi, i32 }
107//static_assertions::assert_type_eq_all! { <i128  as Split>::Lo, <i128  as Split>::Hi, i64 }
108static_assertions::assert_type_eq_all! { <u16   as Split>::Lo, <u16   as Split>::Hi, u8  }
109static_assertions::assert_type_eq_all! { <u32   as Split>::Lo, <u32   as Split>::Hi, u16 }
110static_assertions::assert_type_eq_all! { <u64   as Split>::Lo, <u64   as Split>::Hi, u32 }
111static_assertions::assert_type_eq_all! { <u128  as Split>::Lo, <u128  as Split>::Hi, u64 }
112
113#[cfg(test)]
114mod tests {
115	use super::*;
116
117	#[test]
118	#[rustfmt::skip]
119	fn split_lo() {
120		assert_eq!(u128::lo(u128::MAX), u64::MAX);
121		assert_eq!( u64::lo( u64::MAX), u32::MAX);
122		assert_eq!( u32::lo( u32::MAX), u16::MAX);
123		assert_eq!( u16::lo( u16::MAX),  u8::MAX);
124	}
125
126	#[test]
127	#[rustfmt::skip]
128	fn split_lo_half() {
129		assert_eq!(u128::lo(u128::from(u64::MAX)), u64::MAX);
130		assert_eq!( u64::lo( u64::from(u32::MAX)), u32::MAX);
131		assert_eq!( u32::lo( u32::from(u16::MAX)), u16::MAX);
132		assert_eq!( u16::lo( u16::from( u8::MAX)),  u8::MAX);
133	}
134
135	#[test]
136	#[rustfmt::skip]
137	fn split_hi() {
138		assert_eq!(u128::hi(u128::MAX), u64::MAX);
139		assert_eq!( u64::hi( u64::MAX), u32::MAX);
140		assert_eq!( u32::hi( u32::MAX), u16::MAX);
141		assert_eq!( u16::hi( u16::MAX),  u8::MAX);
142	}
143
144	#[test]
145	#[rustfmt::skip]
146	fn split_hi_half() {
147		assert_eq!(u128::hi(u128::from(u64::MAX)), 0);
148		assert_eq!( u64::hi( u64::from(u32::MAX)), 0);
149		assert_eq!( u32::hi( u32::from(u16::MAX)), 0);
150		assert_eq!( u16::hi( u16::from( u8::MAX)), 0);
151	}
152
153	#[test]
154	#[rustfmt::skip]
155	fn split_lo_hi() {
156		assert_eq!(u128::lo_hi(u128::MAX), (u128::lo(u128::MAX), u128::hi(u128::MAX)));
157		assert_eq!( u64::lo_hi( u64::MAX), ( u64::lo( u64::MAX),  u64::hi( u64::MAX)));
158		assert_eq!( u32::lo_hi( u32::MAX), ( u32::lo( u32::MAX),  u32::hi( u32::MAX)));
159		assert_eq!( u16::lo_hi( u16::MAX), ( u16::lo( u16::MAX),  u16::hi( u16::MAX)));
160	}
161
162	#[test]
163	#[rustfmt::skip]
164	fn split_lo_hi_half() {
165		assert_eq!(u128::lo_hi(u128::from(u64::MAX)), (u128::lo(u128::from(u64::MAX)), u128::hi(u128::from(u64::MAX))));
166		assert_eq!( u64::lo_hi( u64::from(u32::MAX)), ( u64::lo( u64::from(u32::MAX)),  u64::hi( u64::from(u32::MAX))));
167		assert_eq!( u32::lo_hi( u32::from(u16::MAX)), ( u32::lo( u32::from(u16::MAX)),  u32::hi( u32::from(u16::MAX))));
168		assert_eq!( u16::lo_hi( u16::from( u8::MAX)), ( u16::lo( u16::from( u8::MAX)),  u16::hi( u16::from( u8::MAX))));
169	}
170}