Skip to content

Commit

Permalink
Auto merge of #1158 - glyn:large-bitfield-units, r=emilio
Browse files Browse the repository at this point in the history
Support bitfield allocation units larger than 64 bits

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.

It is assumed that the alignment of the `Storage` type is no larger than the
alignment of the `Align` type, which will be true if the `Storage` type is, for
example, an array of `u8`. This assumption is checked in a debug assertion.

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.

r? @emilio
  • Loading branch information
bors-servo committed Nov 23, 2017
2 parents e3e6c73 + f0e0531 commit 7c3584d
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 7c3584d

Please sign in to comment.