Skip to content

Commit

Permalink
introduce enum for endianness
Browse files Browse the repository at this point in the history
  • Loading branch information
vhdirk committed Nov 27, 2022
1 parent a3f1b50 commit 7dfed35
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 19 deletions.
61 changes: 58 additions & 3 deletions impl/src/bitfield/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -16,6 +19,7 @@ pub struct Config {
pub bytes: Option<ConfigValue<usize>>,
pub bits: Option<ConfigValue<usize>>,
pub filled: Option<ConfigValue<bool>>,
pub endian: Option<ConfigValue<Endian>>,
pub repr: Option<ConfigValue<ReprKind>>,
pub derive_debug: Option<ConfigValue<()>>,
pub derive_specifier: Option<ConfigValue<()>>,
Expand Down Expand Up @@ -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<String> for Endian {
type Error = ::syn::Error;

fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
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<T> {
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions impl/src/bitfield/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
26 changes: 26 additions & 0 deletions impl/src/bitfield/params.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::convert::TryInto;

use super::config::Config;
use proc_macro2::Span;
use syn::{
Expand Down Expand Up @@ -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
Expand All @@ -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))
}
Expand Down
38 changes: 23 additions & 15 deletions impl/src/bitfield_specifier.rs
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -39,7 +42,7 @@ fn generate_or_error(input: TokenStream2) -> syn::Result<TokenStream2> {
}
struct Attributes {
bits: Option<usize>,
endian: Option<usize>, // TODO switch to enum
endian: Option<Endian>,
}

fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result<Attributes> {
Expand Down Expand Up @@ -80,9 +83,9 @@ fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result<Attributes> {
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::<usize>()?),
}) => Some(lit.value().try_into()?),
_ => {
return Err(format_err_spanned!(
attr,
Expand Down Expand Up @@ -127,7 +130,7 @@ fn generate_enum(input: syn::ItemEnum) -> syn::Result<TokenStream2> {

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);
Expand Down Expand Up @@ -160,6 +163,19 @@ fn generate_enum(input: syn::ItemEnum) -> syn::Result<TokenStream2> {
)
});

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 )*

Expand All @@ -170,21 +186,13 @@ fn generate_enum(input: syn::ItemEnum) -> syn::Result<TokenStream2> {

#[inline]
fn into_bytes(input: Self::InOut) -> ::core::result::Result<Self::Bytes, ::modular_bitfield::error::OutOfBounds> {
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<Self::InOut, ::modular_bitfield::error::InvalidBitPattern<Self::Bytes>> {
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 ),*
Expand Down
2 changes: 1 addition & 1 deletion impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

0 comments on commit 7dfed35

Please sign in to comment.