use super::{Truncate, ZeroExtend};
use core::{
mem,
ops::{Shl, Shr},
};
pub trait Split: Sized {
type Hi;
type Lo;
fn hi(self) -> Self::Hi;
fn lo(self) -> Self::Lo;
fn lo_hi(self) -> (Self::Lo, Self::Hi);
}
pub trait Join: Split {
fn join(lo: <Self as Split>::Lo, hi: <Self as Split>::Lo) -> Self;
}
macro_rules! impl_split_join {
($T:ty => $Hi:ty : $Lo:ty) => {
::static_assertions::assert_eq_size!($T, ($Lo, $Hi));
impl Split for $T {
type Hi = $Hi;
type Lo = $Lo;
#[inline]
fn lo(self) -> Self::Lo {
<Self as Truncate<Self::Lo>>::truncate(self)
}
#[inline]
fn hi(self) -> Self::Hi {
<Self as Truncate<Self::Lo>>::truncate(self.shr(8 * mem::size_of::<Self::Lo>()))
}
#[inline]
fn lo_hi(self) -> (Self::Lo, Self::Hi) {
let lo = self.lo();
let hi = self.hi();
(lo, hi)
}
}
impl Join for $T {
#[inline]
fn join(lo: <Self as Split>::Lo, hi: <Self as Split>::Lo) -> Self {
<$Hi as ZeroExtend<$T>>::zero_extend(hi).shl(8 * mem::size_of::<Self::Lo>()) | <$Lo as ZeroExtend<$T>>::zero_extend(lo)
}
}
};
}
impl_split_join! { u128 => u64 : u64 }
impl_split_join! { u64 => u32 : u32 }
impl_split_join! { u32 => u16 : u16 }
impl_split_join! { u16 => u8 : u8 }
static_assertions::assert_impl_all! { u16 : Split, Join }
static_assertions::assert_impl_all! { u32 : Split, Join }
static_assertions::assert_impl_all! { u64 : Split, Join }
static_assertions::assert_impl_all! { u128 : Split, Join }
static_assertions::assert_type_eq_all! { <u16 as Split>::Lo, <u16 as Split>::Hi, u8 }
static_assertions::assert_type_eq_all! { <u32 as Split>::Lo, <u32 as Split>::Hi, u16 }
static_assertions::assert_type_eq_all! { <u64 as Split>::Lo, <u64 as Split>::Hi, u32 }
static_assertions::assert_type_eq_all! { <u128 as Split>::Lo, <u128 as Split>::Hi, u64 }
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[rustfmt::skip]
fn split_lo() {
assert_eq!(u128::lo(u128::MAX), u64::MAX);
assert_eq!( u64::lo( u64::MAX), u32::MAX);
assert_eq!( u32::lo( u32::MAX), u16::MAX);
assert_eq!( u16::lo( u16::MAX), u8::MAX);
}
#[test]
#[rustfmt::skip]
fn split_lo_half() {
assert_eq!(u128::lo(u128::from(u64::MAX)), u64::MAX);
assert_eq!( u64::lo( u64::from(u32::MAX)), u32::MAX);
assert_eq!( u32::lo( u32::from(u16::MAX)), u16::MAX);
assert_eq!( u16::lo( u16::from( u8::MAX)), u8::MAX);
}
#[test]
#[rustfmt::skip]
fn split_hi() {
assert_eq!(u128::hi(u128::MAX), u64::MAX);
assert_eq!( u64::hi( u64::MAX), u32::MAX);
assert_eq!( u32::hi( u32::MAX), u16::MAX);
assert_eq!( u16::hi( u16::MAX), u8::MAX);
}
#[test]
#[rustfmt::skip]
fn split_hi_half() {
assert_eq!(u128::hi(u128::from(u64::MAX)), 0);
assert_eq!( u64::hi( u64::from(u32::MAX)), 0);
assert_eq!( u32::hi( u32::from(u16::MAX)), 0);
assert_eq!( u16::hi( u16::from( u8::MAX)), 0);
}
#[test]
#[rustfmt::skip]
fn split_lo_hi() {
assert_eq!(u128::lo_hi(u128::MAX), (u128::lo(u128::MAX), u128::hi(u128::MAX)));
assert_eq!( u64::lo_hi( u64::MAX), ( u64::lo( u64::MAX), u64::hi( u64::MAX)));
assert_eq!( u32::lo_hi( u32::MAX), ( u32::lo( u32::MAX), u32::hi( u32::MAX)));
assert_eq!( u16::lo_hi( u16::MAX), ( u16::lo( u16::MAX), u16::hi( u16::MAX)));
}
#[test]
#[rustfmt::skip]
fn split_lo_hi_half() {
assert_eq!(u128::lo_hi(u128::from(u64::MAX)), (u128::lo(u128::from(u64::MAX)), u128::hi(u128::from(u64::MAX))));
assert_eq!( u64::lo_hi( u64::from(u32::MAX)), ( u64::lo( u64::from(u32::MAX)), u64::hi( u64::from(u32::MAX))));
assert_eq!( u32::lo_hi( u32::from(u16::MAX)), ( u32::lo( u32::from(u16::MAX)), u32::hi( u32::from(u16::MAX))));
assert_eq!( u16::lo_hi( u16::from( u8::MAX)), ( u16::lo( u16::from( u8::MAX)), u16::hi( u16::from( u8::MAX))));
}
}