#![no_std]
use core::{
ops::{ Shl, Shr, BitAnd, BitOrAssign, BitXorAssign },
fmt::{ Debug, Display }
};
#[doc(hidden)]
pub use static_assertions::const_assert;
pub trait Bitfield {
type BaseType: Copy + Debug + Display;
const MAX_BITS: u8 = 8 * core::mem::size_of::<Self::BaseType>() as u8;
}
pub trait Field<B: Bitfield>
where B::BaseType:
Shl<u8, Output=B::BaseType> +
Shr<u8, Output=B::BaseType> +
BitAnd<Output=B::BaseType> +
BitOrAssign + BitXorAssign + PartialEq
{
const SIZE: u8;
const OFFSET: u8;
const MASK: B::BaseType;
fn is_set(&self) -> bool;
const VALID: bool = Self::SIZE + Self::OFFSET <= B::MAX_BITS;
fn size(&self) -> u8 { Self::SIZE }
fn offset(&self) -> u8 { Self::OFFSET }
fn mask(&self) -> B::BaseType { Self::MASK }
fn get(&self) -> B::BaseType {
let data_ptr: *const B::BaseType = self as *const Self as *const B::BaseType;
(unsafe { *data_ptr } >> Self::OFFSET) & Self::MASK
}
fn set(&mut self, new_value: B::BaseType) {
let data_ptr: *mut B::BaseType = self as *const Self as *mut B::BaseType;
let old_value: B::BaseType = self.get() << Self::OFFSET;
unsafe {
*data_ptr ^= old_value;
*data_ptr |= (new_value & Self::MASK) << Self::OFFSET
}
}
fn set_checked(&mut self, new_value: B::BaseType) -> Result<(), B::BaseType> {
let masked = new_value & Self::MASK;
if masked != new_value {
Err(masked)
} else {
self.set(masked);
Ok(())
}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! gen_format_debug {
($self:ident) => { format_args!("{}", "") };
($self:ident | $first_field:ident) => {
format_args!(
"{}: {:?}",
stringify!($first_field), $self.$first_field.get()
)
};
($self:ident | $first_field:ident | $second_field:ident $(| $other_field:ident)*) => {
format_args!(
"{}: {:?}, {}",
stringify!($first_field), $self.$first_field.get(),
$crate::gen_format_debug!($self | $second_field $(| $other_field)*)
)
};
}
#[macro_export]
macro_rules! bitfield {
($($(#[$attr:meta])* $visibility:vis struct $bitfield_name:ident < $big_type:ty > { $($field:tt : $size:literal),* })*) => {$(
// Construct the whole module
#[allow(non_snake_case)]
#[allow(dead_code)]
$visibility mod $bitfield_name {
#[repr(transparent)]
#[derive(Copy, Clone)]
$(#[$attr])*
pub struct $bitfield_name($big_type);
impl $crate::Bitfield for $bitfield_name {
type BaseType = $big_type;
}
impl From<$big_type> for $bitfield_name
{
fn from(val: $big_type) -> Self {
Self(val)
}
}
impl From<$bitfield_name> for $big_type {
fn from(val: $bitfield_name) -> Self {
val.0
}
}
pub const fn new(val: $big_type) -> $bitfield_name {
$bitfield_name(val)
}
$crate::bitfield!{
impl
$($field : $size),* end_marker
Fields, $bitfield_name, 0, processed }
$crate::const_assert!(Fields::VALID);
impl core::ops::Deref for $bitfield_name {
type Target = Fields;
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const Self as *const Fields) }
}
}
impl core::ops::DerefMut for $bitfield_name {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self as *mut Self as *mut Fields) }
}
}
}
)*};
(impl end_marker $struct_name:ident, $bitfield_type:ty, $curr_offset:expr, processed $(| $field_processed:ident)*) => {
#[repr(C)]
pub struct $struct_name {
$(pub $field_processed: $field_processed),*
}
impl $struct_name {
const VALID: bool = $(<$field_processed as $crate::Field<$bitfield_type>>::VALID &)* true;
}
impl core::fmt::Display for $bitfield_type {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
write!(f, "{}({})", stringify!($bitfield_type), self.0)
}
}
impl core::fmt::Debug for $bitfield_type {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
use $crate::Field;
write!(f, "{}", format_args!(
"{}({})", stringify!($bitfield_type),
$crate::gen_format_debug!(self $(| $field_processed)*)
))
}
}
};
(impl _ : $size:literal $(, $other_field:tt : $other_size:literal)* end_marker $struct_name:ident, $bitfield_type:ty, $curr_offset:expr, processed $(| $field_processed:ident)*) => {
$crate::bitfield!{
impl
$($other_field : $other_size),* end_marker
$struct_name, $bitfield_type,
$curr_offset + $size,
processed $(| $field_processed)*
}
};
(impl $field:ident : $size:literal $(, $other_field:tt : $other_size:literal)* end_marker $struct_name:ident, $bitfield_type:ty, $curr_offset:expr, processed $(| $field_processed:ident)*) => {
#[allow(non_camel_case_types)]
pub struct $field(());
/*
* `struct thing(())` is a "unit-valued tuple struct",
* basically the same as `struct thing(<any type>)`,
* which can be constucted like `thing(<value of type>)`,
* but the constructor is invisible outside the module.
*
* https: */
#[allow(dead_code)]
impl $crate::Field<$bitfield_type> for $field {
const SIZE: u8 = $size;
const OFFSET: u8 = $curr_offset;
const MASK: <$bitfield_type as $crate::Bitfield>::BaseType = (1 << Self::SIZE) - 1;
#[inline]
fn is_set(&self) -> bool {
self.get() != 0
}
}
$crate::const_assert!(<$field as $crate::Field<$bitfield_type>>::VALID);
$crate::bitfield!{
impl
$($other_field : $other_size),* end_marker $struct_name, $bitfield_type, $curr_offset + $size, processed $(| $field_processed)* | $field
}
}
}
#[cfg(doc)]
bitfield! {
pub struct TestBitfield<u32> {
field_1: 2,
_: 3,
field_2: 5
}
}
#[cfg(test)]
mod tests;