Last active
February 13, 2019 19:17
-
-
Save ChunMinChang/1e5410f3a7cb8c5bbf066e7dae09d7bc to your computer and use it in GitHub Desktop.
Pass arrays from Rust to C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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