From f71764228adea535188cf37a521b5b673a107b5d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 18 Dec 2018 16:26:50 -0800 Subject: 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 Tested-by: David Tolnay Reviewed-by: Jingkui Wang --- bit_field/bit_field_derive/bit_field_derive.rs | 221 ++++++++++++++-------- bit_field/src/lib.rs | 251 +++++++++++++++++-------- 2 files changed, 316 insertions(+), 156 deletions(-) (limited to 'bit_field') 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 { 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 { 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; (::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize) / 8], } impl MyBitField { pub fn new() -> MyBitField { + let _: ::bit_field::Check<[ + u8; + (::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize + + ::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; (::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize) / 8], } } @@ -402,42 +467,44 @@ mod tests { } } impl MyBitField { - pub fn get_a(&self) -> ::DefaultFieldType { - let offset = BitField0::FIELD_WIDTH as usize; - let val = self.get(offset, BitField1::FIELD_WIDTH); - ::from_u64(val) + pub fn get_a(&self) -> ::DefaultFieldType { + let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize; + let val = self.get(offset, ::FIELD_WIDTH); + ::from_u64(val) } - pub fn set_a(&mut self, val: ::DefaultFieldType) { - let val = ::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: ::DefaultFieldType) { + let val = ::into_u64(val); + debug_assert!(val <= ::bit_field::max::()); + let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize; + self.set(offset, ::FIELD_WIDTH, val) } - pub fn get_b(&self) -> ::DefaultFieldType { - let offset = BitField0::FIELD_WIDTH as usize + BitField1::FIELD_WIDTH as usize; - let val = self.get(offset, BitField2::FIELD_WIDTH); - ::from_u64(val) + pub fn get_b(&self) -> ::DefaultFieldType { + let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize; + let val = self.get(offset, ::FIELD_WIDTH); + ::from_u64(val) } - pub fn set_b(&mut self, val: ::DefaultFieldType) { - let val = ::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: ::DefaultFieldType) { + let val = ::into_u64(val); + debug_assert!(val <= ::bit_field::max::()); + let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize; + self.set(offset, ::FIELD_WIDTH, val) } - pub fn get_c(&self) -> ::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); - ::from_u64(val) + pub fn get_c(&self) -> ::DefaultFieldType { + let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize; + let val = self.get(offset, ::FIELD_WIDTH); + ::from_u64(val) } - pub fn set_c(&mut self, val: ::DefaultFieldType) { - let val = ::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: ::DefaultFieldType) { + let val = ::into_u64(val); + debug_assert!(val <= ::bit_field::max::()); + let offset = <::bit_field::BitField0 as ::bit_field::BitFieldSpecifier>::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize; + self.set(offset, ::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 = ::FIELD_WIDTH as usize + + ::FIELD_WIDTH as usize + + ::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 + ::FIELD_WIDTH as usize, + ::FIELD_WIDTH as usize, + ::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 = ::into_u64(a.get_a()); + let val = ::into_u64(a.get_a()); assert_eq!(val, 0); - let val = ::from_u64(BitField1::field_max()); + let val = ::from_u64(::bit_field::max::()); a.set_a(val); - let val = ::into_u64(a.get_a()); - assert_eq!(val, BitField1::field_max()); + let val = ::into_u64(a.get_a()); + assert_eq!(val, ::bit_field::max::()); } #[test] fn test_b() { let mut a = MyBitField::new(); - let val = ::into_u64(a.get_b()); + let val = ::into_u64(a.get_b()); assert_eq!(val, 0); - let val = ::from_u64(BitField2::field_max()); + let val = ::from_u64(::bit_field::max::()); a.set_b(val); - let val = ::into_u64(a.get_b()); - assert_eq!(val, BitField2::field_max()); + let val = ::into_u64(a.get_b()); + assert_eq!(val, ::bit_field::max::()); } #[test] fn test_c() { let mut a = MyBitField::new(); - let val = ::into_u64(a.get_c()); + let val = ::into_u64(a.get_c()); assert_eq!(val, 0); - let val = ::from_u64(BitField5::field_max()); + let val = ::from_u64(::bit_field::max::()); a.set_c(val); - let val = ::into_u64(a.get_c()); - assert_eq!(val, BitField5::field_max()); + let val = ::into_u64(a.get_c()); + assert_eq!(val, ::bit_field::max::()); } } }; 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() -> 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 { + marker: std::marker::PhantomData, } -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] {} } -- cgit 1.4.1