1#[repr(transparent)]
2#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
3pub struct PackedInt {
4    inner: u16,
5}
6
7macro_rules! impl_from_type {
8    ($type:ty, $name:ident) => {
9        pub fn $name(mut value: $type) -> Self {
10            let mut prefix = 0u16;
11
12            while value > 0x1ff {
13                prefix += 1;
14                value += 1;
15                value >>= 1;
16            }
17
18            return Self {
19                inner: (prefix << 8) + (value as u16),
20            };
21        }
22    };
23}
24
25macro_rules! impl_into_type {
26    ($type:ty, $name:ident) => {
27        pub fn $name(&self) -> $type {
28            let prefix = (self.inner >> 8) as $type;
29            let suffix = (self.inner & 0xff) as $type;
30
31            if prefix == 0 {
32                suffix
33            } else if 7 + prefix >= <$type>::BITS as $type {
34                <$type>::MAX
35            } else {
36                (1 << (7 + prefix)) | (suffix << (prefix - 1))
37            }
38        }
39    };
40}
41
42macro_rules! impl_traits {
43    ($type:ty, $from:ident, $into:ident) => {
44        impl From<$type> for PackedInt {
45            fn from(value: $type) -> Self {
46                Self::$from(value)
47            }
48        }
49
50        impl From<PackedInt> for $type {
51            fn from(packed: PackedInt) -> $type {
52                packed.$into()
53            }
54        }
55    };
56}
57
58impl PackedInt {
59    pub fn from_12_bits(bits: &[u8; 2]) -> Self {
60        Self {
61            inner: ((bits[1] as u16) << 4) | (bits[0] as u16),
62        }
63    }
64
65    pub fn to_12_bits(&self) -> [u8; 2] {
66        [self.inner as u8, 0xF0 & (self.inner >> 4) as u8]
67    }
68
69    pub fn from_16_bits(bits: &[u8; 2]) -> Self {
70        Self {
71            inner: u16::from_le_bytes(*bits),
72        }
73    }
74
75    pub fn to_16_bits(&self) -> [u8; 2] {
76        self.inner.to_le_bytes()
77    }
78
79    pub fn from_inner_u16(inner: u16) -> Self {
80        Self { inner }
81    }
82
83    pub fn to_inner_u16(&self) -> u16 {
84        self.inner
85    }
86
87    impl_from_type!(usize, from_usize);
88    impl_from_type!(u128, from_u128);
89    impl_from_type!(u64, from_u64);
90    impl_from_type!(u32, from_u32);
91    impl_from_type!(u16, from_u16);
92
93    impl_into_type!(usize, to_usize);
94    impl_into_type!(u128, to_u128);
95    impl_into_type!(u64, to_u64);
96    impl_into_type!(u32, to_u32);
97    impl_into_type!(u16, to_u16);
98}
99
100impl_traits!(usize, from_usize, to_usize);
101impl_traits!(u128, from_u128, to_u128);
102impl_traits!(u64, from_u64, to_u64);
103impl_traits!(u32, from_u32, to_u32);
104
105#[cfg(test)]
106mod tests {
107    use crate::*;
108
109    #[test]
110    fn no_panic() {
111        for index in 0..16 {
112            let original = 1 << index;
113            let unpacked = PackedInt::from_u16(original).to_u16();
114
115            assert_eq!(
116                unpacked, original,
117                "unpacked u16 {original} unpacked to {unpacked}"
118            );
119        }
120
121        for index in 0..32 {
122            let original = 1 << index;
123            let unpacked = PackedInt::from_u32(original).to_u32();
124
125            assert_eq!(
126                unpacked, original,
127                "unpacked u32 {original} unpacked to {unpacked}"
128            );
129        }
130
131        for index in 0..64 {
132            let original = 1 << index;
133            let unpacked = PackedInt::from_u64(original).to_u64();
134
135            assert_eq!(
136                unpacked, original,
137                "unpacked u64 {original} unpacked to {unpacked}"
138            );
139        }
140
141        for index in 0..128 {
142            let original = 1 << index;
143            let unpacked = PackedInt::from_u128(original).to_u128();
144
145            assert_eq!(
146                unpacked, original,
147                "unpacked u128 {original} unpacked to {unpacked}"
148            );
149        }
150
151        for index in 0..64 {
152            let original = 1 << index;
153            let unpacked = PackedInt::from_usize(original).to_usize();
154
155            assert_eq!(
156                unpacked, original,
157                "unpacked usize {original} unpacked to {unpacked}"
158            );
159        }
160
161        for index in 0..128 {
162            let original = 1 << index;
163            let packed = PackedInt::from_u128(original);
164
165            let u16 = packed.to_u16();
166            let u32 = packed.to_u32();
167            let u64 = packed.to_u64();
168            let u128 = packed.to_u128();
169            let usize = packed.to_usize();
170
171            if u16::MAX as u128 > original {
172                assert_eq!(u16 as u128, original)
173            } else {
174                assert_eq!(u16, u16::MAX)
175            };
176
177            if u32::MAX as u128 > original {
178                assert_eq!(u32 as u128, original)
179            } else {
180                assert_eq!(u32, u32::MAX)
181            };
182
183            if u64::MAX as u128 > original {
184                assert_eq!(u64 as u128, original)
185            } else {
186                assert_eq!(u64, u64::MAX)
187            };
188
189            if u128::MAX as u128 > original {
190                assert_eq!(u128 as u128, original)
191            } else {
192                assert_eq!(u128, u128::MAX)
193            };
194
195            if usize::MAX as u128 > original {
196                assert_eq!(usize as u128, original)
197            } else {
198                assert_eq!(usize, usize::MAX)
199            };
200        }
201    }
202
203    #[test]
204    pub fn order() {
205        for i in 0..0x1000 {
206            assert!(
207                PackedInt::from_inner_u16(i + 1).to_u128() > PackedInt::from_inner_u16(i).to_u128()
208            );
209        }
210    }
211
212    #[test]
213    pub fn test_int_packing() {
214        let check = |value| {
215            let packed = PackedInt::from_u64(value);
216            let unpacked = packed.to_u64();
217            assert_eq!(value, unpacked);
218        };
219
220        for value in 0..512 {
221            for shift in 0..64 {
222                check(value << shift)
223            }
224        }
225
226        for inner in 0..0x3900 {
227            check((PackedInt { inner }).to_u64())
228        }
229    }
230}