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