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}