Skip to main content

wasm4pm_types/
mask.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
2#[repr(transparent)]
3pub struct FieldMask(pub u64);
4
5impl FieldMask {
6    #[inline]
7    #[must_use]
8    pub const fn empty() -> Self {
9        Self(0)
10    }
11    #[inline]
12    #[must_use]
13    pub const fn is_empty(&self) -> bool {
14        self.0 == 0
15    }
16    #[inline]
17    #[must_use]
18    pub const fn with_bit(self, bit: FieldBit) -> Self {
19        // PR #71 MCC class: the literal `1` would be `i32` here, which silently
20        // overflows for bit.get() >= 31. Use `1u64` to keep the shift in the
21        // mask's actual storage type.
22        Self(self.0 | (1u64 << bit.get()))
23    }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
27#[repr(transparent)]
28pub struct CompletedMask(pub u64);
29
30impl CompletedMask {
31    #[inline]
32    #[must_use]
33    pub const fn empty() -> Self {
34        Self(0)
35    }
36    #[inline]
37    #[must_use]
38    pub const fn is_empty(&self) -> bool {
39        self.0 == 0
40    }
41    #[inline]
42    #[must_use]
43    pub const fn with_bit(self, bit: FieldBit) -> Self {
44        // PR #71 MCC class: see FieldMask::with_bit comment.
45        Self(self.0 | (1u64 << bit.get()))
46    }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
50#[repr(transparent)]
51pub struct FieldBit(u8);
52
53impl FieldBit {
54    /// # Errors
55    /// Returns `MaskError` if value > 63.
56    #[inline]
57    pub const fn new_checked(value: u8) -> Result<Self, MaskError> {
58        if value < 64 {
59            Ok(Self(value))
60        } else {
61            Err(MaskError::OutOfRange)
62        }
63    }
64    #[inline]
65    #[must_use]
66    pub const fn new_unchecked(value: u8) -> Self {
67        Self(value)
68    }
69    #[inline]
70    #[must_use]
71    pub const fn get(self) -> u8 {
72        self.0
73    }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum MaskError {
78    OutOfRange,
79}
80
81impl core::fmt::Display for MaskError {
82    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83        match self {
84            Self::OutOfRange => write!(f, "FieldBit must be in range [0, 63]"),
85        }
86    }
87}
88
89impl std::error::Error for MaskError {}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    /// Rank-1 (mathematical theorem): for every legal bit position b in [0,63],
96    /// `with_bit(b)` on an empty mask must equal exactly `1u64 << b`.
97    /// Regression for PR #71 MCC class — the original literal `1` was `i32`
98    /// and overflowed for b >= 31, silently producing the wrong mask value.
99    #[test]
100    fn with_bit_matches_pow2_for_every_bit() {
101        for b in 0u8..64u8 {
102            let bit = FieldBit::new_checked(b).unwrap();
103            let m = FieldMask::empty().with_bit(bit);
104            assert_eq!(m.0, 1u64 << b, "FieldMask bit {b} mismatch");
105            let cm = CompletedMask::empty().with_bit(bit);
106            assert_eq!(cm.0, 1u64 << b, "CompletedMask bit {b} mismatch");
107        }
108    }
109
110    /// Rank-2 (domain contract): FieldBit::new_checked rejects 64..=255 with
111    /// MaskError::OutOfRange, and accepts 0..=63.
112    #[test]
113    fn field_bit_range_contract() {
114        for v in 0u8..64u8 {
115            assert_eq!(FieldBit::new_checked(v).unwrap().get(), v);
116        }
117        for v in 64u8..=200u8 {
118            assert_eq!(FieldBit::new_checked(v), Err(MaskError::OutOfRange));
119        }
120    }
121}