bytefield/
lib.rs

1//! A pure Rust, no-std implementation of [bit fields](https://en.wikipedia.org/wiki/Bit_field)
2#![forbid(clippy::exit, clippy::expect_used, clippy::panic, clippy::unwrap_used)]
3#![deny(clippy::unimplemented, clippy::unreachable)]
4#![warn(clippy::dbg_macro, clippy::todo, clippy::try_err)]
5#![warn(clippy::cargo, clippy::nursery, clippy::pedantic, missing_docs)]
6#![no_std]
7
8use core::marker::PhantomData;
9
10/// Allows a value to be represented as a bit flag
11pub trait Flag {
12    /// Returns the value's bit flag offset
13    ///
14    /// This is used as a shift offset, so it should not be greater than or equal to the total number of bits within the field
15    fn get_offset(&self) -> u8;
16}
17
18/// Allows a value to be used as an immutable bit field
19pub trait ConstField<T: Flag>: Sized {
20    /// The type stored within the bit field, or at least a type that can be converted to and from it
21    #[cfg(feature = "serde")]
22    type Inner: for<'de> serde::Deserialize<'de>;
23    /// The type stored within the bit field, or at least a type that can be converted to and from it
24    #[cfg(not(feature = "serde"))]
25    type Inner;
26
27    /// Creates a new empty bit field
28    fn new() -> Self;
29    /// Creates a new bit field containing the given value
30    fn new_with(value: Self::Inner) -> Self;
31
32    /// Returns the bit field's inner value
33    fn get_inner(&self) -> Self::Inner;
34
35    /// Returns `true` if the provided bit flag is stored within the bit field
36    fn contains(&self, flag: impl Into<T>) -> bool;
37    /// Returns `true` if all of the provided bit flags are stored within the bit field
38    fn contains_all(&self, flags: &[impl Into<T> + Clone]) -> bool {
39        flags.iter().all(|flag| self.contains(flag.clone()))
40    }
41    /// Returns `true` if any of the provided bit flags are stored within the bit field
42    fn contains_any(&self, flags: &[impl Into<T> + Clone]) -> bool {
43        flags.iter().any(|flag| self.contains(flag.clone()))
44    }
45}
46
47/// Allows a value to be used as a mutable bit field
48pub trait Field<T: Flag>: ConstField<T> {
49    /// Adds the given bit flag into the bit field
50    #[must_use]
51    fn insert(self, flag: impl Into<T>) -> Self;
52    /// Adds all of the given bit flags into the bit field
53    #[must_use]
54    fn insert_all(mut self, flags: &[impl Into<T> + Clone]) -> Self {
55        for flag in flags {
56            self = self.insert(flag.clone());
57        }
58
59        self
60    }
61
62    /// Removes the given bit flag from the bit field
63    #[must_use]
64    fn remove(self, flag: impl Into<T>) -> Self;
65    /// Removes all of the given bit flags into the bit field
66    #[must_use]
67    fn remove_all(mut self, flags: &[impl Into<T> + Clone]) -> Self {
68        for flag in flags {
69            self = self.remove(flag.clone());
70        }
71
72        self
73    }
74
75    /// Inverts the flags within the bit field
76    #[must_use]
77    fn not(self) -> Self;
78}
79
80/// Generates an enum of the given flags and their offsets
81#[macro_export]
82macro_rules! flags {
83    ($visibility:vis $name:ident {$($flag:ident = $offset:literal,)*}) => {
84        #[repr(u8)]
85        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
86        #[allow(missing_docs)]
87        $visibility enum $name {
88            $($flag = $offset,)*
89        }
90
91        impl Flag for $name {
92            fn get_offset(&self) -> u8 {
93                *self as u8
94            }
95        }
96    };
97}
98
99macro_rules! bitfield {
100    (const $id:ident($n:ty); $doc:literal$(, $mark:literal)?) => {
101        #[doc = $doc]
102        $(#[doc = $mark])?
103        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
104        pub struct $id<T: Flag>($n, PhantomData<T>);
105
106        impl<T: Flag> ConstField<T> for $id<T> {
107            type Inner = $n;
108
109            fn new() -> Self {
110                Self::new_with(0)
111            }
112            fn new_with(value: Self::Inner) -> Self {
113                Self(value, PhantomData)
114            }
115            fn get_inner(&self) -> Self::Inner {
116                self.0
117            }
118            fn contains(&self, flag: impl Into<T>) -> bool {
119                self.0 & (1 << flag.into().get_offset()) != 0
120            }
121        }
122
123        impl<T: Flag> Default for $id<T> {
124            fn default() -> Self {
125                Self::new()
126            }
127        }
128
129        #[cfg(feature = "serde")]
130        impl<T: Flag> serde::Serialize for $id<T> {
131            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
132            where
133                S: serde::Serializer,
134            {
135                self.0.serialize(serializer)
136            }
137        }
138        #[cfg(feature = "serde")]
139        impl<'de, T: Flag> serde::Deserialize<'de> for $id<T> {
140            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
141            where
142                D: serde::Deserializer<'de>,
143            {
144                let inner = <Self as ConstField<T>>::Inner::deserialize(deserializer)?;
145
146                Ok(Self::new_with(inner))
147            }
148        }
149    };
150    ($id:ident($n:ty); $doc:literal$(, $mark:literal)?) => {
151        bitfield!(const $id($n); $doc$(, $mark)?);
152
153        impl<T: Flag> Field<T> for $id<T> {
154            fn insert(mut self, flag: impl Into<T>) -> Self {
155                self.0 |= 1 << flag.into().get_offset();
156                self
157            }
158            fn remove(mut self, flag: impl Into<T>) -> Self {
159                self.0 &= !(1 << flag.into().get_offset());
160                self
161            }
162            fn not(mut self) -> Self {
163                self.0 = !self.0;
164                self
165            }
166        }
167    };
168    ($n:ty => const $imm:ident, $mut:ident; $doc:literal) => {
169        bitfield!(const $imm($n); $doc, "\nThis is the immutable form and can be converted with `From::from` or `Into::into`.");
170        bitfield!($mut($n); $doc, "\nThis is the mutable form and can be converted with `From::from` or `Into::into`.");
171
172        impl<T: Flag> From<$imm<T>> for $mut<T> {
173            fn from(value: $imm<T>) -> Self {
174                Self::new_with(value.get_inner())
175            }
176        }
177        impl<T: Flag> From<$mut<T>> for $imm<T> {
178            fn from(value: $mut<T>) -> Self {
179                Self::new_with(value.get_inner())
180            }
181        }
182    };
183}
184
185bitfield!(u8 => const ConstBitField8, BitField8; "A bit field containing a `u8`");
186bitfield!(u16 => const ConstBitField16, BitField16; "A bit field containing a `u16`");
187bitfield!(u32 => const ConstBitField32, BitField32; "A bit field containing a `u32`");
188bitfield!(u64 => const ConstBitField64, BitField64; "A bit field containing a `u64`");
189bitfield!(u128 => const ConstBitField128, BitField128; "A bit field containing a `u128`");
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194
195    flags!(Byte {
196        Zero = 0,
197        One = 1,
198        Two = 2,
199        Three = 3,
200        Four = 4,
201        Five = 5,
202        Six = 6,
203        Seven = 7,
204    });
205
206    #[test]
207    fn new_empty() {
208        let field = BitField8::<Byte>::new();
209
210        assert_eq!(0, field.get_inner());
211    }
212    #[test]
213    fn new_filled() {
214        let field = BitField8::<Byte>::new_with(0b0010_0110);
215
216        assert_ne!(0, field.get_inner());
217    }
218    #[test]
219    fn contains() {
220        let field = BitField8::<Byte>::new_with(0b0010_0110);
221
222        assert!(field.contains_all(&[Byte::One, Byte::Two, Byte::Five]));
223        assert!(!field.contains_any(&[
224            Byte::Zero,
225            Byte::Three,
226            Byte::Four,
227            Byte::Six,
228            Byte::Seven,
229        ]));
230    }
231    #[test]
232    fn insert() {
233        let mut field = BitField8::<Byte>::new_with(0b0000_0111);
234
235        assert!(field.contains_all(&[Byte::Zero, Byte::One, Byte::Two]));
236
237        field = field.insert_all(&[Byte::Five, Byte::Seven]);
238
239        assert!(!field.contains_any(&[Byte::Three, Byte::Four, Byte::Six]));
240        assert!(field.contains_all(&[Byte::Zero, Byte::One, Byte::Two, Byte::Five, Byte::Seven]));
241    }
242    #[test]
243    fn remove() {
244        let mut field = BitField8::<Byte>::new_with(0b0000_0111);
245
246        assert!(field.contains_all(&[Byte::Zero, Byte::One, Byte::Two]));
247
248        field = field.remove_all(&[Byte::Zero, Byte::Two]);
249
250        assert!(field.contains(Byte::One));
251    }
252    #[test]
253    fn not() {
254        let mut field = BitField8::<Byte>::new_with(0b0000_1111);
255
256        assert!(field.contains_all(&[Byte::Zero, Byte::One, Byte::Two, Byte::Three]));
257
258        field = field.not();
259
260        assert!(field.contains_all(&[Byte::Four, Byte::Five, Byte::Six, Byte::Seven]));
261    }
262}