2018-05-22 18:22:13 +00:00
|
|
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#![recursion_limit = "256"]
|
|
|
|
extern crate proc_macro;
|
|
|
|
extern crate proc_macro2;
|
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
extern crate quote;
|
|
|
|
|
2018-11-15 23:09:57 +00:00
|
|
|
#[macro_use]
|
2018-05-22 18:22:13 +00:00
|
|
|
extern crate syn;
|
|
|
|
|
|
|
|
use std::string::String;
|
|
|
|
use std::vec::Vec;
|
|
|
|
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
use proc_macro2::{Span, TokenStream};
|
|
|
|
use syn::{Data, DeriveInput, Fields, Ident};
|
2018-05-22 18:22:13 +00:00
|
|
|
|
|
|
|
type Result<T> = std::result::Result<T, String>;
|
|
|
|
|
|
|
|
/// The function that derives the actual implementation.
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
#[proc_macro_attribute]
|
2018-12-10 22:03:05 +00:00
|
|
|
pub fn bitfield(
|
|
|
|
_args: proc_macro::TokenStream,
|
|
|
|
input: proc_macro::TokenStream,
|
|
|
|
) -> proc_macro::TokenStream {
|
2018-11-15 23:09:57 +00:00
|
|
|
let derive_input = parse_macro_input!(input as DeriveInput);
|
|
|
|
bitfield_impl(derive_input).into()
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:09:57 +00:00
|
|
|
fn bitfield_impl(ast: DeriveInput) -> TokenStream {
|
2018-05-22 18:22:13 +00:00
|
|
|
if !ast.generics.params.is_empty() {
|
|
|
|
return quote! {
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
compile_error!("#[bitfield] does not support generic parameters");
|
2018-05-22 18:22:13 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
let name = ast.ident.clone();
|
2018-07-23 17:24:59 +00:00
|
|
|
let test_mod_ident = Ident::new(
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
format!("test_{}", name.to_string().to_lowercase()).as_str(),
|
2018-07-23 17:24:59 +00:00
|
|
|
Span::call_site(),
|
|
|
|
);
|
2018-05-22 18:22:13 +00:00
|
|
|
let vis = ast.vis.clone();
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
let attrs = ast.attrs.clone();
|
2018-05-22 18:22:13 +00:00
|
|
|
// Visibility.
|
|
|
|
let vis = quote!(#vis);
|
|
|
|
let fields = match get_struct_fields(ast) {
|
|
|
|
Ok(f) => f,
|
|
|
|
Err(err_str) => {
|
|
|
|
return quote! {
|
|
|
|
compile_error!(#err_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let struct_def = get_struct_def(&vis, &name, fields.as_slice());
|
|
|
|
let bits_impl = get_bits_impl(&name);
|
|
|
|
let fields_impl = get_fields_impl(fields.as_slice());
|
|
|
|
let tests_impl = get_tests_impl(&name, fields.as_slice());
|
2018-07-23 17:24:59 +00:00
|
|
|
let debug_fmt_impl = get_debug_fmt_impl(&name, fields.as_slice());
|
2018-11-15 23:16:45 +00:00
|
|
|
quote! {
|
2018-05-22 18:22:13 +00:00
|
|
|
#(#attrs)*
|
|
|
|
#struct_def
|
|
|
|
#bits_impl
|
|
|
|
impl #name {
|
|
|
|
#(#fields_impl)*
|
|
|
|
}
|
2018-07-23 17:24:59 +00:00
|
|
|
|
|
|
|
#debug_fmt_impl
|
2018-05-22 18:22:13 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod #test_mod_ident {
|
|
|
|
use super::*;
|
|
|
|
#(#tests_impl)*
|
|
|
|
}
|
2018-11-15 23:16:45 +00:00
|
|
|
}
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Unwrap ast to get the named fields. Anything unexpected will be treated as an
|
|
|
|
// error.
|
|
|
|
// We only care about field names and types.
|
|
|
|
// "myfield : BitField3" -> ("myfield", Token(BitField3))
|
2018-11-15 23:09:57 +00:00
|
|
|
fn get_struct_fields(ast: DeriveInput) -> Result<Vec<(String, TokenStream)>> {
|
2018-05-22 18:22:13 +00:00
|
|
|
let fields = match ast.data {
|
|
|
|
Data::Struct(data_struct) => match data_struct.fields {
|
|
|
|
Fields::Named(fields_named) => fields_named.named,
|
|
|
|
_ => {
|
|
|
|
return Err(format!("Schema must have named fields."));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
return Err(format!("Schema must be a struct."));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut vec = Vec::new();
|
|
|
|
for field in fields {
|
|
|
|
let ident = match field.ident {
|
|
|
|
Some(ident) => ident,
|
|
|
|
None => {
|
|
|
|
return Err(format!(
|
|
|
|
"Unknown Error. bit_field_derive library might have a bug."
|
|
|
|
))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let ty = field.ty;
|
|
|
|
vec.push((ident.to_string(), quote!(#ty)));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(vec)
|
|
|
|
}
|
|
|
|
|
2018-11-15 23:09:57 +00:00
|
|
|
fn get_struct_def(
|
|
|
|
vis: &TokenStream,
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
name: &Ident,
|
2018-11-15 23:09:57 +00:00
|
|
|
fields: &[(String, TokenStream)],
|
|
|
|
) -> TokenStream {
|
2018-05-22 18:22:13 +00:00
|
|
|
let mut field_types = Vec::new();
|
|
|
|
for &(ref _name, ref ty) in fields {
|
|
|
|
field_types.push(ty.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// It will be something like:
|
|
|
|
// "(BitField1::FIELD_WIDTH + BitField3::FIELD_WIDTH + BitField4::FIELD_WIDTH) / 8)"
|
2018-11-15 23:16:45 +00:00
|
|
|
let data_size_in_bytes = quote! {
|
|
|
|
( #( #field_types::FIELD_WIDTH as usize )+* ) / 8
|
|
|
|
};
|
|
|
|
quote! {
|
2018-05-22 18:22:13 +00:00
|
|
|
#vis struct #name {
|
|
|
|
data: [u8; #data_size_in_bytes],
|
|
|
|
}
|
|
|
|
impl #name {
|
|
|
|
pub fn new() -> #name {
|
2018-11-15 23:16:45 +00:00
|
|
|
#name {
|
|
|
|
data: [0; #data_size_in_bytes],
|
|
|
|
}
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-15 23:16:45 +00:00
|
|
|
}
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Implement setter and getter for all fields.
|
2018-11-15 23:09:57 +00:00
|
|
|
fn get_fields_impl(fields: &[(String, TokenStream)]) -> Vec<TokenStream> {
|
2018-05-22 18:22:13 +00:00
|
|
|
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)];
|
|
|
|
|
|
|
|
for &(ref name, ref ty) in fields {
|
|
|
|
// Creating two copies of current types. As they are going to be moved in quote!.
|
|
|
|
let ct0 = current_types.clone();
|
|
|
|
let ct1 = current_types.clone();
|
|
|
|
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());
|
2018-11-15 23:16:45 +00:00
|
|
|
impls.push(quote! {
|
|
|
|
pub fn #getter_ident(&self) -> <#ty as BitFieldSpecifier>::DefaultFieldType {
|
|
|
|
let offset = #(#ct0::FIELD_WIDTH as usize)+*;
|
|
|
|
self.get(offset, #ty::FIELD_WIDTH) as
|
|
|
|
<#ty as BitFieldSpecifier>::DefaultFieldType
|
|
|
|
}
|
2018-05-22 18:22:13 +00:00
|
|
|
|
2018-11-15 23:16:45 +00:00
|
|
|
pub fn #setter_ident(&mut self, val: <#ty as BitFieldSpecifier>::DefaultFieldType) {
|
|
|
|
debug_assert!((val as u64) <= #ty::FIELD_MAX);
|
|
|
|
let offset = #(#ct1::FIELD_WIDTH as usize)+*;
|
|
|
|
self.set(offset, #ty::FIELD_WIDTH, val as u64)
|
|
|
|
}
|
|
|
|
});
|
2018-05-22 18:22:13 +00:00
|
|
|
current_types.push(ty.clone());
|
|
|
|
}
|
|
|
|
impls
|
|
|
|
}
|
|
|
|
|
2018-07-23 17:24:59 +00:00
|
|
|
// Implement setter and getter for all fields.
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
fn get_debug_fmt_impl(name: &Ident, fields: &[(String, TokenStream)]) -> TokenStream {
|
2018-07-23 17:24:59 +00:00
|
|
|
// print fields:
|
|
|
|
let mut impls = Vec::new();
|
|
|
|
for &(ref name, ref _ty) in fields {
|
|
|
|
let getter_ident = Ident::new(format!("get_{}", name).as_str(), Span::call_site());
|
2018-11-15 23:16:45 +00:00
|
|
|
impls.push(quote! {
|
|
|
|
.field(#name, &self.#getter_ident())
|
|
|
|
});
|
2018-07-23 17:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let name_str = format!("{}", name);
|
2018-11-15 23:16:45 +00:00
|
|
|
quote! {
|
2018-07-23 17:24:59 +00:00
|
|
|
impl std::fmt::Debug for #name {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
f.debug_struct(#name_str)
|
|
|
|
#(#impls)*
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 23:16:45 +00:00
|
|
|
}
|
2018-07-23 17:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Implement test.
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
fn get_tests_impl(struct_name: &Ident, fields: &[(String, TokenStream)]) -> Vec<TokenStream> {
|
2018-05-22 18:22:13 +00:00
|
|
|
let mut field_types = Vec::new();
|
|
|
|
for &(ref _name, ref ty) in fields {
|
|
|
|
field_types.push(ty.clone());
|
|
|
|
}
|
|
|
|
let field_types2 = field_types.clone();
|
|
|
|
let mut impls = Vec::new();
|
2018-11-15 23:16:45 +00:00
|
|
|
impls.push(quote! {
|
|
|
|
#[test]
|
|
|
|
fn test_total_size() {
|
|
|
|
let total_size = #(#field_types::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 mut sum = 0usize;
|
|
|
|
for s in fields_sizes {
|
|
|
|
if sum % 64 == 0 {
|
|
|
|
assert!(s <= 64);
|
|
|
|
} else {
|
|
|
|
if (sum + s) % 64 != 0 {
|
|
|
|
assert_eq!(sum / 64, (sum + s) / 64);
|
|
|
|
}
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
2018-11-15 23:16:45 +00:00
|
|
|
sum += s;
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-15 23:16:45 +00:00
|
|
|
});
|
2018-05-22 18:22:13 +00:00
|
|
|
|
|
|
|
for &(ref name, ref ty) in fields {
|
|
|
|
let testname = Ident::new(
|
|
|
|
format!("test_{}", name.as_str()).as_str(),
|
|
|
|
Span::call_site(),
|
|
|
|
);
|
|
|
|
let getter_ident = Ident::new(format!("get_{}", name.as_str()).as_str(), Span::call_site());
|
|
|
|
let setter_ident = Ident::new(format!("set_{}", name.as_str()).as_str(), Span::call_site());
|
2018-11-15 23:16:45 +00:00
|
|
|
impls.push(quote! {
|
|
|
|
#[test]
|
|
|
|
fn #testname() {
|
|
|
|
let mut a = #struct_name::new();
|
|
|
|
assert_eq!(a.#getter_ident() as u64, 0);
|
|
|
|
a.#setter_ident(#ty::FIELD_MAX as <#ty as BitFieldSpecifier>::DefaultFieldType);
|
|
|
|
assert_eq!(a.#getter_ident() as u64, #ty::FIELD_MAX);
|
|
|
|
}
|
|
|
|
});
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
impls
|
|
|
|
}
|
|
|
|
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
fn get_bits_impl(name: &Ident) -> TokenStream {
|
2018-11-15 23:16:45 +00:00
|
|
|
quote! {
|
2018-05-22 18:22:13 +00:00
|
|
|
impl #name {
|
|
|
|
#[inline]
|
|
|
|
fn check_access(&self, offset: usize, width: u8) {
|
|
|
|
debug_assert!(width <= 64);
|
|
|
|
debug_assert!(offset / 8 < self.data.len());
|
|
|
|
debug_assert!((offset + (width as usize)) <= (self.data.len() * 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn get_bit(&self, offset: usize) -> bool {
|
|
|
|
self.check_access(offset, 1);
|
|
|
|
|
|
|
|
let byte_index = offset / 8;
|
|
|
|
let bit_offset = offset % 8;
|
|
|
|
|
|
|
|
let byte = self.data[byte_index];
|
|
|
|
let mask = 1 << bit_offset;
|
|
|
|
|
|
|
|
byte & mask == mask
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn set_bit(&mut self, offset: usize, val: bool) {
|
|
|
|
self.check_access(offset, 1);
|
|
|
|
|
|
|
|
let byte_index = offset / 8;
|
|
|
|
let bit_offset = offset % 8;
|
|
|
|
|
|
|
|
let byte = &mut self.data[byte_index];
|
|
|
|
let mask = 1 << bit_offset;
|
|
|
|
|
|
|
|
if val {
|
|
|
|
*byte |= mask;
|
|
|
|
} else {
|
|
|
|
*byte &= !mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn get(&self, offset: usize, width: u8) -> u64 {
|
|
|
|
self.check_access(offset, width);
|
|
|
|
let mut val = 0;
|
|
|
|
|
|
|
|
for i in 0..(width as usize) {
|
|
|
|
if self.get_bit(i + offset) {
|
|
|
|
val |= 1 << i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn set(&mut self, offset: usize, width: u8, val: u64) {
|
|
|
|
self.check_access(offset, width);
|
|
|
|
|
|
|
|
for i in 0..(width as usize) {
|
|
|
|
let mask = 1 << i;
|
|
|
|
let val_bit_is_set = val & mask == mask;
|
|
|
|
self.set_bit(i + offset, val_bit_is_set);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 23:16:45 +00:00
|
|
|
}
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn end_to_end() {
|
|
|
|
let input: DeriveInput = parse_quote! {
|
macros: Clean up bitfield macro by replacing the input item
Before:
#[derive(BitField)]
#[passthrough(derive(Clone, Copy, PartialEq))]
pub struct TrbSchema {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
After:
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
parameter: B64,
status: B32,
cycle: B1,
flags: B9,
trb_type: B6,
control: B16,
}
This change eliminates the need for the `passthrough` attribute, and
avoids the separate `FooSchema` struct continuing to float around and
disrupt IDE autocomplete.
TEST=`cargo test` the bit_field_derive crate
TEST=`cargo check` the devices crate against a migrated CL:1144264
Change-Id: I950ce896607468c73852aa181827f1a5dc0f0227
Reviewed-on: https://chromium-review.googlesource.com/1366539
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
2018-12-06 23:50:29 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
struct MyBitField {
|
2018-05-22 18:22:13 +00:00
|
|
|
a: BitField1,
|
|
|
|
b: BitField2,
|
|
|
|
c: BitField5,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let expected = quote! {
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct MyBitField {
|
|
|
|
data: [u8; (BitField1::FIELD_WIDTH as usize
|
|
|
|
+ BitField2::FIELD_WIDTH as usize
|
|
|
|
+ BitField5::FIELD_WIDTH as usize) / 8],
|
|
|
|
}
|
|
|
|
impl MyBitField {
|
|
|
|
pub fn new() -> MyBitField {
|
|
|
|
MyBitField {
|
|
|
|
data: [0; (BitField1::FIELD_WIDTH as usize
|
|
|
|
+ BitField2::FIELD_WIDTH as usize
|
|
|
|
+ BitField5::FIELD_WIDTH as usize) / 8],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl MyBitField {
|
|
|
|
#[inline]
|
|
|
|
fn check_access(&self, offset: usize, width: u8) {
|
|
|
|
debug_assert!(width <= 64);
|
|
|
|
debug_assert!(offset / 8 < self.data.len());
|
|
|
|
debug_assert!((offset + (width as usize)) <= (self.data.len() * 8));
|
|
|
|
}
|
|
|
|
#[inline]
|
|
|
|
pub fn get_bit(&self, offset: usize) -> bool {
|
|
|
|
self.check_access(offset, 1);
|
|
|
|
let byte_index = offset / 8;
|
|
|
|
let bit_offset = offset % 8;
|
|
|
|
let byte = self.data[byte_index];
|
|
|
|
let mask = 1 << bit_offset;
|
|
|
|
byte & mask == mask
|
|
|
|
}
|
|
|
|
#[inline]
|
|
|
|
pub fn set_bit(&mut self, offset: usize, val: bool) {
|
|
|
|
self.check_access(offset, 1);
|
|
|
|
let byte_index = offset / 8;
|
|
|
|
let bit_offset = offset % 8;
|
|
|
|
let byte = &mut self.data[byte_index];
|
|
|
|
let mask = 1 << bit_offset;
|
|
|
|
if val {
|
|
|
|
*byte |= mask;
|
|
|
|
} else {
|
|
|
|
*byte &= !mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[inline]
|
|
|
|
pub fn get(&self, offset: usize, width: u8) -> u64 {
|
|
|
|
self.check_access(offset, width);
|
|
|
|
let mut val = 0;
|
|
|
|
for i in 0..(width as usize) {
|
|
|
|
if self.get_bit(i + offset) {
|
|
|
|
val |= 1 << i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val
|
|
|
|
}
|
|
|
|
#[inline]
|
|
|
|
pub fn set(&mut self, offset: usize, width: u8, val: u64) {
|
|
|
|
self.check_access(offset, width);
|
|
|
|
for i in 0..(width as usize) {
|
|
|
|
let mask = 1 << i;
|
|
|
|
let val_bit_is_set = val & mask == mask;
|
|
|
|
self.set_bit(i + offset, val_bit_is_set);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl MyBitField {
|
|
|
|
pub fn get_a(&self) -> <BitField1 as BitFieldSpecifier>::DefaultFieldType {
|
|
|
|
let offset = BitField0::FIELD_WIDTH as usize;
|
2018-11-15 23:16:45 +00:00
|
|
|
self.get(offset, BitField1::FIELD_WIDTH)
|
|
|
|
as <BitField1 as BitFieldSpecifier>::DefaultFieldType
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
pub fn set_a(&mut self, val: <BitField1 as BitFieldSpecifier>::DefaultFieldType) {
|
|
|
|
debug_assert!((val as u64) <= BitField1::FIELD_MAX);
|
|
|
|
let offset = BitField0::FIELD_WIDTH as usize;
|
2018-11-15 23:16:45 +00:00
|
|
|
self.set(offset, BitField1::FIELD_WIDTH, val as u64)
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
pub fn get_b(&self) -> <BitField2 as BitFieldSpecifier>::DefaultFieldType {
|
|
|
|
let offset = BitField0::FIELD_WIDTH as usize + BitField1::FIELD_WIDTH as usize;
|
2018-11-15 23:16:45 +00:00
|
|
|
self.get(offset, BitField2::FIELD_WIDTH)
|
|
|
|
as <BitField2 as BitFieldSpecifier>::DefaultFieldType
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
pub fn set_b(&mut self, val: <BitField2 as BitFieldSpecifier>::DefaultFieldType) {
|
|
|
|
debug_assert!((val as u64) <= BitField2::FIELD_MAX);
|
|
|
|
let offset = BitField0::FIELD_WIDTH as usize + BitField1::FIELD_WIDTH as usize;
|
2018-11-15 23:16:45 +00:00
|
|
|
self.set(offset, BitField2::FIELD_WIDTH, val as u64)
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
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;
|
2018-11-15 23:16:45 +00:00
|
|
|
self.get(offset, BitField5::FIELD_WIDTH)
|
|
|
|
as <BitField5 as BitFieldSpecifier>::DefaultFieldType
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
pub fn set_c(&mut self, val: <BitField5 as BitFieldSpecifier>::DefaultFieldType) {
|
|
|
|
debug_assert!((val as u64) <= BitField5::FIELD_MAX);
|
|
|
|
let offset = BitField0::FIELD_WIDTH as usize
|
|
|
|
+ BitField1::FIELD_WIDTH as usize
|
|
|
|
+ BitField2::FIELD_WIDTH as usize;
|
2018-11-15 23:16:45 +00:00
|
|
|
self.set(offset, BitField5::FIELD_WIDTH, val as u64)
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-23 17:24:59 +00:00
|
|
|
|
|
|
|
impl std::fmt::Debug for MyBitField {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
f.debug_struct("MyBitField")
|
|
|
|
.field("a", &self.get_a())
|
|
|
|
.field("b", &self.get_b())
|
|
|
|
.field("c", &self.get_c())
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
2018-05-22 18:22:13 +00:00
|
|
|
#[cfg(test)]
|
2018-07-23 17:24:59 +00:00
|
|
|
mod test_mybitfield {
|
2018-05-22 18:22:13 +00:00
|
|
|
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;
|
|
|
|
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
|
|
|
|
];
|
|
|
|
let mut sum = 0usize;
|
|
|
|
for s in fields_sizes {
|
|
|
|
if sum % 64 == 0 {
|
|
|
|
assert!(s <= 64);
|
|
|
|
} else {
|
|
|
|
if (sum + s) % 64 != 0 {
|
|
|
|
assert_eq!(sum / 64, (sum + s) / 64);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sum += s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_a() {
|
|
|
|
let mut a = MyBitField::new();
|
|
|
|
assert_eq!(a.get_a() as u64, 0);
|
|
|
|
a.set_a(BitField1::FIELD_MAX as
|
|
|
|
<BitField1 as BitFieldSpecifier>::DefaultFieldType);
|
|
|
|
assert_eq!(a.get_a() as u64, BitField1::FIELD_MAX);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_b() {
|
|
|
|
let mut a = MyBitField::new();
|
|
|
|
assert_eq!(a.get_b() as u64, 0);
|
|
|
|
a.set_b(BitField2::FIELD_MAX as
|
|
|
|
<BitField2 as BitFieldSpecifier>::DefaultFieldType);
|
|
|
|
assert_eq!(a.get_b() as u64, BitField2::FIELD_MAX);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_c() {
|
|
|
|
let mut a = MyBitField::new();
|
|
|
|
assert_eq!(a.get_c() as u64, 0);
|
|
|
|
a.set_c(BitField5::FIELD_MAX as
|
|
|
|
<BitField5 as BitFieldSpecifier>::DefaultFieldType);
|
|
|
|
assert_eq!(a.get_c() as u64, BitField5::FIELD_MAX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-11-15 23:09:57 +00:00
|
|
|
assert_eq!(bitfield_impl(input).to_string(), expected.to_string());
|
2018-05-22 18:22:13 +00:00
|
|
|
}
|
|
|
|
}
|