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}