s3rvac s3rvac - 1 month ago 7
C Question

How to handle passing usize to a C function that expects uint32_t?

I am writing a Rust wrapper around a C library. The C library provides the following function:

void lib_foo(const unsigned char *buf, uint32_t buf_len);


I have created the following
extern
wrapper:

fn lib_foo(buf: *const u8, buf_len: u32);


and the following high-level wrapper:

pub fn foo(buf: &[u8]) {
unsafe { lib_foo(buf.as_ptr(), buf.len() as u32) }
}


However, the cast from
usize
(the type of
buf.len()
) to
u32
may cause the size to be truncated. What is the best way of dealing with this?

Answer

First of all, please use the libc crate for types.

That is: extern fn lib_foo(buf: *const libc::c_uchar, buf_len: libc::uint32_t);

While it is slightly longer, it will avoid making assumption (unsigned char maps to u8) and makes the translation automatic.


Then, onto the high level wrapper.

Without any assumption, the simplest solution is to panic, and document this.

/// Calls `lib_foo`, ...
///
/// # Panics
///
/// If the size of the buffer is strictly greater than 2^32-1 bytes.
pub fn foo(buf: &[u8]) {
    assert!(buf.len() <= (std::u32::MAX as usize));

    unsafe { lib_foo(buf.as_ptr() as *const _, buf.len() as libc::uint32_t) }
}

Then, depending on the domain, other options may arise:

  • saturation may be an option (using std::cmp::min)
  • repeated calls may be another
  • ...

in any case, if the difference of behavior is observable to the user, document.