mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-01-27 02:28:22 +00:00
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>
This commit is contained in:
parent
c13a68eecb
commit
f71764228a
2 changed files with 319 additions and 159 deletions
|
@ -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>());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
// 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.
|
||||
type DefaultFieldType;
|
||||
|
||||
fn from_u64(val: u64) -> Self::DefaultFieldType;
|
||||
fn into_u64(val: Self::DefaultFieldType) -> u64;
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
type DefaultFieldType;
|
||||
// Defines bit_field::BitField0 through bit_field::BitField64.
|
||||
bit_field_derive::define_bit_field_specifiers!();
|
||||
|
||||
/// 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 {
|
||||
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,) => {};
|
||||
|
||||
impl private::Sealed for bool {}
|
||||
|
||||
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! {
|
||||
u8, 0, BitField0 BitField1 BitField2 BitField3 BitField4 BitField5 BitField6 BitField7 BitField8
|
||||
// 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! {
|
||||
u16, 9, BitField9 BitField10 BitField11 BitField12 BitField13 BitField14 BitField15 BitField16
|
||||
}
|
||||
|
||||
bitfield_structs! {
|
||||
u32, 17, BitField17 BitField18 BitField19 BitField20 BitField21 BitField22 BitField23 BitField24
|
||||
BitField25 BitField26 BitField27 BitField28 BitField29 BitField30 BitField31 BitField32
|
||||
}
|
||||
|
||||
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] {}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue