99 lines
2.3 KiB
Rust
99 lines
2.3 KiB
Rust
use std::ffi::{CStr, CString};
|
|
use std::os::raw::{c_char, c_int};
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
pub struct SharedStats {
|
|
pub total: c_int,
|
|
pub product: c_int,
|
|
pub gap: c_int,
|
|
}
|
|
|
|
pub fn compute_stats(left: c_int, right: c_int) -> SharedStats {
|
|
SharedStats {
|
|
total: left.saturating_add(right),
|
|
product: left.saturating_mul(right),
|
|
gap: left.saturating_sub(right).abs(),
|
|
}
|
|
}
|
|
|
|
pub fn make_rust_message(name: &str, left: c_int, right: c_int) -> String {
|
|
let stats = compute_stats(left, right);
|
|
format!(
|
|
"Rust handled {name}: total={}, product={}, gap={}",
|
|
stats.total, stats.product, stats.gap
|
|
)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn rust_compute_stats(
|
|
left: c_int,
|
|
right: c_int,
|
|
out_stats: *mut SharedStats,
|
|
) -> c_int {
|
|
if out_stats.is_null() {
|
|
return 1;
|
|
}
|
|
|
|
out_stats.write(compute_stats(left, right));
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn rust_make_message(
|
|
name: *const c_char,
|
|
left: c_int,
|
|
right: c_int,
|
|
) -> *mut c_char {
|
|
if name.is_null() {
|
|
return string_into_raw("Rust received a null name pointer".to_string());
|
|
}
|
|
|
|
let name = CStr::from_ptr(name).to_string_lossy();
|
|
string_into_raw(make_rust_message(name.as_ref(), left, right))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn rust_free_string(ptr: *mut c_char) {
|
|
if ptr.is_null() {
|
|
return;
|
|
}
|
|
|
|
drop(CString::from_raw(ptr));
|
|
}
|
|
|
|
fn string_into_raw(message: String) -> *mut c_char {
|
|
let sanitized = message.replace('\0', "?");
|
|
match CString::new(sanitized) {
|
|
Ok(c_string) => c_string.into_raw(),
|
|
Err(_) => std::ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn compute_stats_matches_expected_values() {
|
|
assert_eq!(
|
|
compute_stats(9, 4),
|
|
SharedStats {
|
|
total: 13,
|
|
product: 36,
|
|
gap: 5,
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn message_contains_name_and_values() {
|
|
let message = make_rust_message("Ada", 7, 5);
|
|
|
|
assert!(message.contains("Ada"));
|
|
assert!(message.contains("total=12"));
|
|
assert!(message.contains("product=35"));
|
|
assert!(message.contains("gap=2"));
|
|
}
|
|
}
|