motorcortex-rust 0.5.0

Motorcortex Rust: a Rust client for the Motorcortex Core real-time control system (async + blocking).
Documentation
use crate::error::{MotorcortexError, Result};
use nng_c_sys::nng_socket;
use std::ffi::c_void;
use std::ptr;

pub fn receive_message(sock: &nng_socket) -> Result<Vec<u8>> {
    let mut reply_ptr: *mut c_void = ptr::null_mut();
    let mut reply_size: usize = 0;

    // SAFETY: `reply_ptr` / `reply_size` are out parameters that
    // NNG fills on a successful receive. `NNG_FLAG_ALLOC` asks NNG
    // to allocate the receive buffer with its own allocator; we
    // own it from here and must pair it with `nng_free` below.
    let rv = unsafe {
        nng_c_sys::nng_recv(
            *sock,
            &mut reply_ptr as *mut _ as *mut c_void,
            &mut reply_size,
            nng_c_sys::NNG_FLAG_ALLOC,
        )
    };
    if rv != 0 {
        return Err(MotorcortexError::Io(format!(
            "nng_recv failed with code: {rv}"
        )));
    }

    // Copy the NNG-allocated buffer into a Rust-owned `Vec`, then
    // hand the original back to NNG's allocator.
    // `Vec::from_raw_parts` would require the buffer to have been
    // allocated with Rust's global allocator — not guaranteed for
    // anything `NNG_FLAG_ALLOC` produces, even when both happen to
    // bottom out at the system `malloc`. The copy is O(n) once per
    // inbound frame; soundness wins.
    //
    // SAFETY: NNG guarantees that, when nng_recv returns 0 with
    // `NNG_FLAG_ALLOC`, `reply_ptr` points to `reply_size` bytes of
    // initialised memory owned by the caller.
    let vec = unsafe {
        let slice = std::slice::from_raw_parts(reply_ptr as *const u8, reply_size);
        let vec = slice.to_vec();
        nng_c_sys::nng_free(reply_ptr, reply_size);
        vec
    };
    Ok(vec)
}