summary refs log tree commit diff
path: root/bit_field
diff options
context:
space:
mode:
authorJingkui Wang <jkwang@google.com>2019-03-29 11:16:16 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-04-03 18:13:52 -0700
commit1612ff7a83ad27837d0ce19cc4558da09e20d4e6 (patch)
treef5aaafdb5a0810fb422aa62845943d5e3967dd65 /bit_field
parent6bfc6a0304910701e55e56c4feaf0d73c07d33e5 (diff)
downloadcrosvm-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.rs113
-rw-r--r--bit_field/src/lib.rs24
-rw-r--r--bit_field/tests/test_tuple_struct.rs25
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));
+}