summary refs log tree commit diff
path: root/bit_field
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@chromium.org>2018-12-18 16:26:50 -0800
committerchrome-bot <chrome-bot@chromium.org>2019-01-03 15:04:22 -0800
commitf71764228adea535188cf37a521b5b673a107b5d (patch)
tree322d31e6730a31f2f47c34080d6bd40a6d45bc29 /bit_field
parentc13a68eecb6a813a5c979ec52b09e795c1da7493 (diff)
downloadcrosvm-f71764228adea535188cf37a521b5b673a107b5d.tar
crosvm-f71764228adea535188cf37a521b5b673a107b5d.tar.gz
crosvm-f71764228adea535188cf37a521b5b673a107b5d.tar.bz2
crosvm-f71764228adea535188cf37a521b5b673a107b5d.tar.lz
crosvm-f71764228adea535188cf37a521b5b673a107b5d.tar.xz
crosvm-f71764228adea535188cf37a521b5b673a107b5d.tar.zst
crosvm-f71764228adea535188cf37a521b5b673a107b5d.zip
bitfield: Documentation and simplifications
Changes in this CL:

- Crate-level documentation for bit_field crate!

- Use absolute paths within generated code so that the caller is no
  longer required to have various implementation details from the
  bit_field crate in scope.

- Check that the total number of bits is a multiple of 8. Previously, it
  would generate compilable code that panicked when invoking accessors.

- Provide B0 .. B64 as shorthand for BitField0 .. BitField64.

- Use `bool` as the bool specifier rather than BitFieldBool.

- Disallow BitFieldSpecifier impls outside the bit_field crate.

- Simplify declaration of the BitFieldN types by replacing the recursive
  macro_rules with a simpler procedural macro.

TEST=`cargo test` in bit_field and in bit_field_derive

Change-Id: Ica9347bc89901de85f74366edd038fb5d8042ee6
Reviewed-on: https://chromium-review.googlesource.com/1382578
Commit-Ready: David Tolnay <dtolnay@chromium.org>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
Diffstat (limited to 'bit_field')
-rw-r--r--bit_field/bit_field_derive/bit_field_derive.rs221
-rw-r--r--bit_field/src/lib.rs251
2 files changed, 316 insertions, 156 deletions
diff --git a/bit_field/bit_field_derive/bit_field_derive.rs b/bit_field/bit_field_derive/bit_field_derive.rs
index 0f563c4..e3ed2ae 100644
--- a/bit_field/bit_field_derive/bit_field_derive.rs
+++ b/bit_field/bit_field_derive/bit_field_derive.rs
@@ -119,19 +119,27 @@ fn get_struct_def(
         field_types.push(ty.clone());
     }
 
-    // It will be something like:
-    // "(BitField1::FIELD_WIDTH + BitField3::FIELD_WIDTH + BitField4::FIELD_WIDTH) / 8)"
-    let data_size_in_bytes = quote! {
-        ( #( #field_types::FIELD_WIDTH as usize )+* ) / 8
+    // `(BitField1::FIELD_WIDTH + BitField3::FIELD_WIDTH + ...)`
+    let data_size_in_bits = quote! {
+        (
+            #(
+                <#field_types as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+            )+*
+        )
     };
+
     quote! {
+        #[repr(C)]
         #vis struct #name {
-            data: [u8; #data_size_in_bytes],
+            data: [u8; #data_size_in_bits / 8],
         }
+
         impl #name {
             pub fn new() -> #name {
+                let _: ::bit_field::Check<[u8; #data_size_in_bits % 8]>;
+
                 #name {
-                    data: [0; #data_size_in_bytes],
+                    data: [0; #data_size_in_bits / 8],
                 }
             }
         }
@@ -142,7 +150,7 @@ fn get_struct_def(
 fn get_fields_impl(fields: &[(String, TokenStream)]) -> Vec<TokenStream> {
     let mut impls = Vec::new();
     // This vec keeps track of types before this field, used to generate the offset.
-    let mut current_types = vec![quote!(BitField0)];
+    let mut current_types = vec![quote!(::bit_field::BitField0)];
 
     for &(ref name, ref ty) in fields {
         // Creating two copies of current types. As they are going to be moved in quote!.
@@ -151,17 +159,17 @@ fn get_fields_impl(fields: &[(String, TokenStream)]) -> Vec<TokenStream> {
         let getter_ident = Ident::new(format!("get_{}", name).as_str(), Span::call_site());
         let setter_ident = Ident::new(format!("set_{}", name).as_str(), Span::call_site());
         impls.push(quote! {
-            pub fn #getter_ident(&self) -> <#ty as BitFieldSpecifier>::DefaultFieldType {
-                let offset = #(#ct0::FIELD_WIDTH as usize)+*;
-                let val = self.get(offset, #ty::FIELD_WIDTH);
-                <#ty as BitFieldSpecifier>::from_u64(val)
+            pub fn #getter_ident(&self) -> <#ty as ::bit_field::BitFieldSpecifier>::DefaultFieldType {
+                let offset = #(<#ct0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)+*;
+                let val = self.get(offset, <#ty as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+                <#ty as ::bit_field::BitFieldSpecifier>::from_u64(val)
             }
 
-            pub fn #setter_ident(&mut self, val: <#ty as BitFieldSpecifier>::DefaultFieldType) {
-                let val = <#ty as BitFieldSpecifier>::into_u64(val);
-                debug_assert!(val <= #ty::field_max());
-                let offset = #(#ct1::FIELD_WIDTH as usize)+*;
-                self.set(offset, #ty::FIELD_WIDTH, val)
+            pub fn #setter_ident(&mut self, val: <#ty as ::bit_field::BitFieldSpecifier>::DefaultFieldType) {
+                let val = <#ty as ::bit_field::BitFieldSpecifier>::into_u64(val);
+                debug_assert!(val <= ::bit_field::max::<#ty>());
+                let offset = #(<#ct1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)+*;
+                self.set(offset, <#ty as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
             }
         });
         current_types.push(ty.clone());
@@ -203,14 +211,14 @@ fn get_tests_impl(struct_name: &Ident, fields: &[(String, TokenStream)]) -> Vec<
     impls.push(quote! {
         #[test]
         fn test_total_size() {
-            let total_size = #(#field_types::FIELD_WIDTH as usize)+*;
+            let total_size = #(<#field_types as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)+*;
             assert_eq!(total_size % 8, 0);
         }
     });
     impls.push(quote! {
         #[test]
         fn test_bits_boundary() {
-            let fields_sizes = vec![#(#field_types2::FIELD_WIDTH as usize),*];
+            let fields_sizes = vec![#(<#field_types2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize),*];
             let mut sum = 0usize;
             for s in fields_sizes {
                 if sum % 64 == 0 {
@@ -236,14 +244,14 @@ fn get_tests_impl(struct_name: &Ident, fields: &[(String, TokenStream)]) -> Vec<
             #[test]
             fn #testname() {
                 let mut a = #struct_name::new();
-                let val = <#ty as BitFieldSpecifier>::into_u64(a.#getter_ident());
+                let val = <#ty as ::bit_field::BitFieldSpecifier>::into_u64(a.#getter_ident());
                 assert_eq!(val, 0);
 
-                let val = <#ty as BitFieldSpecifier>::from_u64(#ty::field_max());
+                let val = <#ty as ::bit_field::BitFieldSpecifier>::from_u64(::bit_field::max::<#ty>());
                 a.#setter_ident(val);
 
-                let val = <#ty as BitFieldSpecifier>::into_u64(a.#getter_ident());
-                assert_eq!(val, #ty::field_max());
+                let val = <#ty as ::bit_field::BitFieldSpecifier>::into_u64(a.#getter_ident());
+                assert_eq!(val, ::bit_field::max::<#ty>());
             }
         });
     }
@@ -318,6 +326,54 @@ fn get_bits_impl(name: &Ident) -> TokenStream {
     }
 }
 
+// Only intended to be used from the bit_field crate. This macro emits the
+// marker types bit_field::BitField0 through bit_field::BitField64.
+#[proc_macro]
+#[doc(hidden)]
+pub fn define_bit_field_specifiers(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let mut code = TokenStream::new();
+
+    for width in 0u8..=64 {
+        let span = Span::call_site();
+        let long_name = Ident::new(&format!("BitField{}", width), span);
+        let short_name = Ident::new(&format!("B{}", width), span);
+
+        let default_field_type = if width <= 8 {
+            quote!(u8)
+        } else if width <= 16 {
+            quote!(u16)
+        } else if width <= 32 {
+            quote!(u32)
+        } else {
+            quote!(u64)
+        };
+
+        code.extend(quote! {
+            pub struct #long_name;
+            pub use self::#long_name as #short_name;
+
+            impl BitFieldSpecifier for #long_name {
+                const FIELD_WIDTH: u8 = #width;
+                type DefaultFieldType = #default_field_type;
+
+                #[inline]
+                fn from_u64(val: u64) -> Self::DefaultFieldType {
+                    val as Self::DefaultFieldType
+                }
+
+                #[inline]
+                fn into_u64(val: Self::DefaultFieldType) -> u64 {
+                    val as u64
+                }
+            }
+
+            impl private::Sealed for #long_name {}
+        });
+    }
+
+    code.into()
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -335,18 +391,27 @@ mod tests {
 
         let expected = quote! {
             #[derive(Clone)]
+            #[repr(C)]
             struct MyBitField {
-                data: [u8; (BitField1::FIELD_WIDTH as usize
-                            + BitField2::FIELD_WIDTH as usize
-                            + BitField5::FIELD_WIDTH as usize)
+                data: [u8; (<BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                            + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                            + <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)
                     / 8],
             }
             impl MyBitField {
                 pub fn new() -> MyBitField {
+                    let _: ::bit_field::Check<[
+                        u8;
+                        (<BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                                + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                                + <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)
+                            % 8
+                    ]>;
+
                     MyBitField {
-                        data: [0; (BitField1::FIELD_WIDTH as usize
-                                   + BitField2::FIELD_WIDTH as usize
-                                   + BitField5::FIELD_WIDTH as usize)
+                        data: [0; (<BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                                   + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                                   + <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize)
                             / 8],
                     }
                 }
@@ -402,42 +467,44 @@ mod tests {
                 }
             }
             impl MyBitField {
-                pub fn get_a(&self) -> <BitField1 as BitFieldSpecifier>::DefaultFieldType {
-                    let offset = BitField0::FIELD_WIDTH as usize;
-                    let val = self.get(offset, BitField1::FIELD_WIDTH);
-                    <BitField1 as BitFieldSpecifier>::from_u64(val)
+                pub fn get_a(&self) -> <BitField1 as ::bit_field::BitFieldSpecifier>::DefaultFieldType {
+                    let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+                    let val = self.get(offset, <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+                    <BitField1 as ::bit_field::BitFieldSpecifier>::from_u64(val)
                 }
-                pub fn set_a(&mut self, val: <BitField1 as BitFieldSpecifier>::DefaultFieldType) {
-                    let val = <BitField1 as BitFieldSpecifier>::into_u64(val);
-                    debug_assert!(val <= BitField1::field_max());
-                    let offset = BitField0::FIELD_WIDTH as usize;
-                    self.set(offset, BitField1::FIELD_WIDTH, val)
+                pub fn set_a(&mut self, val: <BitField1 as ::bit_field::BitFieldSpecifier>::DefaultFieldType) {
+                    let val = <BitField1 as ::bit_field::BitFieldSpecifier>::into_u64(val);
+                    debug_assert!(val <= ::bit_field::max::<BitField1>());
+                    let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+                    self.set(offset, <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
                 }
-                pub fn get_b(&self) -> <BitField2 as BitFieldSpecifier>::DefaultFieldType {
-                    let offset = BitField0::FIELD_WIDTH as usize + BitField1::FIELD_WIDTH as usize;
-                    let val = self.get(offset, BitField2::FIELD_WIDTH);
-                    <BitField2 as BitFieldSpecifier>::from_u64(val)
+                pub fn get_b(&self) -> <BitField2 as ::bit_field::BitFieldSpecifier>::DefaultFieldType {
+                    let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+                    let val = self.get(offset, <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+                    <BitField2 as ::bit_field::BitFieldSpecifier>::from_u64(val)
                 }
-                pub fn set_b(&mut self, val: <BitField2 as BitFieldSpecifier>::DefaultFieldType) {
-                    let val = <BitField2 as BitFieldSpecifier>::into_u64(val);
-                    debug_assert!(val <= BitField2::field_max());
-                    let offset = BitField0::FIELD_WIDTH as usize + BitField1::FIELD_WIDTH as usize;
-                    self.set(offset, BitField2::FIELD_WIDTH, val)
+                pub fn set_b(&mut self, val: <BitField2 as ::bit_field::BitFieldSpecifier>::DefaultFieldType) {
+                    let val = <BitField2 as ::bit_field::BitFieldSpecifier>::into_u64(val);
+                    debug_assert!(val <= ::bit_field::max::<BitField2>());
+                    let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+                    self.set(offset, <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
                 }
-                pub fn get_c(&self) -> <BitField5 as BitFieldSpecifier>::DefaultFieldType {
-                    let offset = BitField0::FIELD_WIDTH as usize
-                        + BitField1::FIELD_WIDTH as usize
-                        + BitField2::FIELD_WIDTH as usize;
-                    let val = self.get(offset, BitField5::FIELD_WIDTH);
-                    <BitField5 as BitFieldSpecifier>::from_u64(val)
+                pub fn get_c(&self) -> <BitField5 as ::bit_field::BitFieldSpecifier>::DefaultFieldType {
+                    let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+                    let val = self.get(offset, <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH);
+                    <BitField5 as ::bit_field::BitFieldSpecifier>::from_u64(val)
                 }
-                pub fn set_c(&mut self, val: <BitField5 as BitFieldSpecifier>::DefaultFieldType) {
-                    let val = <BitField5 as BitFieldSpecifier>::into_u64(val);
-                    debug_assert!(val <= BitField5::field_max());
-                    let offset = BitField0::FIELD_WIDTH as usize
-                        + BitField1::FIELD_WIDTH as usize
-                        + BitField2::FIELD_WIDTH as usize;
-                    self.set(offset, BitField5::FIELD_WIDTH, val)
+                pub fn set_c(&mut self, val: <BitField5 as ::bit_field::BitFieldSpecifier>::DefaultFieldType) {
+                    let val = <BitField5 as ::bit_field::BitFieldSpecifier>::into_u64(val);
+                    debug_assert!(val <= ::bit_field::max::<BitField5>());
+                    let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
+                    self.set(offset, <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH, val)
                 }
             }
             impl std::fmt::Debug for MyBitField {
@@ -454,17 +521,17 @@ mod tests {
                 use super::*;
                 #[test]
                 fn test_total_size() {
-                    let total_size = BitField1::FIELD_WIDTH as usize
-                        + BitField2::FIELD_WIDTH as usize
-                        + BitField5::FIELD_WIDTH as usize;
+                    let total_size = <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
+                        + <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize;
                     assert_eq!(total_size % 8, 0);
                 }
                 #[test]
                 fn test_bits_boundary() {
                     let fields_sizes = vec![
-                        BitField1::FIELD_WIDTH as usize,
-                        BitField2::FIELD_WIDTH as usize,
-                        BitField5::FIELD_WIDTH as usize
+                        <BitField1 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize,
+                        <BitField2 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize,
+                        <BitField5 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize
                     ];
                     let mut sum = 0usize;
                     for s in fields_sizes {
@@ -481,32 +548,32 @@ mod tests {
                 #[test]
                 fn test_a() {
                     let mut a = MyBitField::new();
-                    let val = <BitField1 as BitFieldSpecifier>::into_u64(a.get_a());
+                    let val = <BitField1 as ::bit_field::BitFieldSpecifier>::into_u64(a.get_a());
                     assert_eq!(val, 0);
-                    let val = <BitField1 as BitFieldSpecifier>::from_u64(BitField1::field_max());
+                    let val = <BitField1 as ::bit_field::BitFieldSpecifier>::from_u64(::bit_field::max::<BitField1>());
                     a.set_a(val);
-                    let val = <BitField1 as BitFieldSpecifier>::into_u64(a.get_a());
-                    assert_eq!(val, BitField1::field_max());
+                    let val = <BitField1 as ::bit_field::BitFieldSpecifier>::into_u64(a.get_a());
+                    assert_eq!(val, ::bit_field::max::<BitField1>());
                 }
                 #[test]
                 fn test_b() {
                     let mut a = MyBitField::new();
-                    let val = <BitField2 as BitFieldSpecifier>::into_u64(a.get_b());
+                    let val = <BitField2 as ::bit_field::BitFieldSpecifier>::into_u64(a.get_b());
                     assert_eq!(val, 0);
-                    let val = <BitField2 as BitFieldSpecifier>::from_u64(BitField2::field_max());
+                    let val = <BitField2 as ::bit_field::BitFieldSpecifier>::from_u64(::bit_field::max::<BitField2>());
                     a.set_b(val);
-                    let val = <BitField2 as BitFieldSpecifier>::into_u64(a.get_b());
-                    assert_eq!(val, BitField2::field_max());
+                    let val = <BitField2 as ::bit_field::BitFieldSpecifier>::into_u64(a.get_b());
+                    assert_eq!(val, ::bit_field::max::<BitField2>());
                 }
                 #[test]
                 fn test_c() {
                     let mut a = MyBitField::new();
-                    let val = <BitField5 as BitFieldSpecifier>::into_u64(a.get_c());
+                    let val = <BitField5 as ::bit_field::BitFieldSpecifier>::into_u64(a.get_c());
                     assert_eq!(val, 0);
-                    let val = <BitField5 as BitFieldSpecifier>::from_u64(BitField5::field_max());
+                    let val = <BitField5 as ::bit_field::BitFieldSpecifier>::from_u64(::bit_field::max::<BitField5>());
                     a.set_c(val);
-                    let val = <BitField5 as BitFieldSpecifier>::into_u64(a.get_c());
-                    assert_eq!(val, BitField5::field_max());
+                    let val = <BitField5 as ::bit_field::BitFieldSpecifier>::into_u64(a.get_c());
+                    assert_eq!(val, ::bit_field::max::<BitField5>());
                 }
             }
         };
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] {}
 }