Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

Commit

Permalink
Improved FFI.
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgecarleitao committed Aug 23, 2021
1 parent 58eb2db commit 4b43d36
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 4 deletions.
18 changes: 14 additions & 4 deletions arrow-pyarrow-integration-testing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,16 @@ fn to_rust(ob: PyObject, py: Python) -> PyResult<ArrayRef> {
}

fn to_py(array: ArrayRef, py: Python) -> PyResult<PyObject> {
let schema_ptr = ffi::export_field_to_c(&Field::new("", array.data_type().clone(), true));
let array_ptr = ffi::export_array_to_c(array);
let array_ptr = Box::new(ffi::Ffi_ArrowArray::empty());
let schema_ptr = Box::new(ffi::Ffi_ArrowSchema::empty());

let schema_ptr = &*schema_ptr as *const ffi::Ffi_ArrowSchema;
let array_ptr = &*array_ptr as *const ffi::Ffi_ArrowArray;
let array_ptr = Box::into_raw(array_ptr);
let schema_ptr = Box::into_raw(schema_ptr);

unsafe {
ffi::export_field_to_c(&Field::new("", array.data_type().clone(), true), schema_ptr);
ffi::export_array_to_c(array, array_ptr);
};

let pa = py.import("pyarrow")?;

Expand All @@ -109,6 +114,11 @@ fn to_py(array: ArrayRef, py: Python) -> PyResult<PyObject> {
(array_ptr as uintptr_t, schema_ptr as uintptr_t),
)?;

unsafe {
Box::from_raw(array_ptr);
Box::from_raw(schema_ptr);
};

Ok(array.to_object(py))
}

Expand Down
55 changes: 55 additions & 0 deletions examples/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use arrow2::array::{Array, PrimitiveArray};
use arrow2::datatypes::Field;
use arrow2::error::Result;
use arrow2::ffi;
use std::sync::Arc;

unsafe fn export(
array: Arc<dyn Array>,
array_ptr: *mut ffi::Ffi_ArrowArray,
schema_ptr: *mut ffi::Ffi_ArrowSchema,
) {
let field = Field::new("a", array.data_type().clone(), true);
ffi::export_array_to_c(array, array_ptr);
ffi::export_field_to_c(&field, schema_ptr);
}

fn import(
array: Box<ffi::Ffi_ArrowArray>,
schema: &ffi::Ffi_ArrowSchema,
) -> Result<Box<dyn Array>> {
let field = ffi::import_field_from_c(schema)?;
ffi::import_array_from_c(array, &field)
}

fn main() -> Result<()> {
// let's assume that we have an array:
let array = Arc::new(PrimitiveArray::<i32>::from([Some(1), None, Some(123)])) as Arc<dyn Array>;

// the goal is to export this array and import it back via FFI.
// to import, we initialize the structs that will receive the data
let array_ptr = Box::new(ffi::Ffi_ArrowArray::empty());
let schema_ptr = Box::new(ffi::Ffi_ArrowSchema::empty());

// since FFIs work in raw pointers, let's temporarily relinquish ownership so that producers
// can write into it in a thread-safe manner
let array_ptr = Box::into_raw(array_ptr);
let schema_ptr = Box::into_raw(schema_ptr);

// this is where a producer (in this case also us ^_^) writes to the pointers' location.
// `array` here could be anything or not even be available, if this was e.g. from Python.
// Safety: we just allocated the pointers correctly.
unsafe { export(array.clone(), array_ptr, schema_ptr) };

// we can now take ownership back, since we are responsible for deallocating this memory.
// Safety: we just into_raw them.
let array_ptr = unsafe { Box::from_raw(array_ptr) };
let schema_ptr = unsafe { Box::from_raw(schema_ptr) };

// and finally interpret the written memory into a new array.
let new_array = import(array_ptr, schema_ptr.as_ref())?;

// which is equal to the exported array
assert_eq!(array.as_ref(), new_array.as_ref());
Ok(())
}

0 comments on commit 4b43d36

Please sign in to comment.