diff options
Diffstat (limited to 'bit_field/src/lib.rs')
-rw-r--r-- | bit_field/src/lib.rs | 251 |
1 files changed, 172 insertions, 79 deletions
diff --git a/bit_field/src/lib.rs b/bit_field/src/lib.rs index aa943a3..bf4e368 100644 --- a/bit_field/src/lib.rs +++ b/bit_field/src/lib.rs @@ -2,110 +2,203 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +//! This crate provides a `#[bitfield]` attribute macro for defining structs in +//! a packed binary representation that supports access to ranges of bits. +//! +//! We conceptualize one of these structs as a sequence of bits 0..N. The bits +//! are grouped into fields in the order specified by a struct written by the +//! caller. The `#[bitfield]` attribute rewrites the caller's struct into a +//! private byte array representation with public getter and setter methods for +//! each field. +//! +//! Byte order: note that we consider the bit `i` to be the `i % 8`'th least +//! significant bit in the `i / 8`'th byte of the struct. +//! +//! The total number of bits N is required to be a multiple of 8 (this is +//! checked at compile time). +//! +//! # Examples +//! +//! The following invocation builds a struct with a total size of 32 bits or 4 +//! bytes. It places field `a` in the least significant bit of the first byte, +//! field `b` in the next three least significant bits, field `c` in the +//! remaining four most significant bits of the first byte, and field `d` +//! spanning the next three bytes. The least significant byte of `d` will be +//! held in the second byte of our struct, adjacent to the byte holding the +//! first three fields. +//! +//! ``` +//! extern crate bit_field; +//! +//! use bit_field::*; +//! +//! #[bitfield] +//! pub struct MyFourBytes { +//! a: B1, +//! b: B3, +//! c: B4, +//! d: B24, +//! } +//! ``` +//! +//! ```text +//! less significant +//! / more significant +//! / / +//! (first byte) (second byte) / (third) / (fourth byte) +//! 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +//! | \ / \_ _/ \_______________________ _______________________/ +//! a b c less significant d more significant +//! ``` +//! +//! The code emitted by the `#[bitfield]` macro for this struct is as follows. +//! Note that the field getters and setters use whichever of `u8`, `u16`, `u32`, +//! `u64` is the smallest while being at least as large as the number of bits in +//! the field. +//! +//! ```ignore +//! impl MyFourBytes { +//! // Initializes all fields to 0. +//! pub fn new() -> Self; +//! +//! // Field getters and setters: +//! pub fn get_a(&self) -> u8; +//! pub fn set_a(&mut self, val: u8); +//! pub fn get_b(&self) -> u8; +//! pub fn set_b(&mut self, val: u8); +//! pub fn get_c(&self) -> u8; +//! pub fn set_c(&mut self, val: u8); +//! pub fn get_d(&self) -> u32; +//! pub fn set_d(&mut self, val: u32); +//! +//! // Bit-level accessors: +//! pub fn get_bit(&self, offset: usize) -> bool; +//! pub fn set_bit(&mut self, offset: usize, val: bool); +//! pub fn get(&self, offset: usize, width: u8) -> u64; +//! pub fn set(&mut self, offset: usize, width: u8, val: u64); +//! } +//! ``` +//! +//! We also accept `bool` as a field type, which is laid out equivalently to +//! `B1` but with accessors that use `bool` rather than `u8`. +//! +//! ``` +//! extern crate bit_field; +//! +//! use bit_field::*; +//! +//! #[bitfield] +//! pub struct MyFourBytes { +//! a: bool, +//! b: B3, +//! c: B4, +//! d: B24, +//! } +//! ``` +//! +//! Derives may be specified and are applied to the data structure post +//! rewriting by the macro. +//! +//! ``` +//! extern crate bit_field; +//! +//! use bit_field::*; +//! +//! #[bitfield] +//! #[derive(Copy, Clone)] +//! pub struct ExampleWithDerives { +//! car: B4, +//! cdr: B4, +//! } +//! ``` +//! +//! If the total size is not a multiple of 8 bits, you will receive an error +//! message at compile time mentioning: +//! +//! > the trait `bit_field::checks::TotalSizeIsMultipleOfEightBits` is not implemented +//! +//! ```compile_fail +//! extern crate bit_field; +//! +//! use bit_field::*; +//! +//! #[bitfield] +//! pub struct Broken { +//! field_a: B1, +//! field_b: B3, +//! field_c: B6, +//! } +//! ``` + #[allow(unused_imports)] #[macro_use] extern crate bit_field_derive; -pub use bit_field_derive::*; +pub use bit_field_derive::bitfield; -// This functions calculate max possible number represented by `width` bits. If one day this can be -// done in other ways, remove this function. For now, stop worrying and trust constant -// propagation. (checked assembly code, it's a constant when opt-leve >= 2) -fn max_number_of_width(width: u8) -> u64 { - if width < 64 { - (1 << width) - 1 - } else { - u64::max_value() - } -} - -/// BitFieldSpecifier is a group of structs help defining bitfield. It should only -/// be used with the #[bitfield] attribute macro. -/// Example: -/// #[bitfield] -/// pub struct MyBitFieldSchema { -/// field_a : BitField1, -/// field_b : BitField3, -/// field_c : BitField5, -/// field_d : BitField32, -/// } -/// -/// bit_field_derive implementation will use the static informations associated -/// with those tyes to generate a struct named MyBitField and getter/setter for -/// all fields. -/// An example getter/setter is: -/// fn get_field_a(&self) -> u8 -/// fn set_field_a(&self, val: u8) -/// For larger fields: -/// fn get_field_d(&self) -> u32 -/// fn set_field_d(&self, val: u32) -/// -/// You can also pass attributes to the defined bitfield structs. Simply do this: -/// #[derive(Clone)] -/// For more details, refer to bit_field_derive. -pub trait BitFieldSpecifier { - /// Width of this field in bits. +// This trait is sealed and not intended to be implemented outside of the +// bit_field crate. +#[doc(hidden)] +pub trait BitFieldSpecifier: private::Sealed { + // Width of this field in bits. const FIELD_WIDTH: u8; - /// Default data type of this field. - /// For any field, we use the closest u* type. e.g. FIELD_WIDTH <= 8 will - /// have defulat type of u8. - /// It's possible to write a custom specifier and use i8. + // Default data type of this field. + // For any field, we use the closest u* type. e.g. FIELD_WIDTH <= 8 will + // have defulat type of u8. + // It's possible to write a custom specifier and use i8. type DefaultFieldType; - /// Max value of this field. - fn field_max() -> u64 { - max_number_of_width(Self::FIELD_WIDTH) - } fn from_u64(val: u64) -> Self::DefaultFieldType; fn into_u64(val: Self::DefaultFieldType) -> u64; } -pub struct BitFieldBool; -impl BitFieldSpecifier for BitFieldBool { +// Largest u64 representable by this bit field specifier. Used by generated code +// in bit_field_derive. +#[doc(hidden)] +#[inline] +pub fn max<T: BitFieldSpecifier>() -> u64 { + if T::FIELD_WIDTH < 64 { + (1 << T::FIELD_WIDTH) - 1 + } else { + u64::max_value() + } +} + +// Defines bit_field::BitField0 through bit_field::BitField64. +bit_field_derive::define_bit_field_specifiers!(); + +impl BitFieldSpecifier for bool { const FIELD_WIDTH: u8 = 1; type DefaultFieldType = bool; + + #[inline] fn from_u64(val: u64) -> Self::DefaultFieldType { val > 0 } + + #[inline] fn into_u64(val: Self::DefaultFieldType) -> u64 { val as u64 } } -macro_rules! bitfield_structs { - ($t:ty, $min_width:expr, $bt:ident $($bts:ident)*) - => { - pub struct $bt; - impl BitFieldSpecifier for $bt { - const FIELD_WIDTH: u8 = $min_width; - type DefaultFieldType = $t; - fn from_u64(val: u64) -> Self::DefaultFieldType { - val as Self::DefaultFieldType - } - fn into_u64(val: Self::DefaultFieldType) -> u64 { - val as u64 - } - } - bitfield_structs!($t, $min_width + 1, $($bts)*); - }; - ($t:ty, $min_width:expr,) => {}; -} -bitfield_structs! { - u8, 0, BitField0 BitField1 BitField2 BitField3 BitField4 BitField5 BitField6 BitField7 BitField8 -} +impl private::Sealed for bool {} -bitfield_structs! { - u16, 9, BitField9 BitField10 BitField11 BitField12 BitField13 BitField14 BitField15 BitField16 +mod private { + // Seal for the BitFieldSpecifier trait. This seal trait is not nameable + // outside of the bit_field crate, so we are guaranteed that all impls of + // BitFieldSpecifier come from within this crate. + pub trait Sealed {} } -bitfield_structs! { - u32, 17, BitField17 BitField18 BitField19 BitField20 BitField21 BitField22 BitField23 BitField24 - BitField25 BitField26 BitField27 BitField28 BitField29 BitField30 BitField31 BitField32 +// Instantiated by the generated code to prove that the total size of fields is +// a multiple of 8 bits. +#[doc(hidden)] +pub struct Check<T: checks::TotalSizeIsMultipleOfEightBits> { + marker: std::marker::PhantomData<T>, } -bitfield_structs! { - u64, 33, BitField33 BitField34 BitField35 BitField36 BitField37 BitField38 BitField39 BitField40 BitField41 - BitField42 BitField43 BitField44 BitField45 BitField46 BitField47 BitField48 BitField49 BitField50 - BitField51 BitField52 BitField53 BitField54 BitField55 BitField56 BitField57 BitField58 - BitField59 BitField60 BitField61 BitField62 BitField63 BitField64 +mod checks { + pub trait TotalSizeIsMultipleOfEightBits {} + impl TotalSizeIsMultipleOfEightBits for [u8; 0] {} } |