ffi Example 2
(py38) fangjuns-MacBook-Pro:code fangjun$ cargo new ffi_ex_2
Creating binary (application) `ffi_ex_2` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
(py38) fangjuns-MacBook-Pro:code fangjun$ cd ffi_ex_2/
(py38) fangjuns-MacBook-Pro:ffi_ex_2 fangjun$ cargo add cc --build
Updating crates.io index
Adding cc v1.2.27 to build-dependencies
Features:
- jobserver
- parallel
Updating crates.io index
Locking 2 packages to latest Rust 1.87.0 compatible versions
Now create build.rs and src/hello.c and change src/main.rs.
Cargo.toml
[package]
name = "ffi_ex_2"
version = "0.1.0"
edition = "2024"
[dependencies]
[build-dependencies]
cc = "1.2.27"
build.rs
fn main() {
cc::Build::new().file("src/hello.c").compile("hello");
println!("cargo:rerun-if-changed=src/hello.c");
}
src/hello.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float sum_float_arr(const float *v, int32_t n) {
float s = 0;
for (int32_t i = 0; i < n; ++i) {
s += v[i];
}
return s;
}
void add_one_vec(int32_t *v, int32_t n) {
for (int32_t i = 0; i < n; ++i) {
v[i] += 1;
}
}
void add_one_scalar(int32_t *i) { *i += 1; }
int32_t *return_i32_array(int32_t *n) {
int32_t *p = malloc(3 * sizeof(int32_t));
p[0] = 100;
p[1] = 101;
p[2] = 102;
printf("Return p %p\n", p);
*n = 3;
return p;
}
void free_i32_array(int32_t *p) {
printf("Now p %p is: %d, %d, %d\n", p, p[0], p[1], p[2]);
free(p);
}
int32_t my_str_len(const char *s) { return s ? strlen(s) : 0; }
void my_upper_str(char *s) {
if (!s) {
return;
};
while (s && *s) {
if (*s >= 'a' && *s <= 'z') {
*s -= 'a' - 'A';
}
s += 1;
}
}
const char *const *my_str_array() {
static const char *s[] = {"first", "second", NULL};
return s;
}
typedef struct MyStruct1 {
int32_t a;
char b;
float c;
const char *s;
double d;
} MyStruct1;
float compute_struct1_sum(const MyStruct1 *s) {
printf("%d, %d, %.3f, %.3f, %s\n", s->a, (int32_t)s->b, s->c, s->d, s->s);
return s->a + s->b + s->c + s->d + strlen(s->s);
}
typedef struct MyStruct2 {
const char *s1;
MyStruct1 s2;
int32_t i;
} MyStruct2;
float compute_struct2_sum(const MyStruct2 *s) {
printf("%s, %d, %d, %.3f, %.3f, %d\n", s->s1, s->s2.a, (int32_t)s->s2.b,
s->s2.c, s->s2.d, s->i);
if (!s->s2.s) {
printf("empty string in s->s2.s\n");
} else {
printf("string in s->s2.s is: %s\n", s->s2.s);
}
return strlen(s->s1) + s->s2.a + s->s2.b + s->s2.c + s->s2.d + s->i;
}
typedef struct MyOpaque MyOpaque;
MyOpaque *create_my_opaque() {
MyStruct1 *s = malloc(sizeof(MyStruct1));
s->a = 1;
s->b = 2;
s->c = 3.5;
s->s = 0;
s->d = 5.5;
printf("return s: %p\n", s);
return (MyOpaque *)s;
}
void free_my_opaque(MyOpaque *p) {
MyStruct1 *s = (MyStruct1 *)p;
printf("get s: %p\n", s);
printf("my opaque: %d, %d, %.3f, %.3f, %s\n", s->a, (int32_t)s->b, s->c, s->d,
s->s ? s->s : "<empty str>");
free(s);
}
src/main.rs
use std::ffi::{CStr, CString};
#[repr(C)]
#[derive(Debug)]
struct MyStruct1 {
a: i32,
b: i8,
c: f32,
p: *const i8,
d: f64,
}
#[repr(C)]
#[derive(Debug)]
struct MyStruct2 {
s1: *const i8,
s2: MyStruct1,
i: i32,
}
#[repr(C)]
struct MyOpaque {
_private: [u8; 0],
}
unsafe extern "C" {
fn sum_float_arr(v: *const f32, n: i32) -> f32;
fn add_one_vec(v: *mut i32, n: i32);
fn add_one_scalar(i: *mut i32);
fn return_i32_array(n: *mut i32) -> *mut i32;
fn free_i32_array(v: *mut i32);
fn my_str_len(v: *const i8) -> i32;
fn my_upper_str(v: *mut i8);
fn my_str_array() -> *const *const i8;
fn compute_struct1_sum(p: *const MyStruct1) -> f32;
fn compute_struct2_sum(p: *const MyStruct2) -> f32;
fn create_my_opaque() -> *const MyOpaque;
fn free_my_opaque(p: *const MyOpaque);
}
fn test_sum_float_arr() {
println!("---test_sum_float_arr---");
let v: Vec<f32> = vec![1., 2., 3.];
let sum: f32;
unsafe {
sum = sum_float_arr((&v).as_ptr(), v.len() as i32);
}
println!("Sum of {v:?} is {sum}");
}
fn test_add_one_vec() {
println!("---test_add_one_vec---");
let mut v: Vec<i32> = vec![1, 2, 3];
println!("Before add one: {v:?}");
unsafe {
add_one_vec((&mut v).as_mut_ptr(), v.len() as i32);
}
println!("After add one: {v:?}");
}
fn test_add_one_scalar() {
println!("---test_add_one_scalar---");
let mut i = 10;
println!("Before add one scalar: {i}");
unsafe {
add_one_scalar(&mut i); //implicit conversion
add_one_scalar((&mut i) as *mut i32); // explicit
}
println!("After add one scalar (two times): {i}");
}
fn test_return_i32_array() {
println!("---test_return_i32_array---");
unsafe {
let mut i = 0;
let p = return_i32_array(&mut i);
println!("i is {i}, p is: {p:p}");
let v = std::slice::from_raw_parts_mut(p, i as usize);
println!("v is {v:?}");
add_one_vec(v.as_mut_ptr(), v.len() as i32);
println!("after adding one, v is {v:?}");
free_i32_array(p);
}
}
fn test_my_str_len() {
println!("---test_my_str_len---");
let a = "hi";
unsafe {
let b = CString::new(a).unwrap();
let n = my_str_len(b.as_ptr());
assert_eq!(n, 2);
}
unsafe {
let n = my_str_len(std::ptr::null::<i8>());
assert_eq!(n, 0);
let p = std::ptr::null::<i8>();
println!("p is {p:p}"); // p ix 0x0
}
}
fn test_my_upper_str() {
println!("---test_my_upper_str---");
let mut a = String::from("hi");
a.push('0');
unsafe {
my_upper_str(a.as_bytes_mut().as_mut_ptr() as *mut i8);
}
a.pop();
assert_eq!(a, "HI");
let mut a = CString::new("world").unwrap();
unsafe {
let p = a.into_raw();
my_upper_str(p);
a = CString::from_raw(p);
}
assert_eq!(a.as_c_str(), c"WORLD");
}
// convert an array of c strings to Vec<CString>
fn test_my_str_array() {
let p: *const *const i8;
unsafe {
p = my_str_array();
println!("{:p}", *p);
println!("{:p}", *p.add(1));
println!("{:p}", *p.add(2));
}
let mut i = 0;
let mut v = Vec::<CString>::new();
unsafe {
loop {
// if *p.add(i) == std::ptr::null() {
if (*p.add(i)).is_null() {
break;
}
let s = CStr::from_ptr(*p.add(i));
v.push(CString::from(s));
i += 1;
}
}
assert_eq!(i, 2);
println!("{v:?}");
}
fn test_compute_struct1_sum() {
let c = c"hello world";
let s = MyStruct1 {
a: 1,
b: 2,
c: 3.0,
p: c.as_ptr(),
d: 4.0,
};
let sum: f32;
unsafe {
sum = compute_struct1_sum(&s);
}
assert_eq!(sum, 10.0 + c.count_bytes() as f32);
}
fn test_compute_struct2_sum() {
let c = c"hello";
let s = MyStruct2{
s1: c.as_ptr().cast(),
s2: MyStruct1 {
a: 1,
b: 2,
c: 3.0,
p: std::ptr::null(),
d: 4.0,
},
i: 10,
};
let sum: f32;
unsafe {
sum = compute_struct2_sum(&s);
}
assert_eq!(sum, c.count_bytes() as f32 + s.s2.a as f32 + s.s2.b as f32 + s.s2.c + s.s2.d as f32+ s.i as f32);
}
fn test_my_opaque() {
let p: *const MyOpaque;
unsafe {
p = create_my_opaque();
}
println!("p is {p:p}");
unsafe {
free_my_opaque(p);
}
}
fn main() {
test_sum_float_arr();
test_add_one_vec();
test_add_one_scalar();
test_return_i32_array();
test_my_str_len();
test_my_upper_str();
test_my_str_array();
test_compute_struct1_sum();
test_compute_struct2_sum();
test_my_opaque();
}