bitbash/
lib.rs

1#![cfg_attr(
2    all(test, feature = "const"),
3    feature(const_fn, const_panic, const_if_match, const_mut_refs)
4)]
5#![cfg_attr(not(test), no_std)]
6
7//! Macros for working with bitfields.
8//!
9//! This crate contains the `bitfield!` macro and `BitEnum` derive.
10//!
11//! `bitfield!` is used to generate (`const fn`) getter and setter methods on bitfields.
12//! `BitEnum` allows an enum to be used as a field value.
13//!
14//! # Example
15//! ```
16//! use bitbash::{bitfield, BitEnum};
17//!
18//! bitfield! {
19//!     #[derive(Copy, Clone, PartialEq, Eq)]
20//!     pub struct Foo(u16);
21//!
22//!     pub new(bar);
23//!     derive_debug;
24//!
25//!     pub field bar: Bar = [0..3];
26//!     pub field baz: usize = [3..7] ~ [8..12];
27//!     pub field quux: bool = [7];
28//! }
29//!
30//! #[derive(BitEnum, Copy, Clone, PartialEq, Eq, Debug)]
31//! pub enum Bar {
32//!     A = 0b101,
33//!     B = 0b010,
34//! }
35//!
36//! fn main() {
37//!     let mut foo = Foo::new(Bar::A).with_baz(0xcd);
38//!     foo.set_quux(true);
39//!     assert_eq!(foo.bar(), Bar::A);
40//!     assert_eq!(foo.baz(), 0xcd);
41//!     assert!(foo.quux());
42//!     assert_eq!(foo.0, ((Bar::A as u16) << 0) | (0xd << 3) | (0xc << 8) | (1 << 7));
43//! }
44//! ```
45//!
46//! # `bitfield!` macro
47//!
48//! ## Supported structs
49//! The `bitfield!` macro supports three kinds of structs: a tuple struct of an unsigned integer, a tuple struct of an array of unsigned integers, and a regular struct.
50//! ```
51//! # use bitbash::bitfield;
52//! bitfield! {
53//!     struct Foo(u8);
54//!
55//!     field a: bool = [7];
56//! }
57//!
58//! bitfield! {
59//!     struct Bar([u32; 2]);
60//!
61//!     field a: bool = 0[7];
62//!     field b: bool = 1[7];
63//! }
64//!
65//! bitfield! {
66//!     struct Baz {
67//!         _padding: [u32; 1],
68//!         apple: u32,
69//!         banana: u32,
70//!     }
71//!
72//!     field a: bool = apple[7];
73//! }
74//! # fn main() {}
75//! ```
76//! Regular structs may contain types that are not unsigned integers, however they cannot be referred to by `field`s.
77//!
78//! ## (`pub`) `field`
79//! A `field` statement defines in which bits a value is stored.
80//! Fields can refer to a single bit, a range of bits, a concatenation of bits, or a mapping of bits in the value to bits in the bitfield:
81//! ```
82//! # use bitbash::bitfield;
83//! bitfield! {
84//!     struct Foo([u32; 2]);
85//!
86//!     field single: bool = 0[0];
87//!     field range: u8 = 0[0..8];
88//!     field concatenation: u16 = 0[0..8] ~ 1[8..16];
89//!     field mapping: u32 {
90//!         [8..16] => 1[8..16],
91//!         [31] => 0[31],
92//!     }
93//! }
94//! # fn main() {}
95//! ```
96//! By default, three methods are generated for each field: a getter, a setter, and a builder:
97//! ```
98//! # type Value = ();
99//! # trait Example {
100//! fn field_name(&self) -> Value;
101//! fn set_field_name(&mut self, value: Value);
102//! fn with_field_name(self, value: Value) -> Self;
103//! # }
104//! ```
105//! To only generate the getter, use the `#[ro]` attribute.
106//! To make the setter and builder of a `pub field` private, use the `#[private_write]` attribute.
107//!
108//! When a field is read, its bits must be a valid representation of the value.
109//! When a field is written, only the bits in the value that are referred to by the field may be set.
110//! If these requirements are not met a panic occurs.
111//!
112//! Referring to bits that do not exist causes either a compile-time error or a runtime error, depending on whether `const fn` code was generated.
113//!
114//! Construction of values is done entirely in safe code.
115//! Moreover, no unsafe code is generated by the macros in this crate.
116//!
117//! ## (`pub`) `new()`
118//! When the `new()` statement is specified, an `fn new()` method that zero-initializes the bitfield is generated.
119//! Fields for which zero is not a valid representation can be passed as parameters to the `new()` statement to generate a method which takes their initial values as parameters:
120//! ```
121//! # use bitbash::{bitfield, BitEnum};
122//! bitfield! {
123//!     struct Foo(u32);
124//!
125//!     new(a);
126//!
127//!     field a: Bar = [0..3];
128//! }
129//!
130//! #[derive(BitEnum)]
131//! enum Bar {
132//!     A = 0b101,
133//!     B = 0b010,
134//! }
135//!
136//! fn main() {
137//!     let _ = Foo::new(Bar::A);
138//! }
139//! ```
140//!
141//! When the bitfield has been constructed, the `new` method reads all fields to ensure that no invalid representations exist in the bitfield.
142//! This behaviour can be disabled by using the `#[disable_check]` attribute.
143//!
144//! `new()` does not support structs that contain types that are not (arrays of) unsigned integers.
145//!
146//! ## `derive_debug`
147//! The `derive_debug` statement implements `core::fmt::Debug` for the bitfield.
148//! The underlying representation is not printed.
149//!
150//! ## `const fn`
151//! To generate `const fn` methods, build the bitbash crate with the `"const"` feature.
152//! Alternatively, use the `bitflags_const!` macro.
153//!
154//! A nightly compiler is required and the `#![feature(const_fn, const_panic, const_if_match, const_mut_refs)]` features must be enabled.
155//!
156//! ## `ConvertRepr` trait
157//! Bitfield values must implement the `ConvertRepr` trait.
158//! It is implemented for all unsigned integer types and types that derive `BitEnum`.
159//!
160//! The `try_from_repr` and `into_repr` methods are used in non-`const fn` bitfields.
161//! Implement `const_try_from_repr` and `const_into_repr` on your types to also use them in `const fn` bitfields.
162//!
163//! # `BitEnum` derive
164//! The `BitEnum` derive implements `ConvertRepr` for C-style enums.
165//!
166//! Enums that derive `BitEnum` can be used in both `const fn` and non-`const fn` bitfields.
167
168pub use bitbash_macros::{bitfield_const, bitfield_nonconst, BitEnumConst, BitEnumNonConst};
169
170#[cfg(feature = "const")]
171pub use self::{bitfield_const as bitfield, BitEnumConst as BitEnum};
172#[cfg(not(feature = "const"))]
173pub use self::{bitfield_nonconst as bitfield, BitEnumNonConst as BitEnum};
174
175pub trait ConvertRepr: Sized {
176    type Repr;
177
178    fn try_from_repr(repr: Self::Repr) -> Option<Self>;
179    fn into_repr(self) -> Self::Repr;
180}
181
182macro_rules! impl_convert_repr {
183    ($($t:ty),*) => {$(
184        impl ConvertRepr for $t {
185            type Repr = $t;
186
187            fn try_from_repr(repr: $t) -> Option<$t> {
188                Some(repr)
189            }
190
191            fn into_repr(self) -> $t {
192                self
193            }
194        }
195    )*}
196}
197impl_convert_repr!(usize, u8, u16, u32, u64, u128);
198
199impl ConvertRepr for bool {
200    type Repr = u8;
201
202    fn try_from_repr(repr: u8) -> Option<bool> {
203        match repr {
204            0 => Some(false),
205            1 => Some(true),
206            _ => None,
207        }
208    }
209
210    fn into_repr(self) -> u8 {
211        self as u8
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    macro_rules! tests {
218        ($bitfield:tt, $BitEnum:tt) => {
219            #[allow(unused_imports)]
220            use crate as bitbash;
221            crate::$bitfield! {
222                struct Foo(u32);
223
224                new(e);
225                derive_debug;
226
227                field a: bool = [31];
228                field b: u8 = [0..8];
229                field c: u8 = [8..12] ~ [16..20];
230                field d: u32 {
231                    [12..16] => [12..16],
232                    [20..24] => [20..24],
233                }
234                field e: FooE = [24..28];
235            }
236
237            #[derive(crate::$BitEnum, Copy, Clone, PartialEq, Eq, Debug)]
238            #[repr(u8)]
239            enum FooE {
240                A = 0b1010,
241                B = 0b0101,
242            }
243
244            #[test]
245            fn foo() {
246                let mut foo = Foo::new(FooE::A);
247                assert_eq!(foo.a(), false);
248                assert_eq!(foo.b(), 0u8);
249                assert_eq!(foo.c(), 0u8);
250                assert_eq!(foo.d(), 0u32);
251                assert_eq!(foo.e(), FooE::A);
252                assert_eq!(foo.0, (FooE::A as u32) << 24);
253                foo.set_a(true);
254                foo.set_b(0x12u8);
255                foo.set_c(0x34u8);
256                foo.set_d((0x5u32 << 12) | (0x6u32 << 20));
257                foo.set_e(FooE::B);
258                assert_eq!(foo.a(), true);
259                assert_eq!(foo.b(), 0x12u8);
260                assert_eq!(foo.c(), 0x34u8);
261                assert_eq!(foo.d(), (0x5u32 << 12) | (0x6u32 << 20));
262                assert_eq!(foo.e(), FooE::B);
263                assert_eq!(
264                    foo.0,
265                    (1 << 31)
266                        | (0x12 << 0)
267                        | ((0x4 << 8) | (0x3 << 16))
268                        | ((0x5 << 12) | (0x6 << 20))
269                        | ((FooE::B as u32) << 24)
270                );
271
272                match <FooE as crate::ConvertRepr>::try_from_repr(FooE::A as u8) {
273                    Some(FooE::A) => (),
274                    _ => unreachable!(),
275                }
276            }
277
278            const BAR_LEN: usize = 2;
279
280            crate::$bitfield! {
281                struct Bar([u32; BAR_LEN]);
282
283                new();
284
285                field a: u32 = 0;
286                field b: u8 = (BAR_LEN - 1)[16..24];
287            }
288
289            #[test]
290            fn bar() {
291                let mut bar = Bar::new();
292                assert_eq!(bar.a(), 0);
293                assert_eq!(bar.b(), 0);
294                assert_eq!(bar.0, [0, 0]);
295                bar.set_a(1);
296                bar.set_b(2);
297                assert_eq!(bar.a(), 1);
298                assert_eq!(bar.b(), 2);
299                assert_eq!(bar.0, [1, 2 << 16]);
300            }
301
302            crate::$bitfield! {
303                struct Baz {
304                    _padding0: [u32; 1],
305                    a0: u32,
306                    a1: u32,
307                    _padding1: [u32; 2],
308                    pub b: u32,
309                }
310
311                new();
312
313                field a: u64 = a0 ~ a1;
314            }
315
316            #[test]
317            fn baz() {
318                let mut baz = Baz::new();
319                assert_eq!(baz.a(), 0);
320                assert_eq!(baz._padding0, [0]);
321                assert_eq!(baz.a0, 0);
322                assert_eq!(baz.a1, 0);
323                assert_eq!(baz._padding1, [0, 0]);
324                assert_eq!(baz.b, 0);
325                baz.set_a(!0);
326                assert_eq!(baz.a(), !0);
327                assert_eq!(baz._padding0, [0]);
328                assert_eq!(baz.a0, !0);
329                assert_eq!(baz.a1, !0);
330                assert_eq!(baz._padding1, [0, 0]);
331                assert_eq!(baz.b, 0);
332                baz.b = 0x10101010;
333                assert_eq!(baz.a(), !0);
334            }
335
336            const QUUX_START: isize = 0x5;
337
338            #[derive(crate::$BitEnum)]
339            enum Quux {
340                Asdf = QUUX_START,
341                Zxcv = QUUX_START + 1,
342            }
343        };
344    }
345    #[cfg(feature = "const")]
346    mod konst {
347        tests!(bitfield_const, BitEnumConst);
348    }
349    mod nonconst {
350        tests!(bitfield_nonconst, BitEnumNonConst);
351    }
352}