]> piware.de Git - learn-rust.git/blobdiff - call-rust-from-c/src/lib.rs
Call Rust function from C: Complex cases
[learn-rust.git] / call-rust-from-c / src / lib.rs
index f5e43842b908f49ffbbe5e3799f75156aa186521..6a87bea780743861980f7d615fe53ac0ca0a5f45 100644 (file)
@@ -1,3 +1,4 @@
+use std::ffi::{CStr, CString};
 use std::os::raw;
 
 /// Return The Answer.
@@ -14,6 +15,54 @@ pub extern "C" fn answer() -> raw::c_int {
     return 42;
 }
 
+#[no_mangle]
+pub extern "C" fn r_strlen(s: *const raw::c_char) -> usize {
+    unsafe { CStr::from_ptr(s) }.to_bytes().len()
+}
+
+/// Return a vector of pointers as C pointer array
+///
+/// The memory of `vec` gets leaked, as otherwise Rust would free it once the vector
+/// goes out of scope, and C would access invalid memory.
+fn return_c_vec<T>(mut vec: Vec<T>) -> *mut T {
+    let p = vec.as_mut_ptr();
+    // unref vector so that it does not get freed when going out of scope
+    std::mem::forget(vec);
+    p
+}
+
+#[no_mangle]
+pub extern "C" fn r_strlist() -> *mut *const raw::c_uchar {
+    let v = vec![
+        "Hello\0".as_ptr(),
+        "World\0".as_ptr()
+        ];
+    return_c_vec(v)
+}
+
+fn impl_grep<'a>(needle: &str, haystack: &'a str) -> Vec<&'a str> {
+    haystack.lines()
+        .filter(|line| line.contains(needle))
+        .collect()
+}
+
+#[no_mangle]
+pub extern "C" fn r_grep(needle: *const raw::c_char, haystack: *const raw::c_char) -> *mut *const raw::c_char {
+    let haystack_cstr = unsafe { CStr::from_ptr(haystack).to_str() }.unwrap();
+    let strvec = impl_grep(unsafe { CStr::from_ptr(needle).to_str().unwrap() }, haystack_cstr);
+
+    // Vec[str] -> Vec[const char*]
+    let p_vec: Vec<_> = strvec.into_iter()
+        .map(|s| {
+            let s = CString::new(s).unwrap();
+            let p = s.as_ptr();
+            std::mem::forget(s);
+            p
+        })
+        .collect();
+
+    return_c_vec(p_vec)
+}
 
 #[cfg(test)]
 mod tests {
@@ -23,4 +72,9 @@ mod tests {
     fn test_answer() {
         assert_eq!(answer(), 42);
     }
+
+    #[test]
+    fn test_grep() {
+        assert_eq!(impl_grep("ell", "Hello\nworld\ncan you tell?"), vec!["Hello", "can you tell?"]);
+    }
 }