use core::mem::MaybeUninit;
pub unsafe trait Packable: Copy {
const MAX_BITS: usize = core::mem::size_of::<Self>() * 8;
fn pack(self) -> usize;
unsafe fn unpack(value: usize) -> Self;
}
macro_rules! impl_int {
($($t:ty),*) => {
$(
unsafe impl Packable for $t {
fn pack(self) -> usize {
self as usize
}
unsafe fn unpack(value: usize) -> Self {
value as $t
}
}
)*
};
}
impl_int!(u8, u16, u32, usize, i8, i16, i32, isize);
#[cfg(target_pointer_width = "64")]
impl_int!(u64, i64);
unsafe impl Packable for () {
fn pack(self) -> usize {
0
}
unsafe fn unpack(_: usize) -> Self {}
}
unsafe impl Packable for bool {
const MAX_BITS: usize = 1;
fn pack(self) -> usize {
usize::from(self)
}
unsafe fn unpack(value: usize) -> Self {
value != 0
}
}
macro_rules! internal_tuple_unpack {
($value:ident, $t:ident) => {
let $t = {
let mask = (1 << <$t as Packable>::MAX_BITS) - 1;
let $t = <$t as Packable>::unpack($value & mask);
$value >>= <$t as Packable>::MAX_BITS;
$t
};
};
($value:ident, $t:ident, $($rest:ident),*) => {
internal_tuple_unpack!($value, $($rest),*);
let $t = {
let mask = (1 << <$t as Packable>::MAX_BITS) - 1;
let $t = <$t as Packable>::unpack($value & mask);
$value >>= <$t as Packable>::MAX_BITS;
$t
};
};
}
macro_rules! impl_tuple {
($($t:ident),*) => {
#[allow(non_snake_case)]
unsafe impl<$($t: Packable),*> Packable for ($($t,)*) {
const MAX_BITS: usize = 0 $(+ <$t as Packable>::MAX_BITS)*;
fn pack(self) -> usize {
let ($($t,)*) = self;
let mut value = 0;
$(
value <<= <$t as Packable>::MAX_BITS;
value |= <$t as Packable>::pack($t);
)*
value
}
#[allow(unused_assignments)]
unsafe fn unpack(value: usize) -> Self {
let mut value = value;
internal_tuple_unpack!(value, $($t),*);
($($t,)*)
}
}
};
}
impl_tuple!(T0);
impl_tuple!(T0, T1);
impl_tuple!(T0, T1, T2);
impl_tuple!(T0, T1, T2, T3);
impl_tuple!(T0, T1, T2, T3, T4);
impl_tuple!(T0, T1, T2, T3, T4, T5);
impl_tuple!(T0, T1, T2, T3, T4, T5, T6);
impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
unsafe impl<T: Packable, const N: usize> Packable for [T; N] {
const MAX_BITS: usize = T::MAX_BITS * N;
fn pack(self) -> usize {
IntoIterator::into_iter(&self).fold(0, |acc, x| acc << T::MAX_BITS | x.pack())
}
unsafe fn unpack(mut value: usize) -> Self {
let mask: usize = (1 << T::MAX_BITS) - 1;
let mut array: MaybeUninit<[T; N]> = MaybeUninit::uninit();
for i in 1..=N {
let ptr = array.as_mut_ptr().cast::<T>();
ptr.add(N - i).write(T::unpack(value & mask));
value >>= T::MAX_BITS;
}
array.assume_init()
}
}
unsafe impl<T: Packable> Packable for Option<T> {
const MAX_BITS: usize = T::MAX_BITS + 1;
fn pack(self) -> usize {
self.map_or(0, |x| x.pack() << 1 | 1)
}
unsafe fn unpack(value: usize) -> Self {
if value == 0 {
None
} else {
Some(T::unpack(value >> 1))
}
}
}
unsafe impl<T: Packable, E: Packable> Packable for Result<T, E> {
const MAX_BITS: usize = [T::MAX_BITS, E::MAX_BITS][(T::MAX_BITS < E::MAX_BITS) as usize] + 1;
fn pack(self) -> usize {
match self {
Ok(x) => x.pack() << 1 | 1,
Err(e) => e.pack() << 1,
}
}
unsafe fn unpack(value: usize) -> Self {
if value & 1 == 0 {
Err(E::unpack(value >> 1))
} else {
Ok(T::unpack(value >> 1))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn int() {
assert_eq!(<u8 as Packable>::MAX_BITS, 8);
assert_eq!(<u16 as Packable>::MAX_BITS, 16);
assert_eq!(<u32 as Packable>::MAX_BITS, 32);
#[cfg(target_pointer_width = "64")]
assert_eq!(<u64 as Packable>::MAX_BITS, 64);
assert_eq!(0u8.pack(), 0);
assert_eq!(255u8.pack(), 255);
assert_eq!(0u16.pack(), 0);
assert_eq!(65535u16.pack(), 65535);
assert_eq!(0u32.pack(), 0);
assert_eq!(4_294_967_295_u32.pack(), 4_294_967_295);
#[cfg(target_pointer_width = "64")]
assert_eq!(0u64.pack(), 0);
#[cfg(target_pointer_width = "64")]
assert_eq!(
18_446_744_073_709_551_615_u64.pack(),
18_446_744_073_709_551_615
);
unsafe {
assert_eq!(0u8, Packable::unpack(0));
assert_eq!(255u8, Packable::unpack(255));
assert_eq!(0u16, Packable::unpack(0));
assert_eq!(65535u16, Packable::unpack(65535));
assert_eq!(0u32, Packable::unpack(0));
assert_eq!(4_294_967_295_u32, Packable::unpack(4_294_967_295));
#[cfg(target_pointer_width = "64")]
assert_eq!(0u64, Packable::unpack(0));
#[cfg(target_pointer_width = "64")]
assert_eq!(
18_446_744_073_709_551_615_u64,
Packable::unpack(18_446_744_073_709_551_615)
);
}
}
#[test]
fn zst() {
assert_eq!(<() as Packable>::MAX_BITS, 0);
assert_eq!(().pack(), 0);
}
#[test]
fn boolean() {
assert_eq!(<bool as Packable>::MAX_BITS, 1);
assert_eq!(false.pack(), 0);
assert_eq!(true.pack(), 1);
unsafe {
assert!(!<bool as Packable>::unpack(0));
assert!(<bool as Packable>::unpack(1));
}
}
#[test]
fn tuple() {
assert_eq!(<(bool, u8) as Packable>::MAX_BITS, 9);
assert_eq!((false, 0u8).pack(), 0x000);
assert_eq!((false, 255u8).pack(), 0x0ff);
assert_eq!((true, 0u8).pack(), 0x100);
assert_eq!((true, 255u8).pack(), 0x1ff);
assert_eq!((true, false).pack(), 0b10);
unsafe {
assert_eq!((false, 0u8), Packable::unpack(0x000));
assert_eq!((false, 255u8), Packable::unpack(0x0ff));
assert_eq!((true, 0u8), Packable::unpack(0x100));
assert_eq!((true, 255u8), Packable::unpack(0x1ff));
assert_eq!((true, false), Packable::unpack(0b10));
}
}
#[test]
fn array() {
assert_eq!(<[u8; 3] as Packable>::MAX_BITS, 24);
assert_eq!(<[u8; 4] as Packable>::MAX_BITS, 32);
assert_eq!(<[bool; 4] as Packable>::MAX_BITS, 4);
assert_eq!([0u8, 0, 0].pack(), 0x0000_0000);
assert_eq!([0u8, 0, 255].pack(), 0x0000_00ff);
assert_eq!([0u8, 255, 0].pack(), 0x0000_ff00);
assert_eq!([0u8, 255, 255].pack(), 0x0000_ffff);
assert_eq!([255u8, 0, 0].pack(), 0x00ff_0000);
assert_eq!([255u8, 0, 255].pack(), 0x00ff_00ff);
assert_eq!([255u8, 255, 0].pack(), 0x00ff_ff00);
assert_eq!([255u8, 255, 255].pack(), 0x00ff_ffff);
assert_eq!([true, false].pack(), 0b10);
unsafe {
assert_eq!([0u8, 0, 0], <[u8; 3] as Packable>::unpack(0x0000_0000));
assert_eq!([0u8, 0, 255], <[u8; 3] as Packable>::unpack(0x0000_00ff));
assert_eq!([0u8, 255, 0], <[u8; 3] as Packable>::unpack(0x0000_ff00));
assert_eq!([0u8, 255, 255], <[u8; 3] as Packable>::unpack(0x0000_ffff));
assert_eq!([255u8, 0, 0], <[u8; 3] as Packable>::unpack(0x00ff_0000));
assert_eq!([255u8, 0, 255], <[u8; 3] as Packable>::unpack(0x00ff_00ff));
assert_eq!([255u8, 255, 0], <[u8; 3] as Packable>::unpack(0x00ff_ff00));
assert_eq!(
[255u8, 255, 255],
<[u8; 3] as Packable>::unpack(0x00ff_ffff)
);
assert_eq!([true, false], <[bool; 2] as Packable>::unpack(0b10));
}
}
#[test]
fn option() {
assert_eq!(<Option<u8> as Packable>::MAX_BITS, 9);
assert_eq!(<Option<bool> as Packable>::MAX_BITS, 2);
assert_eq!(None::<u8>.pack(), 0x00);
assert_eq!(Some(0u8).pack(), 0x01);
assert_eq!(Some(255u8).pack(), 0x1FF);
unsafe {
assert_eq!(None::<u8>, Packable::unpack(0x00));
assert_eq!(Some(0u8), Packable::unpack(0x01));
assert_eq!(Some(255u8), Packable::unpack(0x1FF));
}
}
#[test]
fn result() {
assert_eq!(<Result<u8, u8> as Packable>::MAX_BITS, 9);
assert_eq!(<Result<bool, bool> as Packable>::MAX_BITS, 2);
assert_eq!(Err::<u8, u8>(0u8).pack(), 0x00);
assert_eq!(Ok::<u8, u8>(0u8).pack(), 0x01);
assert_eq!(Err::<u8, u8>(255u8).pack(), 0x1FE);
assert_eq!(Ok::<u8, u8>(255u8).pack(), 0x1FF);
unsafe {
assert_eq!(Err::<u8, u8>(0u8), Packable::unpack(0x00));
assert_eq!(Ok::<u8, u8>(0u8), Packable::unpack(0x01));
assert_eq!(Err::<u8, u8>(255u8), Packable::unpack(0x1FE));
assert_eq!(Ok::<u8, u8>(255u8), Packable::unpack(0x1FF));
}
}
}