NonZeroU128 through FFI

Hello,

I need to manipulate a Rust ID type from C, which looks like this:

#[repr(transparent)]
pub struct RustId(pub NonZeroU128);

Since u128 is not FFI-safe, and since I'd like to avoid allocating memory everywhere if possible, I've created these helper structs and functions:

#[repr(C)]
pub struct c_id {
    _data: [u8; 16],
}

#[repr(C)]
pub struct opt_c_id {
    pub has_value: bool,
    pub value: c_id,
}

#[no_mangle]
pub extern "C" fn c_id_new(value: u64) -> opt_c_id {
    match NonZeroU128::new(value as u128) {
        Some(value) => opt_c_id {
            has_value: true,
            value: unsafe { mem::transmute::<NonZeroU128, c_id>(value) },
        }
        None => opt_c_id {
            has_value: false,
            value: c_id { _data: [0; 16] },
        }
    }
}

#[no_mangle]
pub extern "C" fn c_id_display(id: c_id) {
    // Probably check to see if the array is full of zeroes here.
    let id = unsafe { mem::transmute::<c_id, RustId>(id) };
    println!("{}", id.0);
}

I can then make use of this API like so:

#include "id.h"

int main() {
    opt_c_id opt_id = c_id_new(42);
    if (opt_id.has_value) {
        c_id id = opt_id.value;
        c_id_display(id);
    }
    return 0;
}

But is it actually safe? Is this approach valid or is there another way? I will have a lot of these types around, so allocating memory each time to then cast to *mut c_void seems bad to me.

For full reference, here is the header file as generated by cbindgen:

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct c_id {
  uint8_t _data[16];
} c_id;

typedef struct opt_c_id {
  bool has_value;
  struct c_id value;
} opt_c_id;

struct opt_c_id c_id_new(uint64_t value);

void c_id_display(struct c_id id);

Thanks in advance for your replies.

You can use u128::to_ne_bytes() and u128::from_ne_bytes() to safely convert between a u128 and a [u8; 16] array. Otherwise, your approach looks entirely sound.

3 Likes

Indeed these two functions can be of great help here. Thanks for checking the soundness of the code.