I need a binary tree data structure in C, and there is already exist a binary tree implementation in Rust. So I decided to wrap that.
I made a C-compatible struct contains raw pointer to BTreeMap and add some methods that take a pointer to my wrapper struct.
The problem is in module test. The test is passed if I using my methods only, but whenever I put the println! macro between method calls, The test failed.
use std::collections::BTreeMap;
#[repr(C)]
pub struct my_btree {
btree: *mut BTreeMap<u64, u64>,
}
#[no_mangle]
pub extern "C" fn my_btree_new() -> *mut my_btree {
let boxed = Box::new(BTreeMap::<u64, u64>::new());
let mut ret = my_btree {
btree: Box::into_raw(boxed),
};
&mut ret as *mut my_btree
}
#[no_mangle]
pub extern "C" fn my_btree_insert(
btree: *mut my_btree,
key: u64,
val: u64,
) -> bool {
unsafe {
let mut boxed = Box::from_raw((*btree).btree);
let contains = (*boxed).contains_key(&key);
if contains {
return false;
}
(*boxed).insert(key, val);
println!("{:?}", boxed);
Box::into_raw(boxed);
return true;
}
}
#[no_mangle]
pub extern "C" fn my_btree_contains(btree: *mut my_btree, key: u64) -> bool {
unsafe {
let boxed = Box::from_raw((*btree).btree);
println!("{:?}", boxed);
let ret = (*boxed).contains_key(&key);
Box::into_raw(boxed);
ret
}
}
#[no_mangle]
pub extern "C" fn my_btree_free(btree: *mut my_btree) {
unsafe {
let _boxed = Box::from_raw((*btree).btree);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert() {
let map = my_btree_new();
my_btree_insert(map, 1, 1);
my_btree_insert(map, 2, 2);
my_btree_insert(map, 3, 30);
let err = my_btree_contains(map, 1);
assert_eq!(err, true);
println!("???"); // If this line commented out, then test success without error.
my_btree_free(map);
}
}
when I run the test with below command,
$ cargo test -- --nocapture
In my terminal,
running 1 test
{1: 1}
{1: 1, 2: 2}
{1: 1, 2: 2, 3: 30}
{1: 1, 2: 2, 3: 30}
???
error: test failed, to rerun pass '--lib'
But if I comment out the println!("???"); then test pass without any error.
And if I put println between my_btree_insert calls, it failed in next call. It's weird. Why this happened?
You've got several issues.
In my_btree_new, you construct an instance of my_btree on the stack (ie. local to the function) and then return a pointer to it.
In my_btree_insert, you take your pointer, then construct a Box around it. Before returning, you deconstruct the Box so that the item won't be freed - but you also have an early return path that does not deconstruct the Box. Your test case does not exercise that code path, but I expect it would crash if it did.
Why does it only crash when you insert the println!? Simple - the extra function call(s) that are generated trash the area of the stack containing the my_btree.
Here are a few suggestions for fixing it up:
You do not really need the wrapper struct (at least for this basic example). But if you are going to have one, the whole struct should be on the heap, not just the BTree structure.
You don't need to box/unbox the pointer in every method; it is not adding value and is just increasing the chances you will forget to rebox it in some code path, resulting in a double-free crash. Only re-box it in the my_btree_free function.
You have made all these functions safe with the code inside wrapped in an unsafe block. That's not really correct - the compiler cannot verify that the pointer being supplied is correct, therefore the function is not safe (or putting it another way, a safe function should not crash regardless of the arguments you supply).
Here is a version that works:
use std::collections::BTreeMap;
#[repr(C)]
pub struct my_btree {
btree: BTreeMap<u64, u64>,
}
#[no_mangle]
pub extern "C" fn my_btree_new() -> *mut my_btree {
let boxed = Box::new(my_btree { btree: BTreeMap::<u64, u64>::new() } );
Box::into_raw(boxed)
}
#[no_mangle]
pub unsafe extern "C" fn my_btree_insert(
btree: *mut my_btree,
key: u64,
val: u64,
) -> bool {
let contains = (*btree).btree.contains_key(&key);
if contains {
return false;
}
(*btree).btree.insert(key, val);
return true;
}
#[no_mangle]
pub unsafe extern "C" fn my_btree_contains(btree: *mut my_btree, key: u64) -> bool {
(*btree).btree.contains_key(&key)
}
#[no_mangle]
pub unsafe extern "C" fn my_btree_free(btree: *mut my_btree) {
let _boxed = Box::from_raw(btree);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert() {
let map = my_btree_new();
unsafe {
my_btree_insert(map, 1, 1);
my_btree_insert(map, 2, 2);
my_btree_insert(map, 3, 30);
let err = my_btree_contains(map, 1);
assert_eq!(err, true);
println!("???"); // If this line commented out, then test success without error.
my_btree_free(map);
}
}
}
Related
I have a Rust library that returns Vecs of size that cannot be predicted by the caller, and I'm looking for an elegant way to implement the FFI interface.
Since Vecs can't be passed over FFI, I understand I need to first return the length, and then fill a buffer with the contents.
Here's my current solution, which works by manually managing memory of the Vec and trusting the caller to
Compute the FfiVec
Create a buffer of appropriate size to copy into
Copy from the FfiVec, cleaning it up in the process
#[repr(C)]
pub struct FfiVec<T: Sized> {
ptr: *mut Vec<T>,
len: c_uint, // necessary for C process to know length of Rust Vec
}
impl<T: Sized> FfiVec<T> {
pub fn from_vec(v: Vec<T>) -> Self {
let len = v.len() as c_uint;
FfiVec {
ptr: Box::into_raw(Box::new(v)),
len,
}
}
pub fn drop(self) {
unsafe {
std::ptr::drop_in_place(self.ptr);
std::alloc::dealloc(self.ptr as *mut u8, Layout::new::<Vec<T>>())
}
}
}
#[no_mangle]
pub extern compute_my_vec() -> FfiVec<u8> {...}
#[no_mangle]
pub extern copy_from_my_vec(ffi_vec: FfiVec<u8>, buffer: *mut u8) -> {
let len = c_vec.len as usize;
let buffer_slice = unsafe { std::slice::from_raw_parts_mut(buffer, len) };
for (i, &item) in c_vec.slice().iter().take(len).enumerate() {
buffer_slice[i] = item;
}
c_vec.drop()
}
Is there a simpler way to do this? Or a common library that can do this for me?
bindgen has nicely given me
extern "C" {
pub fn Hacl_Bignum4096_new_bn_from_bytes_be(len: u32, b: *mut u8) -> *mut u64;
}
returning something of type *mut u64. Unfortunately there is no reliable way (that I have found) to determine how many u64s are allocated. This makes is very hard (for me) to extract the data pointed to into something I can safely persist in a Rust struct instance.
As a consequence, any time I want to use any function from the Hacl library I have to perform that conversion and free up the created pointers in an unsafe block.
impl Bignum {
/// Returns true if self < other
pub fn lt(&self, other: &Bignum) -> Result<bool, Error> {
let hacl_result: HaclBnWord;
unsafe {
let a = self.get_hacl_bn()?;
let b = other.get_hacl_bn()?;
hacl_result = Hacl_Bignum4096_lt_mask(a, b);
free_hacl_bn(a);
free_hacl_bn(b);
}
Ok(hacl_result != 0 as HaclBnWord)
}
}
unsafe fn get_hacl_bn(&self) is suitably defined and calls Hacl_Bignum4096_new_bn_from_bytes_be() appropriately. And unsafe fn free_hacl_bn(bn: HaclBnType) also lives in this module.
I haven't benchmarked anything yet, but having to perform the conversion to a Hacl_Bignum from bytes each and every time feels wasteful.
So is there a way to determine the size of what is pointed to or is there a way to copy the data out of it into something safe?
You write: "having to perform the conversion to a Hacl_Bignum from bytes each and every time feels wasteful". It seems like you are not letting the library do its job. You should not keep a copy of the bignum data in your Rust struct Bignum, but only the pointer you get from the library. Something like:
extern "C" {
pub fn Hacl_Bignum4096_new_bn_from_bytes_be(len: u32, b: *mut u8) -> *mut u64;
pub fn Hacl_Bignum4096_lt_mask(a: *mut u64, b: *mut u64) -> u64;
}
struct Bignum {
handle: *mut u64,
}
struct BignumError {}
impl Bignum {
pub fn new(bytes: &mut [u8]) -> Result<Self, BignumError> {
unsafe {
let handle =
Hacl_Bignum4096_new_bn_from_bytes_be(bytes.len() as u32, bytes.as_mut_ptr());
if handle.is_null() {
return Err(BignumError {});
} else {
Ok(Self { handle })
}
}
}
/// Returns true if self < other
pub fn lt(&self, other: &Bignum) -> bool {
unsafe { Hacl_Bignum4096_lt_mask(self.handle, other.handle) == u64::MAX }
}
}
PS. I used the comments in this file, which seems to be the library in question.
i am working on a Quactel module in rust language .i have an object(Tracking_mgr) and i want to call a method of that object (parse_from_byte) from interrupt handler (CallBack_NMEA_Hdlr) . but i can't because scop of object (in main) is different from scope of interrupt .
#![no_std]
#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(destructuring_assignment)]
include!("./bindings.rs");
extern crate alloc;
extern crate cty;
extern crate nmea0183;
extern crate ql_alloc;
use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use core::convert::TryInto;
use core::slice;
use nmea0183::{ParseResult, Parser};
#[panic_handler]
fn my_panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
pub fn printUart(mut string: String) {
unsafe {
Ql_UART_Write(10, string.as_mut_ptr(), string.len() as u32);
}
}
pub struct Tracking_mgr {
point: Point,
parser: Parser,
}
impl Tracking_mgr {
pub fn new() -> Tracking_mgr {
Tracking_mgr {
point: Point::new(),
parser: Parser::new(),
}
}
pub fn parse_from_byte(&mut self, buff: u8) {
self.parser.parse_from_byte(buff);
}
}
pub trait GpsInterface {
fn power_on();
fn power_off();
}
impl GpsInterface for Tracking_mgr {
fn power_on() {
unsafe {
Ql_GNSS_PowerOn(4, Some(CallBack_NMEA_Hdlr), ::core::ptr::null_mut());
}
}
fn power_off() {
unsafe {
Ql_GNSS_PowerDown();
}
}
}
pub struct Point {
point: [u8; 5],
}
impl Point {
pub fn new() -> Point {
Point {
point: [0, 0, 0, 0, 0],
}
}
}
pub extern "C" fn CallBack_NMEA_Hdlr(
nmea_buff: *mut u8,
len: u16,
customizePara: *mut cty::c_void,
) {
}
pub extern "C" fn CallBack_UART_Notify(
port: Enum_SerialPort,
event: Enum_UARTEventType,
pinLevel: bool,
customizePara: *mut cty::c_void,
) {
printUart("CallBack_UART_Notify".to_string());
}
#[no_mangle]
pub extern "C" fn proc_main_task(taskId: i32) {
let mut msg = ST_MSG {
message: 0,
param1: 0,
param2: 0,
srcTaskId: 0,
};
loop {
unsafe {
Ql_OS_GetMessage(&mut msg);
}
match msg.message {
MSG_ID_RIL_READY => unsafe {
Ql_UART_Register(10, Some(CallBack_UART_Notify), ::core::ptr::null_mut());
Ql_UART_Open(10, 115200, 1);
Ql_GPIO_Init(0, 1, 1, 0);
Ql_GNSS_PowerOn(4, Some(CallBack_NMEA_Hdlr), ::core::ptr::null_mut());
},
_ => (),
}
}
}
i tried static mut tracking_mgr: Tracking_mgr = Tracking_mgr::new(); before but i got this error "calls in statics are limited to constant functions, tuple structs and tuple variants"
can somebody help me ? how can i call a method of object that defined in a main() from interrupt handler? thanks!
As the error message indicates, you can only call const functions to initialize a static. In principle, you could try to make Tracking_mgr::new a const function (pub const fn new()), but that won't work, because it calls other methods that themselves aren't const (Point::new could be const, but Parser::new couldn't, I think).
To solve this, you can store an Option in your static:
static mut tracking_mgr: Option<Tracking_mgr> = None;
And initialize it in your main function:
unsafe { tracking_mgr = Some(Tracking_mgr::new()); }
It looks like you're not building on top of the standard embedded Rust stack (cortex-m/cortex-m-rt). If you were, I'd recommend using RTIC, as that solves the same problem in a much more safe and elegant way.
I need to write a function that returns array of u16 integers in Rust. This function then should be used by FFI.
extern crate libc;
use libc::{uint16_t};
#[no_mangle]
pub extern fn ffi_test() -> *const uint16_t {
let test: [u16;4] = [1,2,3,4];
test.as_ptr()
}
Rust code compiles without errors. I used Ruby to test the ffi call:
# coding: utf-8
require 'ffi'
module MyMod
extend FFI::Library
ffi_lib 'my_ffi_test_lib'
attach_function :ffi_test, [], :pointer
end
a_ptr = MyMod.ffi_test
size = 4
result_array = a_ptr.read_array_of_uint16(size)
p result_array
But the results are totally wrong (expected: [1, 2, 3, 4]):
$ ruby ffi_test.rb
[57871, 25191, 32767, 0]
As if I am reading totally diffirent memory addr. I assume maybe that I should not use #as_ptr() on Rust array?
EDIT
As per recommendation of #FrenchBoiethios I tried to box the array:
extern crate libc;
use libc::{uint16_t};
#[no_mangle]
pub extern fn ffi_test() -> *mut uint16_t {
let test: [u16;4] = [1,2,3,4];
let b = Box::new(test);
Box::into_raw(b)
}
This gives compile error:
note: expected type `std::boxed::Box<u16>`
found type `std::boxed::Box<[u16; 4]>`
Your array is on the stack, so there is a lifetime issue when you returns it as a pointer (returned pointer to a local variable). You must allocate it in the heap:
#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
let mut test = vec![1, 2, 3, 4];
let ptr = test.as_mut_ptr();
std::mem::forget(test); // so that it is not destructed at the end of the scope
ptr
}
or
#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
let test = Box::new([1u16, 2, 3, 4]); // type must be explicit here...
Box::into_raw(test) as *mut _ // ... because this cast can convert
// *mut [i32; 4] to *mut u16
}
I am trying to learn Rust ffi, those implementations are a frankenstein creation from different sources in internet. So take it with a grain of salt.
Currently I am with two approaches:
a) Remove the array from rust GC and return the point. User need to promise to call free later.
#[repr(C)]
pub struct V2 {
pub x: i32,
pub y: i32,
}
#[repr(C)]
struct Buffer {
len: i32,
data: *mut V2,
}
#[no_mangle]
extern "C" fn generate_data() -> Buffer {
let mut buf = vec![V2 { x: 1, y: 0 }, V2 { x: 2, y: 0}].into_boxed_slice();
let data = buf.as_mut_ptr();
let len = buf.len() as i32;
std::mem::forget(buf);
Buffer { len, data }
}
#[no_mangle]
extern "C" fn free_buf(buf: Buffer) {
let s = unsafe { std::slice::from_raw_parts_mut(buf.data, buf.len as usize) };
let s = s.as_mut_ptr();
unsafe {
Box::from_raw(s);
}
}
b) Send the array through FFI callback function. User need to promise to not keep references, but dont need to call free.
#[no_mangle]
pub extern "C" fn context_get_byte_responses(callback: extern "stdcall" fn (*mut u8, i32)) -> bool {
let bytes: Vec<u8> = vec![];
callback(bytes.as_mut_ptr(), bytes.len() as i32);
true
}
Suppose I have the following Rust library:
// lib.rs
#![crate_type = staticlib]
#[no_mangle]
pub extern fn do_something(number: i32) {
// something
}
#[no_mangle]
pub extern fn do_something_else(collection: &Vec<i32>) {
// something
}
I know that, to call do_something from C, I'd just need to declare an extern function taking an int32_t, but is it possible to call do_something_else? If so, how?
You can, but the better question is should you?
Since you cannot construct a Vec from C, you'd have to construct it in Rust and then return a pointer to C. C code would own the pointer to the Vec and would then pass it back when calling do_something_else.
Then there's the problem that you can't really modify the Vec in C either, other than by creating new FFI methods that mirror all of the Rust methods.
You also probably shouldn't take a &Vec<i32> because Rust references are guaranteed to not be NULL, and there's nothing that enforces that when called from C. It's better to take a *const Vec<i32>, assert that it's non-NULL and convert it to a reference.
Chances are that you want to accept a C array through the FFI boundary. C arrays are a pointer and a length, so you'd accept both and reconstitute a Rust slice (since you wouldn't own the array):
use std::slice;
pub extern fn do_something_else(p: *const i32, len: libc::size_t) {
let slice = unsafe {
assert!(!p.is_null());
slice::from_raw_parts(p, len)
};
}
Obligatory link to The Rust FFI Omnibus.
If you really needed to do what you asked, it would probably look something like this:
extern crate libc;
#[no_mangle]
pub extern fn make_vec() -> *mut Vec<i32> {
Box::into_raw(Box::new(Vec::new()))
}
#[no_mangle]
pub extern fn add_number(vec: *mut Vec<i32>, val: libc::int32_t) {
let vec = unsafe {
assert!(!vec.is_null());
&mut *vec
};
vec.push(val);
}
#[no_mangle]
pub extern fn print_vec(vec: *const Vec<i32>) {
let vec = unsafe {
assert!(!vec.is_null());
&*vec
};
println!("{:?}", vec);
}
#[no_mangle]
pub extern fn drop_vec(vec: *mut Vec<i32>) {
unsafe {
assert!(!vec.is_null());
Box::from_raw(vec);
}
}
And would be used like (untested):
// Add extern declarations
int main(int argc, char *argv[]) {
void *v = make_vec(); // Use a real typedef here
add_number(v, 42);
print_vec(v);
drop_vec(v);
}
You'd want to run this under valgrind to make sure I didn't do anything stupid memory-wise.