2

I'm trying to recreate a C struct with mixed bitfield members and "normal" members in Rust for FFI.

I've read that the bitflags crate would be the one to go with, unfortunately I find the documentation lacking on the regards how the syntax actually works.

The bitflags crate makes it easier to create bitmasks in a similar style as in C using enums. The bitfield crate claims to create bitfields that can be accessed, however I have no idea how it works.

I have a C structure like this:

struct mixed {
    unsigned int flag_1_1 : 1;
    unsigned int flag_2_7 : 7;
    unsigned int flag_3_8 : 8;

    unsigned int some_val1;
    unsigned int some_val2;

    unsigned int flag_4_16 : 16;
};

I have no clue on how to represent it in Rust, I'd use the crate libc to have access to c_uint, but other than that, I'm currently pretty much out of ideas and finding other code that does this has not proven successful:

#[repr(transparent)] // do I also need repr(C) ?
struct mixed {
    flags_1_3: mixed_flags_1_3;
    some_val1: c_uint;
    some_val2: c_uint;
    flags_4: mixed_flags_4;
}

bitfield!(
    #[repr(transparent)] // do I also need repr(C), here too?
    struct mixed_flags_1_3(u16 /* what about this parameter? */) {
        u8; // what does this mean?
        /* get/field, set: first bit, last bit; */
        flag_1_1, _: 0, 0;
        flag_2_7, _: 7, 1;
        flag_3_8, _: 15, 8;
    }
)

bitfield!(
    #[repr(transparent)]
    struct mixed_flags_4(u8) {
        u8;
        flag_4_16, _: 15, 0;
    }
)

These are just guesses, how do I create a correct representation?

ljrk
  • 751
  • 1
  • 5
  • 21
  • 1
    You don't want *bitflags*, you want *bit fields*. They aren't the same thing. – Shepmaster Mar 06 '18 at 21:59
  • Duplicate of [How does a C-style struct with a bitfield get represented in a Rust #\[repr(C)\] struct?](https://stackoverflow.com/q/45229167/155423). – Shepmaster Mar 06 '18 at 21:59
  • Oh, I was confused by the answer linking the bitflags crate. This makes sense now. But there's no documentation on whether the created bitflags are compatible. It's perhaps impossible due to being compiler-specific though, so I'd be forced to write accessor-functions in C. – ljrk Mar 06 '18 at 22:49

2 Answers2

1

In cases like this you can look at genearted code by bindgen:

$ bindgen test.h

#[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],
}

//skipped code with access method for bit fields 

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct mixed {
    pub _bitfield_1: __BindgenBitfieldUnit<[u8; 2usize], u8>,
    pub some_val1: ::std::os::raw::c_uint,
    pub some_val2: ::std::os::raw::c_uint,
    pub _bitfield_2: __BindgenBitfieldUnit<[u8; 2usize], u16>,
    pub __bindgen_padding_0: u16,
}
fghj
  • 8,898
  • 4
  • 28
  • 56
  • However the C standard doesn't promise you shit about the layout, so the bindgen created bindings only work for the platform you are currently developing for. I'm wondering whether the bitfield macro would handle this, but I haven't actually been able to understand at all what it should do, the doc seems a bit lacking. I've just gone and used bingen in my crate, it's another addeded dependency but otherwise it'd be difficult to stay portable. – ljrk Mar 09 '18 at 16:54
0

Using rustc -- -Z unstable-options --pretty=expanded I think I could figure out that the macro does, and this seems to yield something that could be correct, however this is probably only compatible when the compiler does not try to pad or reorder the bitfields in the struct.

#[repr(transparent)] // do I also need repr(C) ?
struct mixed {
    flags_1_3: mixed_flags_1_3;
    some_val1: c_uint;
    some_val2: c_uint;
    flags_4: mixed_flags_4;
}

bitfield!(
    #[repr(transparent)] // do I also need repr(C), here too?
    //  Results in a "tuple struct", ie. u16 = total size of bitfields
    struct mixed_flags_1_3(u16) {
        // All the following fields value should be treated as an
        // unsigned int when accessed
        c_uint;
        /* get/field, set: first bit, last bit; */
        flag_1_1, _: 0, 0;
        flag_2_7, _: 7, 1;
        // One could change the type again here, if one wanted to:
        // u16
        flag_3_8, _: 15, 8;
    }
)

bitfield!(
    #[repr(transparent)]
    struct mixed_flags_4(u16) {
        c_uint;
        flag_4_16, _: 15, 0;
    }
)

But for now at least I think I will just use libclang and bindgen as dependencies and generate my bindings automatically, due to the aforementioned problems with platform compat.

ljrk
  • 751
  • 1
  • 5
  • 21