C structs with bit fields and FFI


#1

Hi all,

I’m trying to build a Rust wrapper for a C library (to be exact: joyent/http-parser, and my unfinished wrapper), and found a problem with C packed structs with bit fields.

For instance, here’s a structure:

struct http_parser {
  ...
  unsigned short http_major;
  unsigned short http_minor;
  unsigned int status_code : 16; /* responses only */
  unsigned int method : 8;       /* requests only */
  unsigned int http_errno : 7;
  ...
}

The problem here is that bit fields aren’t standard, and it’s not determined how compiler should align them.
I guess it also means that there’s no portable way to wrap such structs with Rust’s FFI.

Maybe someone here have an idea how these structs should be handled?
My first guess is to write a small wrapper function in C that would take this aligned struct as an argument and rewrap it using bitmasks (e.g. (http_parser.http_errno) | (http_parser.method << 8) | ...), but maybe I’m missing something obvious and there is a better way?

Thanks!


#2

I didn’t realize that alignment wasn’t well defined :disappointed: If it was, you could do the bitmasking in rust, without the need for a C wrapper

Sorry, I don’t have any suggestions, but for anyone else interested in this issue, here is a link to the issue in the RFC repo : https://github.com/rust-lang/rfcs/issues/314


#3

Does marking the struct with #[repr(C)] not work?


#4

It works perfectly for usual structs, but unfortunately not for structs with bit fieds.
Description of this feature can be found e.g. on MSDN - the problem is that bit fields implementation differs from compiler to compiler.


#5

Well, if anyone is interested in this problem: as it turns out, transforming struct’s bit fields into an explicit bit array works perfectly.

Example wrapper function:

uint32_t http_get_struct_flags(const http_parser *state) {
  return state->status_code |
    (state->method << 16) |
    (state->http_errno << 24);
}

And usage in Rust:

fn http_get_struct_flags(parser: *const HttpParser) -> u32;
...
unsafe {
     let flags = http_get_struct_flags(parser_struct as *const _);

     let status_code = (flags & 0xFFFF) as u16;
     let method_code = ((flags >> 16) & 0xFF) as u8;
     let http_errno = ((flags >> 24) & 0x7F) as u8;
     ...
}

But, obviously, the wrapper file should be built using the same compiler (e.g. if you build the lib with GCC, make sure that the wrapper is built with GCC as well).


#6

Careful – the relationship between bitfield declaration order and bit order (as seen by the shift operators) depends not only on the compiler, but on the endianness and platform ABI.

I’d suggest linking in a fragment of C code to provide accessors for the bitfields. Anything else is likely to be fragile.

Note that you may also have trouble determining the size and alignment requirements for this struct in a portable way. Different platforms (even using a given compiler) differ in conventions like whether a zero-length (int : 0) bitfield serves as a break that consumes the rest of the current byte/int/etc. If you search for “bit” in GCC’s storage layout configuration options you’ll find a lot of ways that this can vary. I’m not fluent enough in Rust to suggest a good way of fixing this one, but you might want to expose functions from C that can return the sizeof/alignof, or even let C allocate the struct for you.

Finally, you should consider sending feedback to the library authors. :smile: