#[macro_export]
macro_rules! bitstruct {
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident ($base_type:ty) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$(#[$meta])*
#[derive(Copy, Clone, PartialEq, Eq, Default)]
#[derive($crate::bytemuck::Pod, $crate::bytemuck::Zeroable)]
#[repr(transparent)]
$vis struct $struct_name(pub $base_type);
impl core::fmt::Debug for $struct_name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(stringify!($struct_name))
.field("raw", &self.0)
$(
.field(stringify!($field_name), &self.$field_name())
)*
.finish()
}
}
const _: () = {
let _ = <$base_type as $crate::IsValidBaseInt>::ASSERT_VALID;
$crate::bitstruct!(@check_fields $($field_type)*);
#[allow(dead_code)]
const TOTAL_BITS: usize = 0 $( + $bits )*;
assert!(TOTAL_BITS <= <$base_type as $crate::IsValidBaseInt>::MAX_BITS, "Sum of field bits exceeds base type max bits");
};
impl $struct_name {
#[allow(dead_code)]
pub const BITS: usize = <$base_type as $crate::BitLength>::BITS;
$crate::bitstruct!(@impl_getters_setters $base_type, 0, $($field_vis $field_name $field_type $bits)*);
#[inline(always)]
#[allow(dead_code)]
pub const fn to_bits(self) -> $base_type { self.0 }
#[inline(always)]
#[allow(dead_code)]
pub const fn from_bits(val: $base_type) -> Self { Self(val) }
}
};
(@impl_getters_setters $base_type:ty, $shift:expr, ) => {};
(@check_fields ) => {};
(@check_fields $field_type:tt $($rest:tt)*) => {
let _ = <$field_type as $crate::ValidField>::ASSERT_VALID;
$crate::bitstruct!(@check_fields $($rest)*);
};
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident bool $bits:tt $($rest:tt)*) => {
$crate::paste::paste! {
pub const [<$field_name:upper _OFFSET>]: usize = $shift;
pub const [<$field_name:upper _BITS>]: usize = $bits;
#[doc(hidden)]
const [<$field_name:upper _MASK>]: $base_type = ((!0 as <$base_type as $crate::IsValidBaseInt>::Unsigned) >> (<$base_type as $crate::BitLength>::BITS - Self::[<$field_name:upper _BITS>])) as $base_type;
#[allow(dead_code)]
#[inline]
#[doc = concat!("Returns the boolean value mapping to the `", stringify!($field_name), "` flag.")]
$field_vis const fn $field_name(self) -> bool {
((self.0 >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) != 0
}
#[allow(dead_code)]
#[inline]
#[doc = concat!("Inline mutation to set the `", stringify!($field_name), "` flag.")]
$field_vis fn [<set_ $field_name>](&mut self, val: bool) {
let val_masked = val as $base_type;
self.0 = (self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]);
}
#[allow(dead_code)]
#[doc = concat!("Returns a cloned copy of the bitfield with the `", stringify!($field_name), "` flag specified.")]
$field_vis const fn [<with_ $field_name>](self, val: bool) -> Self {
let val_masked = val as $base_type;
Self((self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}
#[allow(dead_code)]
#[doc = concat!("Inline mutation to set the `", stringify!($field_name), "` flag. Returns `Ok(())` since booleans cannot overflow.")]
$field_vis fn [<try_set_ $field_name>](&mut self, val: bool) -> Result<(), $crate::BitstructError> {
self.[<set_ $field_name>](val);
Ok(())
}
#[allow(dead_code)]
#[doc = concat!("Returns a cloned copy of the bitfield with the `", stringify!($field_name), "` flag specified. Returns `Ok(Self)` since booleans cannot overflow.")]
$field_vis const fn [<try_with_ $field_name>](self, val: bool) -> Result<Self, $crate::BitstructError> {
Ok(self.[<with_ $field_name>](val))
}
}
$crate::bitstruct!(@impl_getters_setters $base_type, $shift + $bits, $($rest)*);
};
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident u8 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_int $base_type, $shift, $field_vis $field_name u8 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident u16 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_int $base_type, $shift, $field_vis $field_name u16 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident u32 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_int $base_type, $shift, $field_vis $field_name u32 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident u64 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_int $base_type, $shift, $field_vis $field_name u64 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident u128 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_int $base_type, $shift, $field_vis $field_name u128 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident i8 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_signed_int $base_type, $shift, $field_vis $field_name i8 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident i16 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_signed_int $base_type, $shift, $field_vis $field_name i16 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident i32 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_signed_int $base_type, $shift, $field_vis $field_name i32 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident i64 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_signed_int $base_type, $shift, $field_vis $field_name i64 $bits $($rest)*); };
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident i128 $bits:tt $($rest:tt)*) => { $crate::bitstruct!(@impl_signed_int $base_type, $shift, $field_vis $field_name i128 $bits $($rest)*); };
(@impl_int $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident $field_type:tt $bits:tt $($rest:tt)*) => {
$crate::paste::paste! {
pub const [<$field_name:upper _OFFSET>]: usize = $shift;
pub const [<$field_name:upper _BITS>]: usize = $bits;
#[doc(hidden)]
const [<$field_name:upper _MASK>]: $base_type = ((!0 as <$base_type as $crate::IsValidBaseInt>::Unsigned) >> (<$base_type as $crate::BitLength>::BITS - Self::[<$field_name:upper _BITS>])) as $base_type;
#[allow(dead_code)]
#[inline]
#[doc = concat!("Returns the `", stringify!($field_name), "` property as a `", stringify!($field_type), "`.")]
$field_vis const fn $field_name(self) -> $field_type {
((self.0 >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) as $field_type
}
#[allow(dead_code)]
#[inline]
#[doc = concat!("Inline mutation to apply the `", stringify!($field_name), "` property. Masks inputs over ", stringify!($bits), " bits.")]
$field_vis fn [<set_ $field_name>](&mut self, val: $field_type) {
debug_assert!((val as $base_type) <= Self::[<$field_name:upper _MASK>], "Value {} overflows allocated {} bits", val, $bits);
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
self.0 = (self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]);
}
#[allow(dead_code)]
#[doc = concat!("Returns a cloned copy of the bitfield with the `", stringify!($field_name), "` property mapped. Masks inputs over ", stringify!($bits), " bits.")]
$field_vis const fn [<with_ $field_name>](self, val: $field_type) -> Self {
debug_assert!((val as $base_type) <= Self::[<$field_name:upper _MASK>], "Value overflows allocated bits");
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
Self((self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}
#[allow(dead_code)]
#[doc = concat!("Strict inline mutation to apply the `", stringify!($field_name), "` property. Returns a `BitstructError` if the value overflows ", stringify!($bits), " bits.")]
$field_vis fn [<try_set_ $field_name>](&mut self, val: $field_type) -> Result<(), $crate::BitstructError> {
if (val as $base_type) > Self::[<$field_name:upper _MASK>] {
return Err($crate::BitstructError::Overflow { value: (val as $base_type) as u128, allocated_bits: $bits });
}
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
self.0 = (self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]);
Ok(())
}
#[allow(dead_code)]
#[doc = concat!("Strict cloned evaluation to apply the `", stringify!($field_name), "` property. Returns a `BitstructError` if the value overflows ", stringify!($bits), " bits.")]
$field_vis const fn [<try_with_ $field_name>](self, val: $field_type) -> Result<Self, $crate::BitstructError> {
if (val as $base_type) > Self::[<$field_name:upper _MASK>] {
return Err($crate::BitstructError::Overflow { value: (val as $base_type) as u128, allocated_bits: $bits });
}
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
Ok(Self((self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>])))
}
}
$crate::bitstruct!(@impl_getters_setters $base_type, $shift + $bits, $($rest)*);
};
(@impl_signed_int $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident $field_type:tt $bits:tt $($rest:tt)*) => {
$crate::paste::paste! {
pub const [<$field_name:upper _OFFSET>]: usize = $shift;
pub const [<$field_name:upper _BITS>]: usize = $bits;
#[doc(hidden)]
const [<$field_name:upper _MASK>]: $base_type = ((!0 as <$base_type as $crate::IsValidBaseInt>::Unsigned) >> (<$base_type as $crate::BitLength>::BITS - Self::[<$field_name:upper _BITS>])) as $base_type;
#[doc(hidden)]
pub const [<$field_name:upper _MIN>]: $field_type = (!0 as $field_type) << (Self::[<$field_name:upper _BITS>] - 1);
#[doc(hidden)]
pub const [<$field_name:upper _MAX>]: $field_type = !Self::[<$field_name:upper _MIN>];
#[doc(hidden)]
const [<$field_name:upper _SHIFT_UP>]: usize = <$field_type as $crate::BitLength>::BITS - Self::[<$field_name:upper _BITS>];
#[allow(dead_code)]
#[inline]
#[doc = concat!("Returns the `", stringify!($field_name), "` property as a signed `", stringify!($field_type), "`.")]
$field_vis const fn $field_name(self) -> $field_type {
let raw = ((self.0 >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) as $field_type;
(raw << Self::[<$field_name:upper _SHIFT_UP>]) >> Self::[<$field_name:upper _SHIFT_UP>]
}
#[allow(dead_code)]
#[inline]
#[doc = concat!("Inline mutation to apply the `", stringify!($field_name), "` signed property. Ensures bounds checking.")]
$field_vis fn [<set_ $field_name>](&mut self, val: $field_type) {
debug_assert!(val >= Self::[<$field_name:upper _MIN>] && val <= Self::[<$field_name:upper _MAX>], "Value {} out of bounds for {} bits", val, $bits);
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
self.0 = (self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]);
}
#[allow(dead_code)]
#[doc = concat!("Returns a cloned copy of the bitfield with the `", stringify!($field_name), "` signed property mapped.")]
$field_vis const fn [<with_ $field_name>](self, val: $field_type) -> Self {
debug_assert!(val >= Self::[<$field_name:upper _MIN>] && val <= Self::[<$field_name:upper _MAX>], "Value overflows allocated bits");
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
Self((self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}
#[allow(dead_code)]
#[doc = concat!("Strict inline mutation to apply the `", stringify!($field_name), "` signed property. Returns a `BitstructError` if out of bounds.")]
$field_vis fn [<try_set_ $field_name>](&mut self, val: $field_type) -> Result<(), $crate::BitstructError> {
if val < Self::[<$field_name:upper _MIN>] || val > Self::[<$field_name:upper _MAX>] {
return Err($crate::BitstructError::Overflow { value: val as i128 as u128, allocated_bits: $bits });
}
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
self.0 = (self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]);
Ok(())
}
#[allow(dead_code)]
#[doc = concat!("Strict cloned evaluation to apply the `", stringify!($field_name), "` signed property. Returns a `BitstructError` if out of bounds.")]
$field_vis const fn [<try_with_ $field_name>](self, val: $field_type) -> Result<Self, $crate::BitstructError> {
if val < Self::[<$field_name:upper _MIN>] || val > Self::[<$field_name:upper _MAX>] {
return Err($crate::BitstructError::Overflow { value: val as i128 as u128, allocated_bits: $bits });
}
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
Ok(Self((self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>])))
}
}
$crate::bitstruct!(@impl_getters_setters $base_type, $shift + $bits, $($rest)*);
};
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident ($field_type:ty) $bits:tt $($rest:tt)*) => {
$crate::paste::paste! {
pub const [<$field_name:upper _OFFSET>]: usize = $shift;
pub const [<$field_name:upper _BITS>]: usize = $bits;
#[doc(hidden)]
const [<$field_name:upper _MASK>]: $base_type = ((!0 as <$base_type as $crate::IsValidBaseInt>::Unsigned) >> (<$base_type as $crate::BitLength>::BITS - Self::[<$field_name:upper _BITS>])) as $base_type;
#[allow(dead_code)]
#[inline]
$field_vis const fn $field_name(self) -> $field_type {
((self.0 >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) as $field_type
}
#[allow(dead_code)]
#[inline]
$field_vis fn [<set_ $field_name>](&mut self, val: $field_type) {
debug_assert!((val as $base_type) <= Self::[<$field_name:upper _MASK>], "Value {} overflows allocated {} bits", val, $bits);
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
self.0 = (self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]);
}
#[allow(dead_code)]
$field_vis const fn [<with_ $field_name>](self, val: $field_type) -> Self {
debug_assert!((val as $base_type) <= Self::[<$field_name:upper _MASK>], "Value overflows allocated bits");
let val_masked = (val as $base_type) & Self::[<$field_name:upper _MASK>];
Self((self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}
#[allow(dead_code)]
$field_vis fn [<try_set_ $field_name>](&mut self, val: $field_type) -> Result<(), $crate::BitstructError> {
if (val as $base_type) > Self::[<$field_name:upper _MASK>] {
return Err($crate::BitstructError::Overflow { value: (val as $base_type) as u128, allocated_bits: $bits });
}
self.[<set_ $field_name>](val);
Ok(())
}
#[allow(dead_code)]
$field_vis const fn [<try_with_ $field_name>](self, val: $field_type) -> Result<Self, $crate::BitstructError> {
if (val as $base_type) > Self::[<$field_name:upper _MASK>] {
return Err($crate::BitstructError::Overflow { value: (val as $base_type) as u128, allocated_bits: $bits });
}
Ok(self.[<with_ $field_name>](val))
}
}
$crate::bitstruct!(@impl_getters_setters $base_type, $shift + $bits, $($rest)*);
};
(@impl_getters_setters $base_type:ty, $shift:expr, $field_vis:vis $field_name:ident $field_type:tt $bits:tt $($rest:tt)*) => {
$crate::paste::paste! {
pub const [<$field_name:upper _OFFSET>]: usize = $shift;
pub const [<$field_name:upper _BITS>]: usize = $bits;
#[doc(hidden)]
const [<$field_name:upper _MASK>]: $base_type = ((!0 as <$base_type as $crate::IsValidBaseInt>::Unsigned) >> (<$base_type as $crate::BitLength>::BITS - Self::[<$field_name:upper _BITS>])) as $base_type;
#[allow(dead_code)]
#[doc = concat!("Returns the `", stringify!($field_name), "` variant strictly typed to the `", stringify!($field_type), "` enumeration.")]
$field_vis const fn $field_name(self) -> $field_type {
$field_type::from_bits(((self.0 >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) as _)
}
#[allow(dead_code)]
#[doc = concat!("Inline mutation to apply the bounded `", stringify!($field_type), "` enumeration to the `", stringify!($field_name), "` property.")]
$field_vis fn [<set_ $field_name>](&mut self, val: $field_type) {
const _: () = assert!(<$field_type>::BITS <= $bits, "Enum bit width exceeds allocated field width");
let val_masked = (val.to_bits() as $base_type) & Self::[<$field_name:upper _MASK>];
self.0 = (self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]);
}
#[allow(dead_code)]
#[doc = concat!("Returns a cloned copy of the bitfield bounded by the `", stringify!($field_type), "` enumeration supplied to `", stringify!($field_name), "`.")]
$field_vis const fn [<with_ $field_name>](self, val: $field_type) -> Self {
const _: () = assert!(<$field_type>::BITS <= $bits, "Enum bit width exceeds allocated field width");
let val_masked = (val.to_bits() as $base_type) & Self::[<$field_name:upper _MASK>];
Self((self.0 & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}
#[allow(dead_code)]
#[doc = concat!("Strict inline mutation to apply the bounded `", stringify!($field_type), "` enumeration to the `", stringify!($field_name), "` property. Returns a `BitstructError` if the value overflows ", stringify!($bits), " bits.")]
$field_vis fn [<try_set_ $field_name>](&mut self, val: $field_type) -> Result<(), $crate::BitstructError> {
self.[<set_ $field_name>](val);
Ok(())
}
#[allow(dead_code)]
#[doc = concat!("Strict cloned evaluation to apply the bounded `", stringify!($field_type), "` enumeration to the `", stringify!($field_name), "` property. Returns a `BitstructError` if the value overflows ", stringify!($bits), " bits.")]
$field_vis const fn [<try_with_ $field_name>](self, val: $field_type) -> Result<Self, $crate::BitstructError> {
Ok(self.[<with_ $field_name>](val))
}
}
$crate::bitstruct!(@impl_getters_setters $base_type, $shift + $bits, $($rest)*);
};
}