ljrk
ljrk

Reputation: 821

Struct with mixed bitflag and normal members

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?

Upvotes: 2

Views: 798

Answers (2)

ljrk
ljrk

Reputation: 821

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.

Upvotes: 0

fghj
fghj

Reputation: 9404

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,
}

Upvotes: 1

Related Questions