diff options
author | Jingkui Wang <jkwang@google.com> | 2019-03-29 11:16:16 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-04-03 18:13:52 -0700 |
commit | 1612ff7a83ad27837d0ce19cc4558da09e20d4e6 (patch) | |
tree | f5aaafdb5a0810fb422aa62845943d5e3967dd65 /bit_field | |
parent | 6bfc6a0304910701e55e56c4feaf0d73c07d33e5 (diff) | |
download | crosvm-1612ff7a83ad27837d0ce19cc4558da09e20d4e6.tar crosvm-1612ff7a83ad27837d0ce19cc4558da09e20d4e6.tar.gz crosvm-1612ff7a83ad27837d0ce19cc4558da09e20d4e6.tar.bz2 crosvm-1612ff7a83ad27837d0ce19cc4558da09e20d4e6.tar.lz crosvm-1612ff7a83ad27837d0ce19cc4558da09e20d4e6.tar.xz crosvm-1612ff7a83ad27837d0ce19cc4558da09e20d4e6.tar.zst crosvm-1612ff7a83ad27837d0ce19cc4558da09e20d4e6.zip |
improve bitfield type safety by allowing tuple struct field
user-defined tuple struct could be used to improve type safety. TEST=cargo test BUG=None Change-Id: I8ce10fc51b79c277ab23029513b707f3dd621af5 Reviewed-on: https://chromium-review.googlesource.com/1546432 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: David Tolnay <dtolnay@chromium.org>
Diffstat (limited to 'bit_field')
-rw-r--r-- | bit_field/bit_field_derive/bit_field_derive.rs | 113 | ||||
-rw-r--r-- | bit_field/src/lib.rs | 24 | ||||
-rw-r--r-- | bit_field/tests/test_tuple_struct.rs | 25 |
3 files changed, 148 insertions, 14 deletions
diff --git a/bit_field/bit_field_derive/bit_field_derive.rs b/bit_field/bit_field_derive/bit_field_derive.rs index a0938a1..20b5b68 100644 --- a/bit_field/bit_field_derive/bit_field_derive.rs +++ b/bit_field/bit_field_derive/bit_field_derive.rs @@ -15,8 +15,8 @@ extern crate syn; use proc_macro2::{Span, TokenStream}; use syn::parse::{Error, Result}; use syn::{ - Attribute, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident, Lit, LitInt, Meta, Type, - Visibility, + Attribute, Data, DataEnum, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Ident, Lit, LitInt, + Meta, Type, Visibility, }; /// The function that derives the actual implementation. @@ -52,9 +52,10 @@ fn bitfield_impl(ast: &DeriveInput) -> Result<TokenStream> { match &ast.data { Data::Struct(data_struct) => match &data_struct.fields { Fields::Named(fields_named) => bitfield_struct_impl(ast, fields_named), - _ => Err(Error::new( + Fields::Unnamed(fields_unnamed) => bitfield_tuple_struct_impl(ast, fields_unnamed), + Fields::Unit => Err(Error::new( Span::call_site(), - "#[bitfield] schema must have named fields", + "#[bitfield] does not work with unit struct", )), }, Data::Enum(data_enum) => bitfield_enum_impl(ast, data_enum), @@ -65,22 +66,88 @@ fn bitfield_impl(ast: &DeriveInput) -> Result<TokenStream> { } } -fn bitfield_enum_impl(ast: &DeriveInput, data: &DataEnum) -> Result<TokenStream> { +fn bitfield_tuple_struct_impl(ast: &DeriveInput, fields: &FieldsUnnamed) -> Result<TokenStream> { let mut ast = ast.clone(); - let mut width = None; - let mut bits_idx = 0; - - for (i, attr) in ast.attrs.iter().enumerate() { - if let Some(w) = try_parse_bits_attr(attr)? { - bits_idx = i; - width = Some(w); + let width = match parse_remove_bits_attr(&mut ast)? { + Some(w) => w, + None => { + return Err(Error::new( + Span::call_site(), + "tuple struct field must have bits attribute", + )); } + }; + + let ident = &ast.ident; + + if width.value() > 64 { + return Err(Error::new( + Span::call_site(), + "max width of bitfield field is 64", + )); } - if width.is_some() { - ast.attrs.remove(bits_idx); + let bits = width.value() as u8; + + if fields.unnamed.len() != 1 { + return Err(Error::new( + Span::call_site(), + "tuple struct field must have exactly 1 field", + )); } + let field_type = match fields.unnamed.first().unwrap().value().ty { + Type::Path(ref t) => t, + _ => { + return Err(Error::new( + Span::call_site(), + "tuple struct field must have primitive field", + )); + } + }; + let span = field_type + .path + .segments + .first() + .unwrap() + .value() + .ident + .span(); + + let from_u64 = quote_spanned! { + span => val as #field_type + }; + + let into_u64 = quote_spanned! { + span => val.0 as u64 + }; + + let expanded = quote! { + #ast + + impl bit_field::BitFieldSpecifier for #ident { + const FIELD_WIDTH: u8 = #bits; + type SetterType = Self; + type GetterType = Self; + + #[inline] + fn from_u64(val: u64) -> Self::GetterType { + Self(#from_u64) + } + + #[inline] + fn into_u64(val: Self::SetterType) -> u64 { + #into_u64 + } + } + }; + + Ok(expanded) +} + +fn bitfield_enum_impl(ast: &DeriveInput, data: &DataEnum) -> Result<TokenStream> { + let mut ast = ast.clone(); + let width = parse_remove_bits_attr(&mut ast)?; match width { None => bitfield_enum_without_width_impl(&ast, data), Some(width) => bitfield_enum_with_width_impl(&ast, data, &width), @@ -346,6 +413,24 @@ fn try_parse_bits_attr(attr: &Attribute) -> Result<Option<LitInt>> { Ok(None) } +fn parse_remove_bits_attr(ast: &mut DeriveInput) -> Result<Option<LitInt>> { + let mut width = None; + let mut bits_idx = 0; + + for (i, attr) in ast.attrs.iter().enumerate() { + if let Some(w) = try_parse_bits_attr(attr)? { + bits_idx = i; + width = Some(w); + } + } + + if width.is_some() { + ast.attrs.remove(bits_idx); + } + + Ok(width) +} + fn get_struct_def(vis: &Visibility, name: &Ident, fields: &[FieldSpec]) -> TokenStream { let mut field_types = Vec::new(); for spec in fields { diff --git a/bit_field/src/lib.rs b/bit_field/src/lib.rs index 64a309b..bd11c3c 100644 --- a/bit_field/src/lib.rs +++ b/bit_field/src/lib.rs @@ -101,6 +101,30 @@ //! } //! ``` //! +//! Fields may be user-defined single element tuple struct with primitive types. Use must specify +//! the width with `#[bits = N]`. This should be used to improve type safety. +//! +//! ``` +//! extern crate bit_field; +//! +//! use bit_field::*; +//! +//! #[bitfield] +//! #[bits = 60] +//! struct AddressField(u64); +//! +//! impl AddressField { +//! pub fn new(addr: u64) -> AddressField { +//! AddressField(addr >> 4) +//! } +//! +//! pub fn get_addr(&self) -> u64 { +//! self.0 << 4 +//! } +//! } +//! +//! ``` +//! //! Finally, fields may be of user-defined enum types. The enum must satisfy one of the following //! requirements. //! diff --git a/bit_field/tests/test_tuple_struct.rs b/bit_field/tests/test_tuple_struct.rs new file mode 100644 index 0000000..3b12735 --- /dev/null +++ b/bit_field/tests/test_tuple_struct.rs @@ -0,0 +1,25 @@ +extern crate bit_field; + +use bit_field::*; + +#[bitfield] +#[bits = 5] +#[derive(Debug, PartialEq)] +pub struct FiveBits(u8); + +#[bitfield] +struct Struct { + prefix: BitField1, + five_bits: FiveBits, + suffix: BitField2, +} + +#[test] +fn test_enum() { + let mut s = Struct::new(); + assert_eq!(s.get(0, 8), 0b_0000_0000); + + s.set_five_bits(FiveBits(0b10101)); + assert_eq!(s.get(0, 8), 0b_0010_1010); + assert_eq!(s.get_five_bits(), FiveBits(0b10101)); +} |