macro_bits/
bitfield.rs

1#[macro_export]
2#[doc(hidden)]
3macro_rules! check_field_mask {
4    (bool, $mask:expr, $name:ident) => {
5        assert!(
6            $mask != 0,
7            concat!("Mask for field ", stringify!($name), " is zero.")
8        );
9        assert!(
10            ($mask as usize).is_power_of_two(),
11            concat!(
12                "Field ",
13                stringify!($name),
14                " is of type boolean, but it's mask isn't a power of two."
15            )
16        );
17    };
18    ($field_type:ty, $mask:expr, $name:ident) => {
19        assert!(
20            $mask != 0,
21            concat!("Mask for field ", stringify!($name), " is zero.")
22        );
23        assert!(
24            (($mask >> 1 ^ $mask) as usize).count_ones() <= 2,
25            concat!("Mask for field ", stringify!($name), " is discontinous.")
26        );
27    };
28}
29#[macro_export]
30#[doc(hidden)]
31macro_rules! read_field {
32    (bool, $representation:ty, $shifted_value:expr) => {
33        $shifted_value != 0
34    };
35    (u8, $representation:ty, $shifted_value:expr) => {
36        $shifted_value as u8
37    };
38    (i8, $representation:ty, $shifted_value:expr) => {
39        $shifted_value as i8
40    };
41    (u16, $representation:ty, $shifted_value:expr) => {
42        $shifted_value as u16
43    };
44    (i16, $representation:ty, $shifted_value:expr) => {
45        $shifted_value as i16
46    };
47    (u32, $representation:ty, $shifted_value:expr) => {
48        $shifted_value as u32
49    };
50    (i32, $representation:ty, $shifted_value:expr) => {
51        $shifted_value as i32
52    };
53    (u64, $representation:ty, $shifted_value:expr) => {
54        $shifted_value as u64
55    };
56    (i64, $representation:ty, $shifted_value:expr) => {
57        $shifted_value as i64
58    };
59    (u128, $representation:ty, $shifted_value:expr) => {
60        $shifted_value as u128
61    };
62    (i128, $representation:ty, $shifted_value:expr) => {
63        $shifted_value as i128
64    };
65    ($field_type:ty, $representation:ty, $shifted_value:expr) => {
66        <$field_type as From<$representation>>::from($shifted_value)
67    };
68}
69#[macro_export]
70#[doc(hidden)]
71macro_rules! write_field {
72    (bool, $representation:ty, $shifted_value:expr) => {
73        $shifted_value as $representation
74    };
75    (u8, $representation:ty, $shifted_value:expr) => {
76        $shifted_value as $representation
77    };
78    (i8, $representation:ty, $shifted_value:expr) => {
79        $shifted_value as $representation
80    };
81    (u16, $representation:ty, $shifted_value:expr) => {
82        $shifted_value as $representation
83    };
84    (i16, $representation:ty, $shifted_value:expr) => {
85        $shifted_value as $representation
86    };
87    (u32, $representation:ty, $shifted_value:expr) => {
88        $shifted_value as $representation
89    };
90    (i32, $representation:ty, $shifted_value:expr) => {
91        $shifted_value as $representation
92    };
93    (u64, $representation:ty, $shifted_value:expr) => {
94        $shifted_value as $representation
95    };
96    (i64, $representation:ty, $shifted_value:expr) => {
97        $shifted_value as $representation
98    };
99    (u128, $representation:ty, $shifted_value:expr) => {
100        $shifted_value as $representation
101    };
102    (i128, $representation:ty, $shifted_value:expr) => {
103        $shifted_value as $representation
104    };
105    ($field_type:ty, $representation:ty, $shifted_value:expr) => {
106        <$representation as From<$field_type>>::from($shifted_value)
107    };
108}
109#[macro_export]
110/// Deconstruct a bitfield.
111///
112/// The parameters specified get compile checked through assertions.
113/// Custom types are allowed, as long as they implement conversions from and into the representation specified on the struct.
114/// ```
115/// use macro_bits::bitfield;
116///
117/// bitfield! {
118///     #[derive(Debug, PartialEq)]
119///     pub struct Test: u8 {
120///         /// This is a doc comment.
121///         pub x: u8 => 0b0000_1111,
122///         pub y: bool => 0b0001_0000,
123///         pub z: u16 => 0b1110_0000
124///     }
125/// }
126/// let target = Test {
127///     x: 0xf,
128///     y: true,
129///     z: 7
130/// };
131/// assert_eq!(target, Test::from_bits(0xff));
132/// assert_eq!(u8::from(target), 0xff);
133/// ```
134macro_rules! bitfield {
135    (
136        $(#[$attr:meta])*
137        $struct_vis:vis struct $struct_name:ident : $struct_representation:ty {
138            $(
139                $(#[$field_attr:meta])*
140                $field_vis:vis $field_name:ident : $field_type:ty => $field_mask:expr
141            ),*
142        }
143    ) => {
144        ::macro_bits::defile::defile! {
145
146            $(#[$attr])*
147            // Autogenerated from bitfield macro.
148            $struct_vis struct $struct_name {
149                $(
150                    $(#[$field_attr])*
151                    $field_vis $field_name: $field_type
152                ),*
153            }
154            #[allow(ineffective_bit_mask)]
155            // Autogenerated from bitfield macro.
156            impl $struct_name {
157                // compile-time checks
158                const _BITFIELD_PROOF: () = {
159                    // mask overlap
160                    assert!(
161                        ($($field_mask)^+) == $($field_mask)|+, 
162                        "Masks are overlapping."
163                    );
164                    // mask type checks
165                    $(
166                        assert!(
167                            $field_mask <= <$struct_representation>::MAX, 
168                            concat!("Mask for field ", 
169                                stringify!($field_name), 
170                                " is larger than the representation type."
171                            )
172                        );
173                    )*
174                    // field specific checks
175                    $(
176                        ::macro_bits::check_field_mask!($field_type, $field_mask, $field_name);
177                    )*
178                };
179                pub fn from_bits(value: $struct_representation) -> Self {
180                    $(
181                        let $field_name = 
182                        ::macro_bits::read_field!(
183                            $field_type, 
184                            $struct_representation,
185                            (value & $field_mask) >> ($field_mask as usize).trailing_zeros()
186                        );
187                    )+
188                    Self {
189                        $(
190                            $field_name,
191                        )+
192                    }
193                }
194                pub fn into_bits(self) -> $struct_representation {
195                    let mut data: $struct_representation = 0;
196                    $(
197                        data |= (
198                            (
199                                ::macro_bits::write_field!(
200                                    $field_type, 
201                                    $struct_representation, 
202                                    self.$field_name
203                                )
204                            ) << ($field_mask as usize).trailing_zeros()
205                        ) & $field_mask;
206                    )*
207                    data
208                }
209            }
210            ::macro_bits::generate_conversions!($struct_representation, $struct_representation, $struct_name);
211        }
212    };
213}