Skip to content

Commit

Permalink
Support bitfield allocation units larger than 64 bits
Browse files Browse the repository at this point in the history
Individual bitfields are still limited to at most 64 bits, but this
restriction can be weakened when Rust supports u128.

This implements issue #816.

Usage notes:

* Since common code is added to each generated binding, a program which uses
  more than one binding may need to work around the duplication by including
  each binding in its own module.
* The values created by bitfield allocation unit constructors can be assigned
  directly to the corresponding struct fields with no need for transmutation.

Implementation notes:

__BindgenBitfieldUnit represents a bitfield allocation unit using a Storage
type accessible as a slice of u8. The alignment of the unit is inherited from
an Align type by virtue of the field:

align: [Align; 0],

The position of this field in the struct is irrelevant.

The alignment of the Storage type is intended to be no larger than the
alignment of the Align type, which will be true if the Storage type is, for
example, an array of u8.

Although the double underscore (__) prefix is reserved for implementations of
C++, there are precedents for this convention elsewhere in bindgen and so the
convention is adopted here too.

Acknowledgement:

Thanks to @fitzgen for an initial implementation of __BindgenBitfieldUnit and
code to integrate it into bindgen.
  • Loading branch information
fitzgen authored and glyn committed Nov 21, 2017
1 parent 5e0cf9c commit f0e0531
Show file tree
Hide file tree
Showing 36 changed files with 4,147 additions and 4,629 deletions.
2 changes: 1 addition & 1 deletion bindgen-integration/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl ParseCallbacks for MacroCallback {
}

fn main() {
gcc::Config::new()
gcc::Build::new()
.cpp(true)
.file("cpp/Test.cc")
.compile("libtest.a");
Expand Down
45 changes: 31 additions & 14 deletions bindgen-integration/cpp/Test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ Date2::assert(unsigned short nWeekDay,
unsigned short nYear,
unsigned short byte)
{
return this->nWeekDay == nWeekDay &&
this->nMonthDay == nMonthDay &&
this->nMonth == nMonth &&
this->nYear == nYear &&
this->byte == byte;
return this->nWeekDay == nWeekDay &&
this->nMonthDay == nMonthDay &&
this->nMonth == nMonth &&
this->nYear == nYear &&
this->byte == byte;
}

bool
Expand All @@ -83,22 +83,39 @@ Fifth::assert(unsigned short nWeekDay,
unsigned short nYear,
unsigned char byte)
{
return this->nWeekDay == nWeekDay &&
this->nMonthDay == nMonthDay &&
this->nMonth == nMonth &&
this->nYear == nYear &&
this->byte == byte;
return this->nWeekDay == nWeekDay &&
this->nMonthDay == nMonthDay &&
this->nMonth == nMonth &&
this->nYear == nYear &&
this->byte == byte;
}

bool
Sixth::assert(unsigned char byte,
unsigned char nWeekDay,
unsigned char nMonth,
unsigned char nMonthDay) {
return this->nWeekDay == nWeekDay &&
this->nMonthDay == nMonthDay &&
this->nMonth == nMonth &&
this->byte == byte;
return this->nWeekDay == nWeekDay &&
this->nMonthDay == nMonthDay &&
this->nMonth == nMonth &&
this->byte == byte;
};

bool
Seventh::assert(bool first,
int second,
unsigned short third,
unsigned int fourth,
unsigned short fifth,
bool sixth,
int seventh) {
return this->first_one_bit == first &&
this->second_thirty_bits == second &&
this->third_two_bits == third &&
this->fourth_thirty_bits == fourth &&
this->fifth_two_bits == fifth &&
this->sixth_one_bit == sixth &&
this->seventh_thirty_bits == seventh;
};

} // namespace bitfields
18 changes: 18 additions & 0 deletions bindgen-integration/cpp/Test.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,24 @@ struct Sixth {
unsigned char nMonthDay);
};

struct Seventh {
bool first_one_bit : 1;
unsigned int second_thirty_bits : 30;
unsigned short third_two_bits : 2;
unsigned int fourth_thirty_bits : 30;
unsigned short fifth_two_bits : 2;
bool sixth_one_bit : 1;
unsigned int seventh_thirty_bits : 30;

/// Returns true if the bitfields match the arguments, false otherwise.
bool assert(bool first,
int second,
unsigned short third,
unsigned int fourth,
unsigned short fifth,
bool sixth,
int seventh);
};

} // namespace bitfields

Expand Down
33 changes: 32 additions & 1 deletion bindgen-integration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,42 @@ fn test_bitfields_sixth() {
});
}

#[test]
fn test_bitfields_seventh() {
let mut large: bindings::bitfields::Seventh = unsafe {
mem::zeroed()
};

assert!(unsafe {
large.assert(false, 0, 0, 0, 0, false, 0)
});

large.set_first_one_bit(true);
large.set_second_thirty_bits(375028802);
large.set_third_two_bits(2);
large.set_fourth_thirty_bits(643472885);
large.set_fifth_two_bits(3);
large.set_sixth_one_bit(true);
large.set_seventh_thirty_bits(1061657575);

assert!(unsafe {
large.assert(true, 375028802, 2, 643472885, 3, true, 1061657575)
});

assert_eq!(large.first_one_bit(), true);
assert_eq!(large.second_thirty_bits(), 375028802);
assert_eq!(large.third_two_bits(), 2);
assert_eq!(large.fourth_thirty_bits(), 643472885);
assert_eq!(large.fifth_two_bits(), 3);
assert_eq!(large.sixth_one_bit(), true);
assert_eq!(large.seventh_thirty_bits(), 1061657575);
}

#[test]
fn test_bitfield_constructors() {
use std::mem;
let mut first = bindings::bitfields::First {
_bitfield_1: unsafe { mem::transmute(bindings::bitfields::First::new_bitfield_1(1, 2, 3)) }
_bitfield_1: bindings::bitfields::First::new_bitfield_1(1, 2, 3)
};
assert!(unsafe {
first.assert(1, 2, 3)
Expand Down
82 changes: 82 additions & 0 deletions src/codegen/bitfield_unit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage, Align>
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
storage: Storage,
align: [Align; 0],
}

impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
#[inline]
pub fn new(storage: Storage) -> Self {
Self {
storage,
align: [],
}
}

#[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];

let bit_index = index % 8;
let mask = 1 << bit_index;

byte & mask == 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];

let bit_index = index % 8;
let mask = 1 << bit_index;

if val {
*byte |= mask;
} else {
*byte &= !mask;
}
}

#[inline]
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
debug_assert!(bit_width <= 64);
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());

let mut val = 0;

for i in 0..(bit_width as usize) {
if self.get_bit(i + bit_offset) {
val |= 1 << i;
}
}

val
}

#[inline]
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
debug_assert!(bit_width <= 64);
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());

for i in 0..(bit_width as usize) {
let mask = 1 << i;
let val_bit_is_set = val & mask == mask;
self.set_bit(i + bit_offset, val_bit_is_set);
}
}
}
Loading

0 comments on commit f0e0531

Please sign in to comment.