Skip to content

Commit

Permalink
Add additional helpers to bitmask data structure
Browse files Browse the repository at this point in the history
This commit addresses the case where a struct containing a bitfield
is wrapped in a struct such as UnsafeCell which allows interior
mutability.

Previously, bitfield accessors only allowed a receiver. This becomes
problematic in the case of interior mutability as raw pointer access may
be required so as not to violate the aliasing rules in Rust.
  • Loading branch information
jbaublitz committed Jul 30, 2024
1 parent 66b6551 commit e2678d1
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 10 deletions.
107 changes: 97 additions & 10 deletions bindgen/codegen/bitfield_unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
#[inline]
pub fn get_bit(&self, index: usize) -> bool {
debug_assert!(index / 8 < self.storage.as_ref().len());

let byte_index = index / 8;
let byte = self.storage.as_ref()[byte_index];

fn extract_bit(byte: u8, index: usize) -> bool {
let bit_index = if cfg!(target_endian = "big") {
7 - (index % 8)
} else {
Expand All @@ -34,12 +29,28 @@ where
}

#[inline]
pub fn set_bit(&mut self, index: usize, val: bool) {
pub fn get_bit(&self, index: usize) -> bool {
debug_assert!(index / 8 < self.storage.as_ref().len());

let byte_index = index / 8;
let byte = &mut self.storage.as_mut()[byte_index];
let byte = self.storage.as_ref()[byte_index];

Self::extract_bit(byte, index)
}

#[inline]
pub unsafe fn raw_get_bit(this: *const Self, index: usize) -> bool {
debug_assert!(index / 8 < std::mem::size_of::<Storage>());

let byte_index = index / 8;
let byte = *(std::ptr::addr_of!((*this).storage) as *const u8)
.offset(byte_index as isize);

Self::extract_bit(byte, index)
}

#[inline]
fn change_bit(byte: u8, index: usize, val: bool) -> u8 {
let bit_index = if cfg!(target_endian = "big") {
7 - (index % 8)
} else {
Expand All @@ -48,12 +59,33 @@ where

let mask = 1 << bit_index;
if val {
*byte |= mask;
byte | mask
} else {
*byte &= !mask;
byte & !mask
}
}

#[inline]
pub fn set_bit(&mut self, index: usize, val: bool) {
debug_assert!(index / 8 < self.storage.as_ref().len());

let byte_index = index / 8;
let byte = &mut self.storage.as_mut()[byte_index];

*byte = Self::change_bit(*byte, index, val);
}

#[inline]
pub unsafe fn raw_set_bit(this: *mut Self, index: usize, val: bool) {
debug_assert!(index / 8 < std::mem::size_of::<Storage>());

let byte_index = index / 8;
let byte = (std::ptr::addr_of_mut!((*this).storage) as *mut u8)
.offset(byte_index as isize);

*byte = Self::change_bit(*byte, index, val);
}

#[inline]
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
debug_assert!(bit_width <= 64);
Expand All @@ -79,6 +111,35 @@ where
val
}

#[inline]
pub unsafe fn raw_get(
this: *const Self,
bit_offset: usize,
bit_width: u8,
) -> u64 {
debug_assert!(bit_width <= 64);
debug_assert!(bit_offset / 8 < std::mem::size_of::<Storage>());
debug_assert!(
(bit_offset + (bit_width as usize)) / 8 <=
std::mem::size_of::<Storage>()
);

let mut val = 0;

for i in 0..(bit_width as usize) {
if Self::raw_get_bit(this, i + bit_offset) {
let index = if cfg!(target_endian = "big") {
bit_width as usize - 1 - i
} else {
i
};
val |= 1 << index;
}
}

val
}

#[inline]
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
debug_assert!(bit_width <= 64);
Expand All @@ -99,4 +160,30 @@ where
self.set_bit(index + bit_offset, val_bit_is_set);
}
}

#[inline]
pub unsafe fn raw_set(
this: *mut Self,
bit_offset: usize,
bit_width: u8,
val: u64,
) {
debug_assert!(bit_width <= 64);
debug_assert!(bit_offset / 8 < std::mem::size_of::<Storage>());
debug_assert!(
(bit_offset + (bit_width as usize)) / 8 <=
std::mem::size_of::<Storage>()
);

for i in 0..(bit_width as usize) {
let mask = 1 << i;
let val_bit_is_set = val & mask == mask;
let index = if cfg!(target_endian = "big") {
bit_width as usize - 1 - i
} else {
i
};
Self::raw_set_bit(this, index + bit_offset, val_bit_is_set);
}
}
}
39 changes: 39 additions & 0 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,15 @@ fn bitfield_getter_name(
quote! { #name }
}

fn bitfield_raw_getter_name(
ctx: &BindgenContext,
bitfield: &Bitfield,
) -> proc_macro2::TokenStream {
let name = bitfield.getter_name();
let name = ctx.rust_ident_raw(format!("raw_{name}"));
quote! { #name }
}

fn bitfield_setter_name(
ctx: &BindgenContext,
bitfield: &Bitfield,
Expand All @@ -1858,6 +1867,15 @@ fn bitfield_setter_name(
quote! { #setter }
}

fn bitfield_raw_setter_name(
ctx: &BindgenContext,
bitfield: &Bitfield,
) -> proc_macro2::TokenStream {
let setter = bitfield.setter_name();
let setter = ctx.rust_ident_raw(format!("raw_{setter}"));
quote! { #setter }
}

impl<'a> FieldCodegen<'a> for Bitfield {
type Extra = (&'a str, &'a mut bool, &'a mut FieldVisibilityKind);

Expand Down Expand Up @@ -1885,6 +1903,8 @@ impl<'a> FieldCodegen<'a> for Bitfield {
let prefix = ctx.trait_prefix();
let getter_name = bitfield_getter_name(ctx, self);
let setter_name = bitfield_setter_name(ctx, self);
let raw_getter_name = bitfield_raw_getter_name(ctx, self);
let raw_setter_name = bitfield_raw_setter_name(ctx, self);
let unit_field_ident = Ident::new(unit_field_name, Span::call_site());

let bitfield_ty_item = ctx.resolve_item(self.ty());
Expand Down Expand Up @@ -1951,6 +1971,25 @@ impl<'a> FieldCodegen<'a> for Bitfield {
)
}
}

#[inline]
#access_spec unsafe fn #raw_getter_name(this: *const Self) -> #bitfield_ty {
unsafe {
::std::mem::transmute(__BindgenBitfieldUnit::raw_get(
addr_of!((*this).#unit_field_ident),
#offset,
#width,
) as #bitfield_int_ty)
}
}

#[inline]
#access_spec unsafe fn #raw_setter_name(this: *mut Self, val: #bitfield_ty) {
unsafe {
let val: #bitfield_int_ty = ::std::mem::transmute(val);
__BindgenBitfieldUnit::raw_set(addr_of!((*this)._bitfield_1), #offset, #width, val as u64)
}
}
}));
} else {
methods.extend(Some(quote! {
Expand Down

0 comments on commit e2678d1

Please sign in to comment.