1use std::fmt;
2
3#[derive(Clone, Copy, PartialEq)]
4pub(crate) struct Pack {
5 mask: usize,
6 shift: u32,
7}
8
9impl Pack {
10 /// Value is packed in the `width` least-significant bits.
11 pub(crate) const fn least_significant(width: u32) -> Pack {
12 let mask = mask_for(width);
13
14 Pack { mask, shift: 0 }
15 }
16
17 /// Value is packed in the `width` more-significant bits.
18 pub(crate) const fn then(&self, width: u32) -> Pack {
19 let shift = pointer_width() - self.mask.leading_zeros();
20 let mask = mask_for(width) << shift;
21
22 Pack { mask, shift }
23 }
24
25 /// Width, in bits, dedicated to storing the value.
26 pub(crate) const fn width(&self) -> u32 {
27 pointer_width() - (self.mask >> self.shift).leading_zeros()
28 }
29
30 /// Max representable value.
31 pub(crate) const fn max_value(&self) -> usize {
32 (1 << self.width()) - 1
33 }
34
35 pub(crate) fn pack(&self, value: usize, base: usize) -> usize {
36 assert!(value <= self.max_value());
37 (base & !self.mask) | (value << self.shift)
38 }
39
40 /// Packs the value with `base`, losing any bits of `value` that fit.
41 ///
42 /// If `value` is larger than the max value that can be represented by the
43 /// allotted width, the most significant bits are truncated.
44 pub(crate) fn pack_lossy(&self, value: usize, base: usize) -> usize {
45 self.pack(value & self.max_value(), base)
46 }
47
48 pub(crate) fn unpack(&self, src: usize) -> usize {
49 unpack(src, self.mask, self.shift)
50 }
51}
52
53impl fmt::Debug for Pack {
54 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(
56 fmt,
57 "Pack {{ mask: {:b}, shift: {} }}",
58 self.mask, self.shift
59 )
60 }
61}
62
63/// Returns the width of a pointer in bits.
64pub(crate) const fn pointer_width() -> u32 {
65 std::mem::size_of::<usize>() as u32 * 8
66}
67
68/// Returns a `usize` with the right-most `n` bits set.
69pub(crate) const fn mask_for(n: u32) -> usize {
70 let shift: usize = 1usize.wrapping_shl(n - 1);
71 shift | (shift - 1)
72}
73
74/// Unpacks a value using a mask & shift.
75pub(crate) const fn unpack(src: usize, mask: usize, shift: u32) -> usize {
76 (src & mask) >> shift
77}
78