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

Made importing from FFI unsafe #458

Merged
merged 1 commit into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions arrow-pyarrow-integration-testing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ fn to_rust_array(ob: PyObject, py: Python) -> PyResult<Arc<dyn Array>> {
(array_ptr as Py_uintptr_t, schema_ptr as Py_uintptr_t),
)?;

let field = ffi::import_field_from_c(schema.as_ref()).map_err(PyO3ArrowError::from)?;
let array = ffi::import_array_from_c(array, &field).map_err(PyO3ArrowError::from)?;
let field = unsafe { ffi::import_field_from_c(schema.as_ref()).map_err(PyO3ArrowError::from)? };
let array = unsafe { ffi::import_array_from_c(array, &field).map_err(PyO3ArrowError::from)? };

Ok(array.into())
}
Expand Down Expand Up @@ -108,7 +108,7 @@ fn to_rust_field(ob: PyObject, py: Python) -> PyResult<Field> {
// this changes the pointer's memory and is thus unsafe. In particular, `_export_to_c` can go out of bounds
ob.call_method1(py, "_export_to_c", (schema_ptr as Py_uintptr_t,))?;

let field = ffi::import_field_from_c(schema.as_ref()).map_err(PyO3ArrowError::from)?;
let field = unsafe { ffi::import_field_from_c(schema.as_ref()).map_err(PyO3ArrowError::from)? };

Ok(field)
}
Expand Down
5 changes: 3 additions & 2 deletions examples/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ unsafe fn export(
ffi::export_field_to_c(&field, schema_ptr);
}

fn import(
unsafe fn import(
array: Box<ffi::Ffi_ArrowArray>,
schema: &ffi::Ffi_ArrowSchema,
) -> Result<Box<dyn Array>> {
Expand Down Expand Up @@ -47,7 +47,8 @@ fn main() -> Result<()> {
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())?;
// Safety: we used `export`, which is a valid exporter to the C data interface
let new_array = unsafe { import(array_ptr, schema_ptr.as_ref())? };

// which is equal to the exported array
assert_eq!(array.as_ref(), new_array.as_ref());
Expand Down
4 changes: 2 additions & 2 deletions src/array/binary/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ unsafe impl<O: Offset> ToFfi for BinaryArray<O> {
}
}

unsafe impl<O: Offset, A: ffi::ArrowArrayRef> FromFfi<A> for BinaryArray<O> {
fn try_from_ffi(array: A) -> Result<Self> {
impl<O: Offset, A: ffi::ArrowArrayRef> FromFfi<A> for BinaryArray<O> {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
let data_type = array.field().data_type().clone();
let expected = if O::is_large() {
DataType::LargeBinary
Expand Down
4 changes: 2 additions & 2 deletions src/array/boolean/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ unsafe impl ToFfi for BooleanArray {
}
}

unsafe impl<A: ffi::ArrowArrayRef> FromFfi<A> for BooleanArray {
fn try_from_ffi(array: A) -> Result<Self> {
impl<A: ffi::ArrowArrayRef> FromFfi<A> for BooleanArray {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
let data_type = array.field().data_type().clone();
assert_eq!(data_type, DataType::Boolean);
let length = array.array().len();
Expand Down
4 changes: 2 additions & 2 deletions src/array/dictionary/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ unsafe impl<K: DictionaryKey> ToFfi for DictionaryArray<K> {
}
}

unsafe impl<K: DictionaryKey, A: ffi::ArrowArrayRef> FromFfi<A> for DictionaryArray<K> {
fn try_from_ffi(array: A) -> Result<Self> {
impl<K: DictionaryKey, A: ffi::ArrowArrayRef> FromFfi<A> for DictionaryArray<K> {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
// keys: similar to PrimitiveArray, but the datatype is the inner one
let length = array.array().len();
let offset = array.array().offset();
Expand Down
7 changes: 5 additions & 2 deletions src/array/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ pub unsafe trait ToFfi {

/// Trait describing how a struct imports into itself from the
/// [C data interface](https://arrow.apache.org/docs/format/CDataInterface.html) (FFI).
pub unsafe trait FromFfi<T: ffi::ArrowArrayRef>: Sized {
pub trait FromFfi<T: ffi::ArrowArrayRef>: Sized {
/// Convert itself from FFI.
fn try_from_ffi(array: T) -> Result<Self>;
/// # Safety
/// This function is intrinsically `unsafe` as it requires the FFI to be made according
/// to the [C data interface](https://arrow.apache.org/docs/format/CDataInterface.html)
unsafe fn try_from_ffi(array: T) -> Result<Self>;
}

macro_rules! ffi_dyn {
Expand Down
4 changes: 2 additions & 2 deletions src/array/list/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ unsafe impl<O: Offset> ToFfi for ListArray<O> {
}
}

unsafe impl<O: Offset, A: ffi::ArrowArrayRef> FromFfi<A> for ListArray<O> {
fn try_from_ffi(array: A) -> Result<Self> {
impl<O: Offset, A: ffi::ArrowArrayRef> FromFfi<A> for ListArray<O> {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
let data_type = array.field().data_type().clone();
let length = array.array().len();
let offset = array.array().offset();
Expand Down
4 changes: 2 additions & 2 deletions src/array/primitive/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ unsafe impl<T: NativeType> ToFfi for PrimitiveArray<T> {
}
}

unsafe impl<T: NativeType, A: ffi::ArrowArrayRef> FromFfi<A> for PrimitiveArray<T> {
fn try_from_ffi(array: A) -> Result<Self> {
impl<T: NativeType, A: ffi::ArrowArrayRef> FromFfi<A> for PrimitiveArray<T> {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
let data_type = array.field().data_type().clone();
let length = array.array().len();
let offset = array.array().offset();
Expand Down
4 changes: 2 additions & 2 deletions src/array/struct_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ unsafe impl ToFfi for StructArray {
}
}

unsafe impl<A: ffi::ArrowArrayRef> FromFfi<A> for StructArray {
fn try_from_ffi(array: A) -> Result<Self> {
impl<A: ffi::ArrowArrayRef> FromFfi<A> for StructArray {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
let field = array.field();
let fields = Self::get_fields(field.data_type()).to_vec();

Expand Down
4 changes: 2 additions & 2 deletions src/array/union/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ unsafe impl ToFfi for UnionArray {
}
}

unsafe impl<A: ffi::ArrowArrayRef> FromFfi<A> for UnionArray {
fn try_from_ffi(array: A) -> Result<Self> {
impl<A: ffi::ArrowArrayRef> FromFfi<A> for UnionArray {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
let field = array.field();
let data_type = field.data_type().clone();
let fields = Self::get_fields(field.data_type());
Expand Down
4 changes: 2 additions & 2 deletions src/array/utf8/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ unsafe impl<O: Offset> ToFfi for Utf8Array<O> {
}
}

unsafe impl<O: Offset, A: ffi::ArrowArrayRef> FromFfi<A> for Utf8Array<O> {
fn try_from_ffi(array: A) -> Result<Self> {
impl<O: Offset, A: ffi::ArrowArrayRef> FromFfi<A> for Utf8Array<O> {
unsafe fn try_from_ffi(array: A) -> Result<Self> {
let length = array.array().len();
let offset = array.array().offset();
let mut validity = unsafe { array.validity() }?;
Expand Down
19 changes: 1 addition & 18 deletions src/ffi/array.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

//! Contains functionality to load an ArrayData from the C Data Interface

use super::ffi::ArrowArrayRef;
Expand All @@ -27,7 +10,7 @@ use crate::{array::*, datatypes::PhysicalType};
/// If and only if:
/// * the data type is not supported
/// * the interface is not valid (e.g. a null pointer)
pub fn try_from<A: ArrowArrayRef>(array: A) -> Result<Box<dyn Array>> {
pub unsafe fn try_from<A: ArrowArrayRef>(array: A) -> Result<Box<dyn Array>> {
use PhysicalType::*;
Ok(match array.field().data_type().to_physical_type() {
Boolean => Box::new(BooleanArray::try_from_ffi(array)?),
Expand Down
15 changes: 12 additions & 3 deletions src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,20 @@ pub unsafe fn export_field_to_c(field: &Field, ptr: *mut Ffi_ArrowSchema) {
}

/// Imports a [`Field`] from the C data interface.
pub fn import_field_from_c(field: &Ffi_ArrowSchema) -> Result<Field> {
/// # Safety
/// This function is intrinsically `unsafe` and relies on a [`Ffi_ArrowSchema`]
/// valid according to the [C data interface](https://arrow.apache.org/docs/format/CDataInterface.html) (FFI).
pub unsafe fn import_field_from_c(field: &Ffi_ArrowSchema) -> Result<Field> {
to_field(field)
}

/// Imports a [`Field`] from the C data interface.
pub fn import_array_from_c(array: Box<Ffi_ArrowArray>, field: &Field) -> Result<Box<dyn Array>> {
/// Imports an [`Array`] from the C data interface.
/// # Safety
/// This function is intrinsically `unsafe` and relies on a [`Ffi_ArrowArray`]
/// valid according to the [C data interface](https://arrow.apache.org/docs/format/CDataInterface.html) (FFI).
pub unsafe fn import_array_from_c(
array: Box<Ffi_ArrowArray>,
field: &Field,
) -> Result<Box<dyn Array>> {
try_from(Arc::new(ArrowArray::new(array, field.clone())))
}
4 changes: 2 additions & 2 deletions src/ffi/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ impl Drop for Ffi_ArrowSchema {
}
}

pub fn to_field(schema: &Ffi_ArrowSchema) -> Result<Field> {
pub(crate) unsafe fn to_field(schema: &Ffi_ArrowSchema) -> Result<Field> {
let dictionary = schema.dictionary();
let data_type = if let Some(dictionary) = dictionary {
let indices_data_type = to_data_type(schema)?;
Expand All @@ -224,7 +224,7 @@ pub fn to_field(schema: &Ffi_ArrowSchema) -> Result<Field> {
Ok(field)
}

fn to_data_type(schema: &Ffi_ArrowSchema) -> Result<DataType> {
unsafe fn to_data_type(schema: &Ffi_ArrowSchema) -> Result<DataType> {
Ok(match schema.format() {
"n" => DataType::Null,
"b" => DataType::Boolean,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Doc provided by README

// So that we have more control over what is `unsafe` inside an `unsafe` block
#![allow(unused_unsafe)]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[macro_use]
Expand Down
6 changes: 3 additions & 3 deletions tests/it/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ fn test_round_trip(expected: impl Array + Clone + 'static) -> Result<()> {
let schema_ptr = unsafe { Box::from_raw(schema_ptr) };

// import references
let result_field = ffi::import_field_from_c(schema_ptr.as_ref())?;
let result_array = ffi::import_array_from_c(array_ptr, &result_field)?;
let result_field = unsafe { ffi::import_field_from_c(schema_ptr.as_ref())? };
let result_array = unsafe { ffi::import_array_from_c(array_ptr, &result_field)? };

assert_eq!(&result_array, &expected);
assert_eq!(result_field, field);
Expand All @@ -42,7 +42,7 @@ fn test_round_trip_schema(field: Field) -> Result<()> {

let schema_ptr = unsafe { Box::from_raw(schema_ptr) };

let result = ffi::import_field_from_c(schema_ptr.as_ref())?;
let result = unsafe { ffi::import_field_from_c(schema_ptr.as_ref())? };

assert_eq!(result, field);
Ok(())
Expand Down