Skip to main content

ghostflow_ffi/
lib.rs

1//! C FFI bindings for GhostFlow
2//!
3//! This module provides C-compatible APIs for using GhostFlow from other languages
4//! like Python, C++, Java, Go, etc.
5
6use std::ffi::{CStr, CString};
7use std::os::raw::{c_char, c_float, c_int};
8use std::ptr;
9use std::slice;
10use ghostflow_core::Tensor;
11
12/// Opaque handle to a GhostFlow tensor
13#[repr(C)]
14pub struct GhostFlowTensor {
15    _private: [u8; 0],
16}
17
18/// Opaque handle to a GhostFlow model
19#[repr(C)]
20pub struct GhostFlowModel {
21    _private: [u8; 0],
22}
23
24/// Error codes
25#[repr(C)]
26#[derive(Debug, Clone, Copy, PartialEq)]
27pub enum GhostFlowError {
28    Success = 0,
29    InvalidShape = 1,
30    InvalidData = 2,
31    NullPointer = 3,
32    AllocationFailed = 4,
33    ComputationFailed = 5,
34    Unknown = 99,
35}
36
37/// Convert Rust Tensor to opaque pointer
38fn tensor_to_ptr(tensor: Tensor) -> *mut GhostFlowTensor {
39    Box::into_raw(Box::new(tensor)) as *mut GhostFlowTensor
40}
41
42/// Convert opaque pointer back to Rust Tensor reference
43unsafe fn ptr_to_tensor<'a>(ptr: *const GhostFlowTensor) -> Option<&'a Tensor> {
44    if ptr.is_null() {
45        None
46    } else {
47        Some(&*(ptr as *const Tensor))
48    }
49}
50
51/// Convert opaque pointer back to mutable Rust Tensor reference
52unsafe fn ptr_to_tensor_mut<'a>(ptr: *mut GhostFlowTensor) -> Option<&'a mut Tensor> {
53    if ptr.is_null() {
54        None
55    } else {
56        Some(&mut *(ptr as *mut Tensor))
57    }
58}
59
60/// Initialize GhostFlow library
61#[no_mangle]
62pub extern "C" fn ghostflow_init() -> GhostFlowError {
63    GhostFlowError::Success
64}
65
66/// Get GhostFlow version string
67#[no_mangle]
68pub extern "C" fn ghostflow_version() -> *const c_char {
69    let version = CString::new(env!("CARGO_PKG_VERSION")).unwrap();
70    version.into_raw()
71}
72
73/// Free a version string
74#[no_mangle]
75pub unsafe extern "C" fn ghostflow_free_string(s: *mut c_char) {
76    if !s.is_null() {
77        let _ = CString::from_raw(s);
78    }
79}
80
81/// Create a new tensor from data
82///
83/// # Arguments
84/// * `data` - Pointer to float array
85/// * `data_len` - Length of data array
86/// * `shape` - Pointer to shape array
87/// * `shape_len` - Length of shape array
88/// * `out` - Output pointer to store the created tensor
89///
90/// # Returns
91/// Error code
92#[no_mangle]
93pub unsafe extern "C" fn ghostflow_tensor_create(
94    data: *const c_float,
95    data_len: usize,
96    shape: *const usize,
97    shape_len: usize,
98    out: *mut *mut GhostFlowTensor,
99) -> GhostFlowError {
100    if data.is_null() || shape.is_null() || out.is_null() {
101        return GhostFlowError::NullPointer;
102    }
103
104    let data_slice = slice::from_raw_parts(data, data_len);
105    let shape_slice = slice::from_raw_parts(shape, shape_len);
106
107    match Tensor::from_slice(data_slice, shape_slice) {
108        Ok(tensor) => {
109            *out = tensor_to_ptr(tensor);
110            GhostFlowError::Success
111        }
112        Err(_) => GhostFlowError::InvalidShape,
113    }
114}
115
116/// Create a tensor filled with zeros
117#[no_mangle]
118pub unsafe extern "C" fn ghostflow_tensor_zeros(
119    shape: *const usize,
120    shape_len: usize,
121    out: *mut *mut GhostFlowTensor,
122) -> GhostFlowError {
123    if shape.is_null() || out.is_null() {
124        return GhostFlowError::NullPointer;
125    }
126
127    let shape_slice = slice::from_raw_parts(shape, shape_len);
128    let numel: usize = shape_slice.iter().product();
129    let data = vec![0.0f32; numel];
130
131    match Tensor::from_slice(&data, shape_slice) {
132        Ok(tensor) => {
133            *out = tensor_to_ptr(tensor);
134            GhostFlowError::Success
135        }
136        Err(_) => GhostFlowError::InvalidShape,
137    }
138}
139
140/// Create a tensor filled with ones
141#[no_mangle]
142pub unsafe extern "C" fn ghostflow_tensor_ones(
143    shape: *const usize,
144    shape_len: usize,
145    out: *mut *mut GhostFlowTensor,
146) -> GhostFlowError {
147    if shape.is_null() || out.is_null() {
148        return GhostFlowError::NullPointer;
149    }
150
151    let shape_slice = slice::from_raw_parts(shape, shape_len);
152    let numel: usize = shape_slice.iter().product();
153    let data = vec![1.0f32; numel];
154
155    match Tensor::from_slice(&data, shape_slice) {
156        Ok(tensor) => {
157            *out = tensor_to_ptr(tensor);
158            GhostFlowError::Success
159        }
160        Err(_) => GhostFlowError::InvalidShape,
161    }
162}
163
164/// Free a tensor
165#[no_mangle]
166pub unsafe extern "C" fn ghostflow_tensor_free(tensor: *mut GhostFlowTensor) {
167    if !tensor.is_null() {
168        let _ = Box::from_raw(tensor as *mut Tensor);
169    }
170}
171
172/// Get tensor shape
173#[no_mangle]
174pub unsafe extern "C" fn ghostflow_tensor_shape(
175    tensor: *const GhostFlowTensor,
176    out_shape: *mut usize,
177    out_ndim: *mut usize,
178) -> GhostFlowError {
179    if tensor.is_null() || out_shape.is_null() || out_ndim.is_null() {
180        return GhostFlowError::NullPointer;
181    }
182
183    if let Some(t) = ptr_to_tensor(tensor) {
184        let shape = t.dims();
185        *out_ndim = shape.len();
186        ptr::copy_nonoverlapping(shape.as_ptr(), out_shape, shape.len());
187        GhostFlowError::Success
188    } else {
189        GhostFlowError::NullPointer
190    }
191}
192
193/// Get tensor data
194#[no_mangle]
195pub unsafe extern "C" fn ghostflow_tensor_data(
196    tensor: *const GhostFlowTensor,
197    out_data: *mut c_float,
198    out_len: *mut usize,
199) -> GhostFlowError {
200    if tensor.is_null() || out_data.is_null() || out_len.is_null() {
201        return GhostFlowError::NullPointer;
202    }
203
204    if let Some(t) = ptr_to_tensor(tensor) {
205        let data = t.data_f32();
206        *out_len = data.len();
207        ptr::copy_nonoverlapping(data.as_ptr(), out_data, data.len());
208        GhostFlowError::Success
209    } else {
210        GhostFlowError::NullPointer
211    }
212}
213
214/// Add two tensors
215#[no_mangle]
216pub unsafe extern "C" fn ghostflow_tensor_add(
217    a: *const GhostFlowTensor,
218    b: *const GhostFlowTensor,
219    out: *mut *mut GhostFlowTensor,
220) -> GhostFlowError {
221    if a.is_null() || b.is_null() || out.is_null() {
222        return GhostFlowError::NullPointer;
223    }
224
225    let tensor_a = match ptr_to_tensor(a) {
226        Some(t) => t,
227        None => return GhostFlowError::NullPointer,
228    };
229
230    let tensor_b = match ptr_to_tensor(b) {
231        Some(t) => t,
232        None => return GhostFlowError::NullPointer,
233    };
234
235    match tensor_a.add(&tensor_b) {
236        Ok(result) => {
237            unsafe { *out = tensor_to_ptr(result); }
238            GhostFlowError::Success
239        }
240        Err(_) => GhostFlowError::ComputationFailed,
241    }
242}
243
244/// Multiply two tensors element-wise
245#[no_mangle]
246pub unsafe extern "C" fn ghostflow_tensor_mul(
247    a: *const GhostFlowTensor,
248    b: *const GhostFlowTensor,
249    out: *mut *mut GhostFlowTensor,
250) -> GhostFlowError {
251    if a.is_null() || b.is_null() || out.is_null() {
252        return GhostFlowError::NullPointer;
253    }
254
255    let tensor_a = match ptr_to_tensor(a) {
256        Some(t) => t,
257        None => return GhostFlowError::NullPointer,
258    };
259
260    let tensor_b = match ptr_to_tensor(b) {
261        Some(t) => t,
262        None => return GhostFlowError::NullPointer,
263    };
264
265    match tensor_a.mul(&tensor_b) {
266        Ok(result) => {
267            unsafe { *out = tensor_to_ptr(result); }
268            GhostFlowError::Success
269        }
270        Err(_) => GhostFlowError::ComputationFailed,
271    }
272}
273
274/// Matrix multiplication
275#[no_mangle]
276pub unsafe extern "C" fn ghostflow_tensor_matmul(
277    a: *const GhostFlowTensor,
278    b: *const GhostFlowTensor,
279    out: *mut *mut GhostFlowTensor,
280) -> GhostFlowError {
281    if a.is_null() || b.is_null() || out.is_null() {
282        return GhostFlowError::NullPointer;
283    }
284
285    let tensor_a = match ptr_to_tensor(a) {
286        Some(t) => t,
287        None => return GhostFlowError::NullPointer,
288    };
289
290    let tensor_b = match ptr_to_tensor(b) {
291        Some(t) => t,
292        None => return GhostFlowError::NullPointer,
293    };
294
295    match tensor_a.matmul(tensor_b) {
296        Ok(result) => {
297            *out = tensor_to_ptr(result);
298            GhostFlowError::Success
299        }
300        Err(_) => GhostFlowError::ComputationFailed,
301    }
302}
303
304/// Reshape a tensor
305#[no_mangle]
306pub unsafe extern "C" fn ghostflow_tensor_reshape(
307    tensor: *const GhostFlowTensor,
308    new_shape: *const usize,
309    new_shape_len: usize,
310    out: *mut *mut GhostFlowTensor,
311) -> GhostFlowError {
312    if tensor.is_null() || new_shape.is_null() || out.is_null() {
313        return GhostFlowError::NullPointer;
314    }
315
316    let t = match ptr_to_tensor(tensor) {
317        Some(t) => t,
318        None => return GhostFlowError::NullPointer,
319    };
320
321    let shape_slice = slice::from_raw_parts(new_shape, new_shape_len);
322
323    match t.reshape(shape_slice) {
324        Ok(result) => {
325            *out = tensor_to_ptr(result);
326            GhostFlowError::Success
327        }
328        Err(_) => GhostFlowError::InvalidShape,
329    }
330}
331
332/// Get last error message
333#[no_mangle]
334pub extern "C" fn ghostflow_get_last_error() -> *const c_char {
335    let msg = CString::new("No error").unwrap();
336    msg.into_raw()
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    #[test]
344    fn test_tensor_create() {
345        unsafe {
346            let data = vec![1.0f32, 2.0, 3.0, 4.0];
347            let shape = vec![2, 2];
348            let mut tensor: *mut GhostFlowTensor = ptr::null_mut();
349
350            let result = ghostflow_tensor_create(
351                data.as_ptr(),
352                data.len(),
353                shape.as_ptr(),
354                shape.len(),
355                &mut tensor,
356            );
357
358            assert_eq!(result, GhostFlowError::Success);
359            assert!(!tensor.is_null());
360
361            ghostflow_tensor_free(tensor);
362        }
363    }
364
365    #[test]
366    fn test_tensor_zeros() {
367        unsafe {
368            let shape = vec![2, 3];
369            let mut tensor: *mut GhostFlowTensor = ptr::null_mut();
370
371            let result = ghostflow_tensor_zeros(
372                shape.as_ptr(),
373                shape.len(),
374                &mut tensor,
375            );
376
377            assert_eq!(result, GhostFlowError::Success);
378            assert!(!tensor.is_null());
379
380            ghostflow_tensor_free(tensor);
381        }
382    }
383}