From 7dfed35cab77fdb3be2f0da5c9c6d57fe4d5e9f4 Mon Sep 17 00:00:00 2001 From: Dirk Van Haerenborgh Date: Sun, 27 Nov 2022 17:09:53 +0100 Subject: [PATCH] introduce enum for endianness --- impl/src/bitfield/config.rs | 61 ++++++++++++++++++++++++++++++++-- impl/src/bitfield/mod.rs | 2 ++ impl/src/bitfield/params.rs | 26 +++++++++++++++ impl/src/bitfield_specifier.rs | 38 ++++++++++++--------- impl/src/lib.rs | 2 +- 5 files changed, 110 insertions(+), 19 deletions(-) diff --git a/impl/src/bitfield/config.rs b/impl/src/bitfield/config.rs index ef7c509..1bb2198 100644 --- a/impl/src/bitfield/config.rs +++ b/impl/src/bitfield/config.rs @@ -4,9 +4,12 @@ use super::field_config::FieldConfig; use crate::errors::CombineError; use core::any::TypeId; use proc_macro2::Span; -use std::collections::{ - hash_map::Entry, - HashMap, +use std::{ + collections::{ + hash_map::Entry, + HashMap, + }, + convert::TryFrom, fmt, }; use syn::parse::Result; @@ -16,6 +19,7 @@ pub struct Config { pub bytes: Option>, pub bits: Option>, pub filled: Option>, + pub endian: Option>, pub repr: Option>, pub derive_debug: Option>, pub derive_specifier: Option>, @@ -57,6 +61,42 @@ impl core::fmt::Debug for ReprKind { } } +/// Types of endiannes for a `#[bitfield]` struct. +#[derive(Debug, Copy, Clone)] +pub enum Endian { + Little, + Big, + Native, +} + +impl TryFrom for Endian { + type Error = ::syn::Error; + + fn try_from(value: String) -> std::result::Result { + match value.as_str() { + "big" => Ok(Endian::Big), + "little" => Ok(Endian::Little), + "native" => Ok(Endian::Native), + invalid => { + Err(format_err!( + invalid, + "encountered invalid value argument for #[bitfield] `endian` parameter", + )) + } + } + } +} + +impl fmt::Display for Endian { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", match self { + Endian::Big => "big", + Endian::Little => "little", + Endian::Native => "native" + }) + } +} + /// A configuration value and its originating span. #[derive(Clone)] pub struct ConfigValue { @@ -234,6 +274,21 @@ impl Config { Ok(()) } + /// Sets the `endian: str` #[bitfield] parameter to the given value. + /// + /// # Errors + /// + /// If the specifier has already been set. + pub fn endian(&mut self, value: Endian, span: Span) -> Result<()> { + match &self.endian { + Some(previous) => { + return Err(Self::raise_duplicate_error("endian", span, previous)) + } + None => self.endian = Some(ConfigValue::new(value, span)), + } + Ok(()) + } + /// Registers the `#[repr(uN)]` attribute for the #[bitfield] macro. /// /// # Errors diff --git a/impl/src/bitfield/mod.rs b/impl/src/bitfield/mod.rs index 4aee306..d727119 100644 --- a/impl/src/bitfield/mod.rs +++ b/impl/src/bitfield/mod.rs @@ -16,6 +16,8 @@ use syn::{ parse::Result, }; +pub use config::Endian; + /// Analyzes the given token stream for `#[bitfield]` properties and expands code if valid. pub fn analyse_and_expand(args: TokenStream2, input: TokenStream2) -> TokenStream2 { match analyse_and_expand_or_error(args, input) { diff --git a/impl/src/bitfield/params.rs b/impl/src/bitfield/params.rs index 4bdc5ad..218d7c2 100644 --- a/impl/src/bitfield/params.rs +++ b/impl/src/bitfield/params.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use super::config::Config; use proc_macro2::Span; use syn::{ @@ -112,6 +114,28 @@ impl Config { Ok(()) } + /// Feeds a `endian: int` parameter to the `#[bitfield]` configuration. + fn feed_endian_param(&mut self, name_value: syn::MetaNameValue) -> Result<()> { + assert!(name_value.path.is_ident("endian")); + match &name_value.lit { + syn::Lit::Str(lit_str) => { + let endian = match lit_str.value().try_into() { + Ok(endian) => endian, + Err(err) => return Err(err), + }; + + self.endian(endian, name_value.span())?; + } + invalid => { + return Err(format_err!( + invalid, + "encountered invalid value argument for #[bitfield] `endian` parameter", + )) + } + } + Ok(()) + } + /// Feeds the given parameters to the `#[bitfield]` configuration. /// /// # Errors @@ -132,6 +156,8 @@ impl Config { self.feed_bits_param(name_value)?; } else if name_value.path.is_ident("filled") { self.feed_filled_param(name_value)?; + } else if name_value.path.is_ident("endian") { + self.feed_endian_param(name_value)?; } else { return Err(unsupported_argument(name_value)) } diff --git a/impl/src/bitfield_specifier.rs b/impl/src/bitfield_specifier.rs index 7f618d5..998a92d 100644 --- a/impl/src/bitfield_specifier.rs +++ b/impl/src/bitfield_specifier.rs @@ -1,6 +1,9 @@ +use std::convert::TryInto; + use proc_macro2::TokenStream as TokenStream2; -use quote::quote_spanned; +use quote::{quote, quote_spanned}; use syn::spanned::Spanned as _; +use crate::bitfield::Endian; pub fn generate(input: TokenStream2) -> TokenStream2 { match generate_or_error(input) { @@ -39,7 +42,7 @@ fn generate_or_error(input: TokenStream2) -> syn::Result { } struct Attributes { bits: Option, - endian: Option, // TODO switch to enum + endian: Option, } fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result { @@ -80,9 +83,9 @@ fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result { let meta = attr.parse_meta()?; attributes.endian = match meta { syn::Meta::NameValue(syn::MetaNameValue { - lit: syn::Lit::Int(lit), + lit: syn::Lit::Str(lit), .. - }) => Some(lit.base10_parse::()?), + }) => Some(lit.value().try_into()?), _ => { return Err(format_err_spanned!( attr, @@ -127,7 +130,7 @@ fn generate_enum(input: syn::ItemEnum) -> syn::Result { let endian = match attributes.endian { Some(endian) => endian, // 1 big, 2 little - None => 0, // Default to host endian + None => Endian::Native // Default to host endian }; println!("{} endian {}", enum_ident, endian); @@ -160,6 +163,19 @@ fn generate_enum(input: syn::ItemEnum) -> syn::Result { ) }); + let endian_to = match endian { + Endian::Big => quote!{ (input as Self::Bytes).to_be() }, + Endian::Little => quote!{ (input as Self::Bytes).to_le() }, + _ => quote!{ (input as Self::Bytes)}, + }; + + + let endian_from = match endian { + Endian::Big => quote!{ Self::Bytes::from_be() }, + Endian::Little => quote!{ Self::Bytes::from_le() }, + _ => quote!{ bytes }, + }; + Ok(quote_spanned!(span=> #( #check_discriminants )* @@ -170,21 +186,13 @@ fn generate_enum(input: syn::ItemEnum) -> syn::Result { #[inline] fn into_bytes(input: Self::InOut) -> ::core::result::Result { - let bytes = match #endian { - 1 => (input as Self::Bytes).to_be(), - 2 => (input as Self::Bytes).to_le(), - _ => input as Self::Bytes, - }; + let bytes = #endian_to; return ::core::result::Result::Ok(bytes); } #[inline] fn from_bytes(bytes: Self::Bytes) -> ::core::result::Result> { - let bytes = match #endian { - 1 => Self::Bytes::from_be(bytes), - 2 => Self::Bytes::from_le(bytes), - _ => bytes, - }; + let bytes = #endian_from; match bytes { #( #from_bytes_arms ),* diff --git a/impl/src/lib.rs b/impl/src/lib.rs index b2aa6ec..7ba8adb 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -432,7 +432,7 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream { /// assert_eq!(slot.to(), 15); /// assert!(!slot.expired()); /// ``` -#[proc_macro_derive(BitfieldSpecifier, attributes(bits, endian))] +#[proc_macro_derive(BitfieldSpecifier, attributes(bits))] pub fn bitfield_specifier(input: TokenStream) -> TokenStream { bitfield_specifier::generate(input.into()).into() }