laminar_db/ffi/memory.rs
1//! FFI memory management functions.
2//!
3//! Provides `extern "C"` functions for freeing memory allocated by `LaminarDB`.
4
5use std::ffi::{c_char, CString};
6
7/// Transfer ownership of a `CString` to a raw pointer.
8///
9/// The caller is responsible for freeing this with `laminar_string_free`.
10pub(crate) fn take_ownership_string(s: CString) -> *mut c_char {
11 s.into_raw()
12}
13
14/// Free a string allocated by `LaminarDB`.
15///
16/// # Arguments
17///
18/// * `s` - String pointer to free, or NULL
19///
20/// # Safety
21///
22/// `s` must be a pointer returned by a laminar function, or NULL.
23/// After calling this function, the pointer is invalid.
24#[no_mangle]
25pub unsafe extern "C" fn laminar_string_free(s: *mut c_char) {
26 if !s.is_null() {
27 // SAFETY: s is non-null and was allocated by CString::into_raw
28 drop(unsafe { CString::from_raw(s) });
29 }
30}
31
32/// Get the `LaminarDB` library version.
33///
34/// # Returns
35///
36/// A pointer to a null-terminated version string. This is a static string
37/// and should NOT be freed.
38///
39/// # Safety
40///
41/// The returned pointer is valid for the lifetime of the process.
42#[no_mangle]
43pub extern "C" fn laminar_version() -> *const c_char {
44 // Static string, never freed
45 static VERSION: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
46 VERSION.as_ptr().cast()
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use std::ffi::CStr;
53
54 #[test]
55 fn test_string_free_null() {
56 // Should not crash
57 // SAFETY: Testing null handling
58 unsafe {
59 laminar_string_free(std::ptr::null_mut());
60 }
61 }
62
63 #[test]
64 fn test_string_round_trip() {
65 let original = "Hello, FFI!";
66 let c_string = CString::new(original).unwrap();
67 let ptr = take_ownership_string(c_string);
68
69 // SAFETY: ptr was just created
70 let retrieved = unsafe { CStr::from_ptr(ptr).to_str().unwrap() };
71 assert_eq!(retrieved, original);
72
73 // SAFETY: ptr is valid
74 unsafe { laminar_string_free(ptr) };
75 }
76
77 #[test]
78 fn test_version() {
79 let ptr = laminar_version();
80 assert!(!ptr.is_null());
81
82 // SAFETY: ptr points to a static string
83 let version = unsafe { CStr::from_ptr(ptr).to_str().unwrap() };
84 assert!(!version.is_empty());
85 // Should contain version number pattern
86 assert!(version.chars().any(|c| c.is_ascii_digit()));
87 }
88}