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();
}