I need pass a vector of Rust string to a C variadic function. But I can't figure out what is the expected(CString
, [u8]
..) format.
References:
- API reference: isc_event_block
- My extern C declaration: ibase
- How I'm calling: que_events
- C example: api16
- API implementation: isc_event_block
My api16
example code version:
strcpy (ids[0], "evento");
length = (short) isc_event_block((char **) &event_buffer, (char **) &result_buffer, 1, ids[0], 0);
printf("event_buffer: '%s' %d\n", event_buffer, sizeof(event_buffer));
printf("result_buffer: '%s' %d\n", result_buffer, sizeof(result_buffer));
Result of version of api16
:
event_buffer: 'evento' 8
result_buffer: '' 8
My Rust code:
let mut event_buffer: Vec<u8> = Vec::with_capacity(256);
let mut result_buffer: Vec<u8> = Vec::with_capacity(256);
let mut len = 0;
let names = "evento".to_string();
unsafe {
len = self.ibase.isc_event_block()(
event_buffer.as_mut_ptr() as *mut _,
result_buffer.as_mut_ptr() as *mut _,
names.len() as u16,
names.as_ptr()
);
event_buffer.set_len(len as usize);
result_buffer.set_len(len as usize);
}
println!("{:?} {:?}", len, names);
println!("{:x?} {:x?}", event_buffer, result_buffer);
println!("{:?} {:?}", String::from_utf8_lossy(&event_buffer.clone()), String::from_utf8_lossy(&result_buffer.clone()));
Result of my Rust code:
12 ["evento"]
[e0, 4f, 51, 28, a8, 7f, 0, 0, 0, 0, 0, 0] [0, 50, 51, 28, a8, 7f, 0, 0, 0, 0, 0, 0]
"�OQ(�\u{7f}\0\0\0\0\0\0" "\0PQ(�\u{7f}\0\0\0\0\0\0"
I already tried use CString
or CStr
, like here.
What am I doing wrong?
CodePudding user response:
You're doing multiple things wrong in the rust version. For the first two arguments, you're intended to pass a pointer to a location that can hold a single pointer to a byte buffer. Instead, you're passing in a pointer to a byte buffer, so a pointer is getting written into that buffer, which isn't what you want.
Secondly, the id_count
parameter corresponds to the number of strings you're passing as the variadic parameters, rather than the length of a single variadic string, meaning your c code just reads a bunch of uninitialized memory, which definitely isn't what you want. Additionally, that string does need to be null-terminated, and it isn't in your example, you do need CString
. What you really want is something like this:
use std::ffi::{c_char, c_long, c_ushort, CStr, CString};
use std::ptr;
use std::slice;
use std::str;
fn main() {
let mut event_buffer = ptr::null_mut();
let mut result_buffer = ptr::null_mut();
let names = CString::new("evento").unwrap();
let len = unsafe {
isc_event_block(
&mut event_buffer,
&mut result_buffer,
1,
names.as_ptr() as *mut c_char,
)
};
debug_assert!(!event_buffer.is_null() && !result_buffer.is_null());
let event_slice = unsafe { slice::from_raw_parts(event_buffer.cast(), len as usize) };
let result_slice = unsafe { slice::from_raw_parts(event_buffer.cast(), len as usize) };
let event_str = str::from_utf8(event_slice).unwrap();
let result_str = str::from_utf8(result_slice).unwrap();
println!("event: {event_str}");
println!("result: {result_str}");
}
Adding a simple stub to print the string passed in, and write a couple of strings into the buffers, I got this:
"evento"
event: some events
result: some events