#[macro_export]
macro_rules! atomic_bitstruct {
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicU8) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicU8, u8, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicU16) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicU16, u16, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicU32) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicU32, u32, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicU64) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicU64, u64, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicU128) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicU128, u128, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicI8) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicI8, i8, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicI16) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicI16, i16, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicI32) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicI32, i32, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicI64) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicI64, i64, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident (AtomicI128) {
$(
$field_vis:vis $field_name:ident: $field_type:tt = $bits:tt
),* $(,)?
}
) => {
$crate::atomic_bitstruct!(@impl $(#[$meta])* $vis $struct_name $crate::reexport::portable_atomic::AtomicI128, i128, { $( $field_vis $field_name: $field_type = $bits ),* }, $($field_vis $field_name $field_type $bits)*);
};
(@impl $(#[$meta:meta])* $vis:vis $struct_name:ident $atomic_ty:ty, $base_type:ty, { $($field_vis_struct:vis $field_name_struct:ident: $field_type_struct:tt = $bits_struct:tt),* }, $($field_vis:vis $field_name:ident $field_type:tt $bits:tt)*) => {
$(#[$meta])*
#[repr(transparent)]
$vis struct $struct_name(pub $atomic_ty);
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.load($crate::reexport::portable_atomic::Ordering::Relaxed))
$(
.field(stringify!($field_name), &self.$field_name($crate::reexport::portable_atomic::Ordering::Relaxed))
)*
.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 Default for $struct_name {
fn default() -> Self {
Self::new(0)
}
}
impl $struct_name {
#[allow(dead_code)]
pub const BITS: usize = <$base_type as $crate::BitLength>::BITS;
#[inline(always)]
#[allow(dead_code)]
pub const fn new(val: $base_type) -> Self {
Self(<$atomic_ty>::new(val))
}
#[inline(always)]
#[allow(dead_code)]
pub fn load(&self, order: $crate::reexport::portable_atomic::Ordering) -> $base_type {
self.0.load(order)
}
#[inline(always)]
#[allow(dead_code)]
pub fn store(&self, val: $base_type, order: $crate::reexport::portable_atomic::Ordering) {
self.0.store(val, order)
}
$crate::atomic_bitstruct!(@impl_getters_setters $base_type, 0, $($field_vis $field_name $field_type $bits)*);
}
$crate::paste::paste! {
$crate::bitstruct! {
#[doc = concat!("A non-atomic value snapshot of `", stringify!($struct_name), "` used for batch updates.")]
$vis struct [<$struct_name Value>]($base_type) {
$(
$field_vis_struct $field_name_struct: $field_type_struct = $bits_struct,
)*
}
}
impl $struct_name {
#[inline]
pub fn get(&self, order: $crate::reexport::portable_atomic::Ordering) -> [<$struct_name Value>] {
[<$struct_name Value>]::from_bits(self.0.load(order))
}
#[inline]
pub fn set(&self, val: [<$struct_name Value>], order: $crate::reexport::portable_atomic::Ordering) {
self.0.store(val.to_bits(), order);
}
#[inline]
pub fn update<F>(&self, set_order: $crate::reexport::portable_atomic::Ordering, fetch_order: $crate::reexport::portable_atomic::Ordering, mut f: F) -> [<$struct_name Value>]
where
F: FnMut(&mut [<$struct_name Value>])
{
let raw_prev = self.0.fetch_update(set_order, fetch_order, |raw| {
let mut snap = [<$struct_name Value>]::from_bits(raw);
f(&mut snap);
Some(snap.to_bits())
}).unwrap();
[<$struct_name Value>]::from_bits(raw_prev)
}
#[inline]
pub fn update_or_abort<F>(&self, set_order: $crate::reexport::portable_atomic::Ordering, fetch_order: $crate::reexport::portable_atomic::Ordering, mut f: F) -> Result<[<$struct_name Value>], [<$struct_name Value>]>
where
F: FnMut(&mut [<$struct_name Value>]) -> Option<()>
{
self.0.fetch_update(set_order, fetch_order, |raw| {
let mut snap = [<$struct_name Value>]::from_bits(raw);
f(&mut snap).map(|_| snap.to_bits())
}).map(|raw| [<$struct_name Value>]::from_bits(raw))
.map_err(|raw| [<$struct_name Value>]::from_bits(raw))
}
}
}
};
(@impl_getters_setters $base_type:ty, $shift:expr, ) => {};
(@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]
$field_vis fn $field_name(&self, order: $crate::reexport::portable_atomic::Ordering) -> bool {
((self.0.load(order) >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) != 0
}
#[allow(dead_code)]
#[inline]
$field_vis fn [<set_ $field_name>](&self, val: bool, order: $crate::reexport::portable_atomic::Ordering) {
let val_masked = val as $base_type;
self.0.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
}
#[allow(dead_code)]
$field_vis fn [<try_set_ $field_name>](&self, val: bool, order: $crate::reexport::portable_atomic::Ordering) -> Result<(), $crate::BitstructError> {
self.[<set_ $field_name>](val, order);
Ok(())
}
}
$crate::atomic_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::atomic_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::atomic_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::atomic_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::atomic_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::atomic_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::atomic_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::atomic_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::atomic_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::atomic_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::atomic_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]
$field_vis fn $field_name(&self, order: $crate::reexport::portable_atomic::Ordering) -> $field_type {
((self.0.load(order) >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) as $field_type
}
#[allow(dead_code)]
#[inline]
$field_vis fn [<set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) {
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.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
}
#[allow(dead_code)]
$field_vis fn [<try_set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) -> 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.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
Ok(())
}
}
$crate::atomic_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]
$field_vis fn $field_name(&self, order: $crate::reexport::portable_atomic::Ordering) -> $field_type {
let raw = ((self.0.load(order) >> 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]
$field_vis fn [<set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) {
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.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
}
#[allow(dead_code)]
$field_vis fn [<try_set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) -> 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.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
Ok(())
}
}
$crate::atomic_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 fn $field_name(&self, order: $crate::reexport::portable_atomic::Ordering) -> $field_type {
((self.0.load(order) >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) as $field_type
}
#[allow(dead_code)]
#[inline]
$field_vis fn [<set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) {
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.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
}
#[allow(dead_code)]
$field_vis fn [<try_set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) -> 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.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
Ok(())
}
}
$crate::atomic_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)]
#[inline]
$field_vis fn $field_name(&self, order: $crate::reexport::portable_atomic::Ordering) -> $field_type {
$field_type::from_bits(((self.0.load(order) >> Self::[<$field_name:upper _OFFSET>]) & Self::[<$field_name:upper _MASK>]) as _)
}
#[allow(dead_code)]
#[inline]
$field_vis fn [<set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) {
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.fetch_update(order, $crate::reexport::portable_atomic::Ordering::Relaxed, |raw| {
Some((raw & !(Self::[<$field_name:upper _MASK>] << Self::[<$field_name:upper _OFFSET>])) | (val_masked << Self::[<$field_name:upper _OFFSET>]))
}).unwrap();
}
#[allow(dead_code)]
$field_vis fn [<try_set_ $field_name>](&self, val: $field_type, order: $crate::reexport::portable_atomic::Ordering) -> Result<(), $crate::BitstructError> {
self.[<set_ $field_name>](val, order);
Ok(())
}
}
$crate::atomic_bitstruct!(@impl_getters_setters $base_type, $shift + $bits, $($rest)*);
};
}