summary refs log tree commit diff
path: root/bit_field/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'bit_field/src/lib.rs')
-rw-r--r--bit_field/src/lib.rs251
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] {}
 }