Skip to main content

bitrs/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![cfg_attr(not(test), no_std)]
6
7// So that layout!å works in the test submodule.
8#[cfg(test)]
9extern crate self as bitrs;
10
11use core::fmt;
12
13// Re-exported for use by code generated by `layout!`, `multilayout!`, and
14// `bitfield_repr` so downstream crates don't have to depend on `zerocopy`
15// directly. This also allows `bitrs` to control the version of `zerocopy` that
16// is used.
17#[doc(hidden)]
18pub use ::zerocopy as __zerocopy;
19
20/// Specifies a layout of bitfields.
21///
22/// See the crate's `README.md` for details on syntax and behavior.
23pub use bitrs_macro::layout;
24
25/// Specifies a family of closely related bitfield layouts in one place,
26/// expanding to one [`layout!`]-equivalent type per declared struct.
27///
28/// See the crate's `README.md` for details on syntax and behavior.
29pub use bitrs_macro::multilayout;
30
31/// Syntax sugar for defining a layout representation that also auto-derives the
32/// traits required of a custom bitfield representation.
33///
34/// In particular, `#[bitfield_repr(...)]` translates to
35/// ```text
36/// #[repr(...)]
37/// #[derive(
38///     Debug,
39///     Eq,
40///     PartialEq,
41///     zerocopy::Immutable,
42///     zerocopy::IntoBytes,
43///     zerocopy::TryFromBytes,
44/// )]
45/// ```
46pub use bitrs_macro::bitfield_repr;
47
48//
49// The following macros are convenient routines for use in the proc macros.
50//
51// TODO(https://github.com/rust-lang/rust-project-goals/issues/106): Ideally
52// these would be const functions generic over the base type, but that can't be
53// properly done until we have const traits.
54//
55
56#[doc(hidden)]
57#[macro_export]
58macro_rules! get_bit {
59    ($int:expr, $low_bit:literal) => {
60        (($int) & (1 << $low_bit)) != 0
61    };
62}
63
64#[doc(hidden)]
65#[macro_export]
66macro_rules! set_bit {
67    ($int:expr, $low_bit:literal, $value:ident) => {
68        if $value {
69            $int |= (1 << $low_bit);
70        } else {
71            $int &= !(1 << $low_bit);
72        }
73    };
74}
75
76#[doc(hidden)]
77#[macro_export]
78macro_rules! shifted_mask {
79    ($base:ty, $high_bit:literal, $low_bit:literal) => {{
80        const WIDTH: usize = $high_bit - $low_bit + 1;
81        if (<$base>::BITS as usize) == WIDTH { <$base>::MAX } else { (1 << WIDTH) - 1 }
82    }};
83}
84
85#[doc(hidden)]
86#[macro_export]
87macro_rules! get_field {
88    ($base:ty, $clamped:ty, $high_bit:literal, $low_bit:literal, $shifted:literal, $int:expr) => {{
89        const SHIFTED_MASK: $base = $crate::shifted_mask!($base, $high_bit, $low_bit);
90        let value = if $shifted {
91            ($int >> $low_bit) & SHIFTED_MASK
92        } else {
93            const UNSHIFTED_MASK: $base = SHIFTED_MASK << $low_bit;
94            $int & UNSHIFTED_MASK
95        };
96        value as $clamped
97    }};
98}
99
100#[doc(hidden)]
101#[macro_export]
102macro_rules! set_field {
103    ($base:ty, $high_bit:literal, $low_bit:literal, $shifted:literal, $int:expr, $value:ident) => {
104        const SHIFTED_MASK: $base = $crate::shifted_mask!($base, $high_bit, $low_bit);
105        const UNSHIFTED_MASK: $base = SHIFTED_MASK << $low_bit;
106
107        $int &= !UNSHIFTED_MASK;
108        if $shifted {
109            debug_assert!(($value & !SHIFTED_MASK) == 0);
110            $int |= ($value & SHIFTED_MASK) << $low_bit;
111        } else {
112            debug_assert!(($value & !UNSHIFTED_MASK) == 0);
113            $int |= $value & UNSHIFTED_MASK;
114        }
115    };
116}
117
118/// Implemented by unsigned integral type, this trait represents a valid base
119/// type for a bitfield layout.
120pub trait Unsigned: fmt::Debug + private::Sealed {}
121impl Unsigned for u8 {}
122impl Unsigned for u16 {}
123impl Unsigned for u32 {}
124impl Unsigned for u64 {}
125impl Unsigned for u128 {}
126
127// Ensures that no type outside of bitrs can implement this type.
128mod private {
129    pub trait Sealed {}
130    impl Sealed for u8 {}
131    impl Sealed for u16 {}
132    impl Sealed for u32 {}
133    impl Sealed for u64 {}
134    impl Sealed for u128 {}
135}
136
137/// Represents an invalid bit pattern for a field with a custom representation.
138///
139/// This is returned as the error type for the getters of such fields.
140#[derive(Debug)]
141pub struct InvalidBits<Base: Unsigned>(pub Base);
142
143/// The metadata of a (non-reserved) bitfield.
144///
145/// The iterator of a [`layout!`] type will have an associated item type of
146/// `(&'static FieldMetadata<Base>, Base)`.
147#[derive(Debug)]
148pub struct FieldMetadata<Base: Unsigned> {
149    /// The name of the bitfield.
150    pub name: &'static str,
151    /// The high bit of the bitfield.
152    pub high_bit: u32,
153    /// The low bit of the bitfield.
154    pub low_bit: u32,
155    /// The default value of the bitfield.
156    pub default: Base,
157}
158
159#[cfg(test)]
160mod tests {
161    use super::{FieldMetadata, bitfield_repr, layout, multilayout};
162
163    layout!({
164        struct EmptyU8(u8);
165        {}
166    });
167
168    layout!({
169        struct OneFieldU16(u16);
170        {
171            let a @ 15..0;
172        }
173    });
174
175    layout!({
176        struct TwoFieldsU32(u32);
177        {
178            let a @ 31..16;
179            let b @ 15..0;
180        }
181    });
182
183    layout!({
184        struct ThreeFieldsU64(u64);
185        {
186            let a @ 63..32;
187            let b @ 31..16;
188            let c @ 15..0;
189        }
190    });
191
192    layout!({
193        struct FourFieldsU128(u128);
194        {
195            let a @ 127..96;
196            let b @ 95..64;
197            let c @ 63..32;
198            let d @ 31..0;
199        }
200    });
201
202    #[test]
203    fn size_and_alignment() {
204        assert_eq!(size_of::<EmptyU8>(), size_of::<u8>());
205        assert_eq!(align_of::<EmptyU8>(), align_of::<u8>());
206
207        assert_eq!(size_of::<OneFieldU16>(), size_of::<u16>());
208        assert_eq!(align_of::<OneFieldU16>(), align_of::<u16>());
209
210        assert_eq!(size_of::<TwoFieldsU32>(), size_of::<u32>());
211        assert_eq!(align_of::<TwoFieldsU32>(), align_of::<u32>());
212
213        assert_eq!(size_of::<ThreeFieldsU64>(), size_of::<u64>());
214        assert_eq!(align_of::<ThreeFieldsU64>(), align_of::<u64>());
215
216        assert_eq!(size_of::<FourFieldsU128>(), size_of::<u128>());
217        assert_eq!(align_of::<FourFieldsU128>(), align_of::<u128>());
218    }
219
220    #[bitfield_repr(u8)]
221    pub enum CustomFieldRepr {
222        Option1 = 0xa,
223        Option2 = 0xf,
224    }
225
226    layout!({
227        pub struct Example(u64);
228        {
229            let u32_repr @ 44..27;
230            let custom @ 26..23: CustomFieldRepr;
231            let custom_with_default @ 22..19: CustomFieldRepr = CustomFieldRepr::Option1;
232            let __ @ 18..11 = 0xef;
233            let with_default @ 10..9 = 0b11;
234            let bit @ 8;
235            let u8_repr @ 7..4;
236            let __ @ 3..2 = 1;
237            let __ @ 1..0;
238        }
239    });
240
241    #[test]
242    fn constants() {
243        assert_eq!(Example::RSVD1_MASK, (0xef << 11) | (0b01 << 2));
244        assert_eq!(Example::RSVD0_MASK, (0x10 << 11) | (0b10 << 2));
245
246        assert_eq!(
247            Example::DEFAULT,
248            (0xef << 11) | (0b01 << 2) | ((CustomFieldRepr::Option1 as u64) << 19) | (0b11 << 9)
249        );
250
251        assert_eq!(Example::U32_REPR_MASK, 0x1fff_f800_0000);
252        assert_eq!(Example::U32_REPR_SHIFT, 27usize,);
253
254        assert_eq!(Example::CUSTOM_MASK, 0x780_0000);
255        assert_eq!(Example::CUSTOM_SHIFT, 23usize);
256
257        assert_eq!(Example::CUSTOM_WITH_DEFAULT_MASK, 0x78_0000);
258        assert_eq!(Example::CUSTOM_WITH_DEFAULT_SHIFT, 19usize);
259
260        assert_eq!(Example::RSVD_18_11, 0xef << 11);
261
262        assert_eq!(Example::WITH_DEFAULT_MASK, 0x600);
263        assert_eq!(Example::WITH_DEFAULT_SHIFT, 9usize);
264
265        assert_eq!(Example::BIT_SHIFT, 8usize);
266
267        assert_eq!(Example::U8_REPR_MASK, 0xf0);
268        assert_eq!(Example::U8_REPR_SHIFT, 4usize);
269
270        assert_eq!(Example::RSVD_3_2, 1 << 2);
271    }
272
273    // new() should return a value with only reserved-as values set.
274    #[test]
275    fn new() {
276        assert_eq!(Example::new().bits(), Example::RSVD1_MASK);
277    }
278
279    // default() should return a value with only defaults and reserved-as
280    // values set.
281    #[test]
282    fn default() {
283        assert_eq!(Example::default().bits(), Example::DEFAULT);
284    }
285
286    #[test]
287    fn from() {
288        assert_eq!(Example::from(Example::RSVD1_MASK).bits(), Example::RSVD1_MASK);
289        assert_eq!(Example::from(1 | Example::RSVD1_MASK).bits(), 1 | Example::RSVD1_MASK);
290        assert_eq!(
291            Example::from(0xffff_0000_0000_0000 | Example::RSVD1_MASK).bits(),
292            0xffff_0000_0000_0000 | Example::RSVD1_MASK
293        );
294    }
295
296    #[test]
297    fn from_then_get() {
298        let example = Example::from(
299            0xabcd << 27
300                | (CustomFieldRepr::Option1 as u64) << 23
301                | (CustomFieldRepr::Option2 as u64) << 19
302                | 0b10 << 9
303                | 1 << 8
304                | 0xc << 4
305                | Example::RSVD1_MASK,
306        );
307        assert_eq!(example.u32_repr(), 0xabcd);
308        assert_eq!(example.custom(), CustomFieldRepr::Option1);
309        assert_eq!(example.custom_with_default(), CustomFieldRepr::Option2);
310        assert_eq!(example.with_default(), 0b10);
311        assert!(example.bit());
312        assert_eq!(example.u8_repr(), 0xc);
313    }
314
315    #[test]
316    fn set_then_get() {
317        let example = *Example::new()
318            .set_u32_repr(0xabcd)
319            .set_custom(CustomFieldRepr::Option1)
320            .set_custom_with_default(CustomFieldRepr::Option2)
321            .set_with_default(0b10)
322            .set_bit(true)
323            .set_u8_repr(0xc);
324        assert_eq!(example.u32_repr(), 0xabcd);
325        assert_eq!(example.custom(), CustomFieldRepr::Option1);
326        assert_eq!(example.custom_with_default(), CustomFieldRepr::Option2);
327        assert_eq!(example.with_default(), 0b10);
328        assert!(example.bit());
329        assert_eq!(example.u8_repr(), 0xc);
330    }
331
332    #[test]
333    fn iter() {
334        type Metadata = FieldMetadata<u64>;
335
336        const EXPECTED: [(u64, Metadata); 6] = [
337            (0xabcd, Metadata { name: "u32_repr", high_bit: 44, low_bit: 27, default: 0 }),
338            (0xa, Metadata { name: "custom", high_bit: 26, low_bit: 23, default: 0 }),
339            (
340                0xf,
341                Metadata { name: "custom_with_default", high_bit: 22, low_bit: 19, default: 0xa },
342            ),
343            (0b10, Metadata { name: "with_default", high_bit: 10, low_bit: 9, default: 0b11 }),
344            (0b1, Metadata { name: "bit", high_bit: 8, low_bit: 8, default: 0 }),
345            (0xc, Metadata { name: "u8_repr", high_bit: 7, low_bit: 4, default: 0 }),
346        ];
347
348        let example = *Example::new()
349            .set_u32_repr(0xabcd)
350            .set_custom(CustomFieldRepr::Option1)
351            .set_custom_with_default(CustomFieldRepr::Option2)
352            .set_with_default(0b10)
353            .set_bit(true)
354            .set_u8_repr(0xc);
355
356        let actual: Vec<(&'static Metadata, u64)> = example.into_iter().collect();
357        let rev_actual: Vec<(&'static Metadata, u64)> = example.into_iter().rev().collect();
358
359        assert_eq!(actual.len(), EXPECTED.len());
360        assert_eq!(rev_actual.len(), EXPECTED.len());
361        for i in 0..EXPECTED.len() {
362            let (expected_val, expected_metadata) = &EXPECTED[i];
363            for (label, (actual_metadata, actual_val)) in
364                [("fwd", &actual[i]), ("rev", &rev_actual[EXPECTED.len() - 1 - i])]
365            {
366                assert_eq!(actual_val, expected_val, "{label}:{i}");
367                assert_eq!(actual_metadata.name, expected_metadata.name, "{label}:{i}");
368                assert_eq!(actual_metadata.high_bit, expected_metadata.high_bit, "{label}:{i}");
369                assert_eq!(actual_metadata.low_bit, expected_metadata.low_bit, "{label}:{i}");
370                assert_eq!(actual_metadata.default, expected_metadata.default, "{label}:{i}");
371            }
372        }
373    }
374
375    layout!({
376        struct Unshifted(u32);
377        {
378            let field @ 19..16;
379            #[unshifted]
380            let unshifted_field @ 15..12;
381            let __ @ 11..9;
382            #[unshifted]
383            let unshifted_bit @ 8;
384            let normal_bit @ 7;
385            let __ @ 6..0;
386        }
387    });
388
389    #[test]
390    fn unshifted_multi_bit_getter() {
391        let val = Unshifted::from(0x5 << 12);
392        assert_eq!(val.unshifted_field(), 0x5000);
393    }
394
395    #[test]
396    fn unshifted_multi_bit_setter() {
397        let mut val = Unshifted::new();
398        val.set_unshifted_field(0xa000);
399        assert_eq!(val.unshifted_field(), 0xa000);
400        assert_eq!(val.bits() & (0xf << 12), 0xa000);
401    }
402
403    #[test]
404    fn unshifted_single_bit_getter() {
405        let val = Unshifted::from(1 << 8);
406        assert_eq!(val.unshifted_bit(), 1 << 8);
407
408        let val = Unshifted::from(0);
409        assert_eq!(val.unshifted_bit(), 0);
410    }
411
412    #[test]
413    fn unshifted_single_bit_setter() {
414        let mut val = Unshifted::new();
415        val.set_unshifted_bit(1 << 8);
416        assert_eq!(val.unshifted_bit(), 1 << 8);
417
418        val.set_unshifted_bit(0);
419        assert_eq!(val.unshifted_bit(), 0);
420    }
421
422    #[test]
423    fn unshifted_round_trip() {
424        let val = *Unshifted::new()
425            .set_unshifted_field(0x7000)
426            .set_unshifted_bit(1 << 8)
427            .set_field(0xa)
428            .set_normal_bit(true);
429        assert_eq!(val.unshifted_field(), 0x7000);
430        assert_eq!(val.unshifted_bit(), 1 << 8);
431        assert_eq!(val.field(), 0xa);
432        assert!(val.normal_bit());
433    }
434
435    #[test]
436    fn unshifted_ignores_other_bits() {
437        let val = Unshifted::from(0xffff_ffff);
438        assert_eq!(val.unshifted_field(), 0xf000);
439        assert_eq!(val.unshifted_bit(), 1 << 8);
440    }
441
442    multilayout!({
443        #[bitrs(m, rv32)]
444        pub struct Mstatus32(u32);
445        #[bitrs(m, rv64)]
446        pub struct Mstatus64(u64);
447        #[bitrs(rv32)]
448        pub struct Sstatus32(u32);
449        #[bitrs(rv64)]
450        pub struct Sstatus64(u64);
451
452        #[rv32]
453        {
454            let sd @ 31;
455        }
456        #[rv64]
457        {
458            let sd @ 63;
459        }
460        #[all(m, rv64)]
461        {
462            let mbe @ 37;
463            let sbe @ 36;
464            let sxl @ 35..34;
465        }
466        #[rv64]
467        {
468            let uxl @ 33..32;
469        }
470        #[m]
471        {
472            let tsr @ 22;
473            let tw @ 21;
474            let tvm @ 20;
475            let mprv @ 17;
476            let mpp @ 12..11;
477            let mpie @ 7;
478            let mie @ 3;
479        }
480        {
481            let mxr @ 19;
482            let sum @ 18;
483            let xs @ 16..15;
484            let fs @ 14..13;
485            let vs @ 10..9;
486            let spp @ 8;
487            let ube @ 6;
488            let spie @ 5;
489            let sie @ 1;
490        }
491    });
492
493    #[test]
494    fn sd_at_xlen_minus_1() {
495        // SD is at bit 31 on RV32 and bit 63 on RV64 — same name in every
496        // *status variant, position keyed on base width.
497        let m32 = *Mstatus32::new().set_sd(true);
498        let m64 = *Mstatus64::new().set_sd(true);
499        let s32 = *Sstatus32::new().set_sd(true);
500        let s64 = *Sstatus64::new().set_sd(true);
501        assert_eq!(m32.bits() & Mstatus32::SD_MASK, 1u32 << 31);
502        assert_eq!(m64.bits() & Mstatus64::SD_MASK, 1u64 << 63);
503        assert_eq!(s32.bits() & Sstatus32::SD_MASK, 1u32 << 31);
504        assert_eq!(s64.bits() & Sstatus64::SD_MASK, 1u64 << 63);
505    }
506
507    #[test]
508    fn mstatus_round_trip() {
509        // Behavioral check on shared low-half fields plus M-mode-only ones:
510        // set, then read back.
511        let m = *Mstatus64::new().set_tsr(true).set_mpp(0b11).set_mxr(true).set_mie(true);
512        assert!(m.tsr());
513        assert_eq!(m.mpp(), 0b11);
514        assert!(m.mxr());
515        assert!(m.mie());
516    }
517
518    #[test]
519    fn uxl_visible_in_both_modes() {
520        // UXL appears in mstatus64 and sstatus64 at the same position. SXL
521        // is M-mode-only — Sstatus64 doesn't have a set_sxl method.
522        let m = *Mstatus64::new().set_uxl(0b10);
523        let s = *Sstatus64::new().set_uxl(0b10);
524        assert_eq!(m.uxl(), 0b10);
525        assert_eq!(s.uxl(), 0b10);
526    }
527
528    #[test]
529    fn sstatus_shared_low_half_round_trip() {
530        // The shared low half (MXR/SUM/SPP/UBE/SPIE/SIE etc.) must work in
531        // the supervisor variants too — set and read back on both.
532        let s32 = *Sstatus32::new().set_mxr(true).set_sum(true).set_spp(true).set_sie(true);
533        let s64 = *Sstatus64::new().set_mxr(true).set_sum(true).set_spp(true).set_sie(true);
534        assert!(s32.mxr() && s32.sum() && s32.spp() && s32.sie());
535        assert!(s64.mxr() && s64.sum() && s64.spp() && s64.sie());
536    }
537
538    multilayout!({
539        #[bitrs(a, x)]
540        struct PredA(u32);
541        #[bitrs(b, x)]
542        struct PredB(u32);
543        #[bitrs(c, y)]
544        struct PredC(u32);
545
546        // Tests `any` and trailing comma
547        #[any(a, b)]
548        {
549            let ab_shared @ 0;
550        }
551
552        // Tests `not` with trailing comma
553        #[not(c)]
554        {
555            let not_c @ 1;
556        }
557
558        // Tests nested predicates with trailing comma in all
559        #[all(x, any(a, not(b)))]
560        {
561            let nested @ 2;
562        }
563
564        // Tests custom repr, unshifted field, reserved field with default
565        #[c]
566        {
567            let custom @ 6..3: CustomFieldRepr = CustomFieldRepr::Option1;
568            #[unshifted]
569            let unshifted @ 15..12;
570            let __ @ 19..16 = 0xa;
571        }
572    });
573
574    #[test]
575    fn any_not_and_nested_predicates() {
576        let a = *PredA::new().set_ab_shared(true).set_not_c(true).set_nested(true);
577        assert!(a.ab_shared());
578        assert!(a.not_c());
579        assert!(a.nested());
580
581        let b = *PredB::new().set_ab_shared(true).set_not_c(true);
582        assert!(b.ab_shared());
583        assert!(b.not_c());
584    }
585
586    #[test]
587    fn multilayout_feature_interactions() {
588        assert_eq!(PredC::new().bits(), PredC::RSVD1_MASK);
589        assert_eq!(PredC::RSVD1_MASK, 0xa << 16);
590
591        let mut c = PredC::default();
592        assert_eq!(c.custom(), CustomFieldRepr::Option1);
593        assert_eq!(c.bits(), PredC::DEFAULT);
594
595        c.set_custom(CustomFieldRepr::Option2);
596        assert_eq!(c.custom(), CustomFieldRepr::Option2);
597
598        c.set_unshifted(0x5000);
599        assert_eq!(c.unshifted(), 0x5000);
600
601        let fields: Vec<(&'static FieldMetadata<u32>, u32)> = c.iter().collect();
602        assert_eq!(fields.len(), 2);
603        assert_eq!(fields[0].0.name, "unshifted");
604        assert_eq!(fields[0].1, 0x5);
605        assert_eq!(fields[1].0.name, "custom");
606        assert_eq!(fields[1].1, CustomFieldRepr::Option2 as u32);
607    }
608}