Skip to content

Instantly share code, notes, and snippets.

@ChunMinChang
Last active February 13, 2019 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChunMinChang/1e5410f3a7cb8c5bbf066e7dae09d7bc to your computer and use it in GitHub Desktop.
Save ChunMinChang/1e5410f3a7cb8c5bbf066e7dae09d7bc to your computer and use it in GitHub Desktop.
Pass arrays from Rust to C
#if !defined(EXT_H)
#define EXT_H
#include <stddef.h> // size_t
#include <stdint.h> // uint32_t
typedef struct {
uint32_t id;
const char* name;
} Device;
typedef struct {
Device* devices;
size_t numbers;
} DeviceCollection;
#if defined(__cplusplus)
extern "C" {
#endif
extern void create_device_collection(DeviceCollection* collection);
extern void destroy_device_collection(DeviceCollection* collection);
#if defined(__cplusplus)
}
#endif
#endif // EXT_H
// Prototype:
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6d6c2271e3811d55f740b20a00975ecf
use std::ffi::CString;
use std::mem;
use std::os::raw::c_char;
use std::ptr;
#[repr(C)]
pub struct Device {
id: u32,
name: *const c_char,
}
#[repr(C)]
pub struct DeviceCollection {
devices: *mut Device,
numbers: usize,
}
fn create_device(id: u32) -> Device {
// Leak the memory of the CString.
let name = CString::new(id.to_string() + " dev ⚙").unwrap().into_raw();
println!("[leak string @ {:p}]", name);
Device {
id,
name,
}
}
fn create_devices(numbers: u32) -> Vec<Device> {
let mut devices = Vec::<Device>::new();
for i in 0..numbers {
devices.push(
create_device(i)
);
}
devices
}
fn destroy_device(device: &mut Device) {
// Retake the memory of the CString.
unsafe {
println!("[retake string @ {:p}]", device.name);
let _ = CString::from_raw(device.name as *mut c_char);
}
}
fn destroy_devices(devices: &mut Vec<Device>) {
for device in devices {
destroy_device(device);
}
}
fn leak_vec<T>(v: Vec<T>) -> (*mut T, usize) {
// Drop any excess capacity by calling `into_boxed_slice`.
let mut slice = v.into_boxed_slice();
let ptr_and_len = (slice.as_mut_ptr(), slice.len());
mem::forget(slice); // Leak the memory to the external code.
ptr_and_len
}
fn retake_leaked_vec<T>(ptr: &mut *mut T, len: &mut usize) -> Vec<T> {
let v = unsafe {
Vec::from_raw_parts(
*ptr,
*len,
*len
)
};
*ptr = ptr::null_mut();
*len = 0;
v
}
#[no_mangle]
pub extern "C" fn create_device_collection(
collection: *mut DeviceCollection
) {
if collection.is_null() {
return;
}
assert!(!collection.is_null());
let coll = unsafe { &mut *collection };
assert!(coll.devices.is_null() && coll.numbers == 0);
let devices = create_devices(3);
// Leak the memory to C.
let (ptr, len) = leak_vec(devices);
coll.devices = ptr;
coll.numbers = len;
println!("[Leak devies @ {:p}]", coll.devices);
}
#[no_mangle]
pub extern "C" fn destroy_device_collection(
collection: *mut DeviceCollection
) {
if collection.is_null() {
return;
}
assert!(!collection.is_null());
let coll = unsafe { &mut *collection };
assert!(!coll.devices.is_null() && coll.numbers > 0);
// Retake the ownership of the previous leaked memory.
let mut devices = retake_leaked_vec(
&mut coll.devices,
&mut coll.numbers
);
println!("[Retake devies @ {:p}]", devices.as_ptr());
assert_eq!(coll.devices, ptr::null_mut());
assert_eq!(coll.numbers, 0);
// Retake the leaked memory linked to the deivces.
destroy_devices(&mut devices);
// devices will be dropped after program runs out of the scope, or we can
// call drop here directly.
drop(devices);
}
all:
# Build a static library from the Rust file
rustc --crate-type=staticlib ext.rs
# Compile the C file with the static library
# gcc -o sample-c sample.c libext.a
gcc -o sample sample.c -L. -lext
./sample
clean:
rm libext.a
rm sample
#include "ext.h"
#include <assert.h>
#include <stdio.h>
void print_device(Device* device) {
assert(device);
printf("device - id: %d, name: %s\n", device->id, device->name);
}
void print_collection(DeviceCollection* collection) {
assert(collection);
for (size_t i = 0 ; i < collection->numbers ; ++i) {
print_device(&collection->devices[i]);
}
}
int main() {
DeviceCollection collection = { NULL, 0 };
create_device_collection(&collection);
print_collection(&collection);
destroy_device_collection(&collection);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment