Skip to main content

arrow_array/
ffi.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Contains declarations to bind to the [C Data Interface](https://arrow.apache.org/docs/format/CDataInterface.html).
19//!
20//! Generally, this module is divided in two main interfaces:
21//! One interface maps C ABI to native Rust types, i.e. convert c-pointers, c_char, to native rust.
22//! This is handled by [FFI_ArrowSchema] and [FFI_ArrowArray].
23//!
24//! The second interface maps native Rust types to the Rust-specific implementation of Arrow such as `format` to `Datatype`,
25//! `Buffer`, etc. This is handled by `from_ffi` and `to_ffi`.
26//!
27//!
28//! Export to FFI
29//!
30//! ```rust
31//! # use std::sync::Arc;
32//! # use arrow_array::{Int32Array, Array, make_array};
33//! # use arrow_data::ArrayData;
34//! # use arrow_array::ffi::{to_ffi, from_ffi};
35//! # use arrow_schema::ArrowError;
36//! # fn main() -> Result<(), ArrowError> {
37//! // create an array natively
38//!
39//! let array = Int32Array::from(vec![Some(1), None, Some(3)]);
40//! let data = array.into_data();
41//!
42//! // Export it
43//! let (out_array, out_schema) = to_ffi(&data)?;
44//!
45//! // import it
46//! let data = unsafe { from_ffi(out_array, &out_schema) }?;
47//! let array = Int32Array::from(data);
48//!
49//! // verify
50//! assert_eq!(array, Int32Array::from(vec![Some(1), None, Some(3)]));
51//! #
52//! # Ok(())
53//! # }
54//! ```
55//!
56//! Import from FFI
57//!
58//! ```
59//! # use std::ptr::addr_of_mut;
60//! # use arrow_array::ffi::{from_ffi, FFI_ArrowArray};
61//! # use arrow_array::{ArrayRef, make_array};
62//! # use arrow_schema::{ArrowError, ffi::FFI_ArrowSchema};
63//! #
64//! /// A foreign data container that can export to C Data interface
65//! struct ForeignArray {};
66//!
67//! impl ForeignArray {
68//!     /// Export from foreign array representation to C Data interface
69//!     /// e.g. <https://github.com/apache/arrow/blob/fc1f9ebbc4c3ae77d5cfc2f9322f4373d3d19b8a/python/pyarrow/array.pxi#L1552>
70//!     fn export_to_c(&self, array: *mut FFI_ArrowArray, schema: *mut FFI_ArrowSchema) {
71//!         // ...
72//!     }
73//! }
74//!
75//! /// Import an [`ArrayRef`] from a [`ForeignArray`]
76//! fn import_array(foreign: &ForeignArray) -> Result<ArrayRef, ArrowError> {
77//!     let mut schema = FFI_ArrowSchema::empty();
78//!     let mut array = FFI_ArrowArray::empty();
79//!     foreign.export_to_c(addr_of_mut!(array), addr_of_mut!(schema));
80//!     Ok(make_array(unsafe { from_ffi(array, &schema) }?))
81//! }
82//! ```
83
84/*
85# Design:
86
87Main assumptions:
88* A memory region is deallocated according it its own release mechanism.
89* Rust shares memory regions between arrays.
90* A memory region should be deallocated when no-one is using it.
91
92The design of this module is as follows:
93
94`ArrowArray` contains two `Arc`s, one per ABI-compatible `struct`, each containing data
95according to the C Data Interface. These Arcs are used for ref counting of the structs
96within Rust and lifetime management.
97
98Each ABI-compatible `struct` knowns how to `drop` itself, calling `release`.
99
100To import an array, unsafely create an `ArrowArray` from two pointers using [ArrowArray::try_from_raw].
101To export an array, create an `ArrowArray` using [ArrowArray::try_new].
102*/
103
104use std::{mem::size_of, ptr::NonNull, sync::Arc};
105
106use arrow_buffer::{Buffer, MutableBuffer, bit_util};
107pub use arrow_data::ffi::FFI_ArrowArray;
108use arrow_data::{ArrayData, layout};
109pub use arrow_schema::ffi::FFI_ArrowSchema;
110use arrow_schema::{ArrowError, DataType, UnionMode};
111
112type Result<T> = std::result::Result<T, ArrowError>;
113
114/// returns the number of bits that buffer `i` (in the C data interface) is expected to have.
115/// This is set by the Arrow specification
116fn bit_width(data_type: &DataType, i: usize) -> Result<usize> {
117    if let Some(primitive) = data_type.primitive_width() {
118        return match i {
119            0 => Err(ArrowError::CDataInterface(format!(
120                "The datatype \"{data_type}\" doesn't expect buffer at index 0. Please verify that the C data interface is correctly implemented."
121            ))),
122            1 => Ok(primitive * 8),
123            i => Err(ArrowError::CDataInterface(format!(
124                "The datatype \"{data_type}\" expects 2 buffers, but requested {i}. Please verify that the C data interface is correctly implemented."
125            ))),
126        };
127    }
128
129    Ok(match (data_type, i) {
130        (DataType::Boolean, 1) => 1,
131        (DataType::Boolean, _) => {
132            return Err(ArrowError::CDataInterface(format!(
133                "The datatype \"{data_type}\" expects 2 buffers, but requested {i}. Please verify that the C data interface is correctly implemented."
134            )));
135        }
136        (DataType::FixedSizeBinary(num_bytes), 1) => {
137            TryInto::<usize>::try_into(*num_bytes).map_err(|_| {
138                ArrowError::InvalidArgumentError(format!(
139                    "cannot determine bit_width for FixedSizeBinary({num_bytes})"
140                ))
141            })? * u8::BITS as usize
142        }
143        (DataType::FixedSizeList(f, num_elems), 1) => {
144            let child_bit_width = bit_width(f.data_type(), 1)?;
145            child_bit_width * (*num_elems as usize)
146        }
147        (DataType::FixedSizeBinary(_) | DataType::FixedSizeList(_, _), _) => {
148            return Err(ArrowError::CDataInterface(format!(
149                "The datatype \"{data_type}\" expects 2 buffers, but requested {i}. Please verify that the C data interface is correctly implemented."
150            )));
151        }
152        // Variable-size list and map have one i32 buffer.
153        // Variable-sized binaries: have two buffers.
154        // "small": first buffer is i32, second is in bytes
155        (DataType::Utf8 | DataType::Binary | DataType::List(_) | DataType::Map(_, _), 1) => {
156            i32::BITS as _
157        }
158        (DataType::Utf8 | DataType::Binary, 2) => u8::BITS as _,
159        // List views have two i32 buffers, offsets and sizes
160        (DataType::ListView(_), 1 | 2) => i32::BITS as _,
161        // Large list views have two i64 buffers, offsets and sizes
162        (DataType::LargeListView(_), 1 | 2) => i64::BITS as _,
163        (DataType::List(_) | DataType::Map(_, _), _) => {
164            return Err(ArrowError::CDataInterface(format!(
165                "The datatype \"{data_type}\" expects 2 buffers, but requested {i}. Please verify that the C data interface is correctly implemented."
166            )));
167        }
168        (DataType::Utf8 | DataType::Binary, _) => {
169            return Err(ArrowError::CDataInterface(format!(
170                "The datatype \"{data_type}\" expects 3 buffers, but requested {i}. Please verify that the C data interface is correctly implemented."
171            )));
172        }
173        // Variable-sized binaries: have two buffers.
174        // LargeUtf8: first buffer is i64, second is in bytes
175        (DataType::LargeUtf8 | DataType::LargeBinary | DataType::LargeList(_), 1) => i64::BITS as _,
176        (DataType::LargeUtf8 | DataType::LargeBinary | DataType::LargeList(_), 2) => u8::BITS as _,
177        (DataType::LargeUtf8 | DataType::LargeBinary | DataType::LargeList(_), _) => {
178            return Err(ArrowError::CDataInterface(format!(
179                "The datatype \"{data_type}\" expects 3 buffers, but requested {i}. Please verify that the C data interface is correctly implemented."
180            )));
181        }
182        // Variable-sized views: have 3 or more buffers.
183        // Buffer 1 are the u128 views
184        // Buffers 2...N-1 are u8 byte buffers
185        (DataType::Utf8View | DataType::BinaryView, 1) => u128::BITS as _,
186        (DataType::Utf8View | DataType::BinaryView, _) => u8::BITS as _,
187        // type ids. UnionArray doesn't have null bitmap so buffer index begins with 0.
188        (DataType::Union(_, _), 0) => i8::BITS as _,
189        // Only DenseUnion has 2nd buffer
190        (DataType::Union(_, UnionMode::Dense), 1) => i32::BITS as _,
191        (DataType::Union(_, UnionMode::Sparse), _) => {
192            return Err(ArrowError::CDataInterface(format!(
193                "The datatype \"{data_type}\" expects 1 buffer, but requested {i}. Please verify that the C data interface is correctly implemented."
194            )));
195        }
196        (DataType::Union(_, UnionMode::Dense), _) => {
197            return Err(ArrowError::CDataInterface(format!(
198                "The datatype \"{data_type}\" expects 2 buffer, but requested {i}. Please verify that the C data interface is correctly implemented."
199            )));
200        }
201        (_, 0) => {
202            // We don't call this `bit_width` to compute buffer length for null buffer. If any types that don't have null buffer like
203            // UnionArray, they should be handled above.
204            return Err(ArrowError::CDataInterface(format!(
205                "The datatype \"{data_type}\" doesn't expect buffer at index 0. Please verify that the C data interface is correctly implemented."
206            )));
207        }
208        _ => {
209            return Err(ArrowError::CDataInterface(format!(
210                "The datatype \"{data_type}\" is still not supported in Rust implementation"
211            )));
212        }
213    })
214}
215
216/// returns a new buffer corresponding to the index `i` of the FFI array. It may not exist (null pointer).
217/// `bits` is the number of bits that the native type of this buffer has.
218/// The size of the buffer will be `ceil(self.length * bits, 8)`.
219/// # Panics
220/// This function panics if `i` is larger or equal to `n_buffers`.
221/// # Safety
222/// This function assumes that `ceil(self.length * bits, 8)` is the size of the buffer
223unsafe fn create_buffer(
224    owner: Arc<FFI_ArrowArray>,
225    array: &FFI_ArrowArray,
226    index: usize,
227    len: usize,
228) -> Option<Buffer> {
229    if array.num_buffers() == 0 {
230        return None;
231    }
232    NonNull::new(array.buffer(index).cast_mut())
233        .map(|ptr| unsafe { Buffer::from_custom_allocation(ptr, len, owner) })
234}
235
236/// Export to the C Data Interface
237pub fn to_ffi(data: &ArrayData) -> Result<(FFI_ArrowArray, FFI_ArrowSchema)> {
238    let array = FFI_ArrowArray::new(data);
239    let schema = FFI_ArrowSchema::try_from(data.data_type())?;
240    Ok((array, schema))
241}
242
243/// Import [ArrayData] from the C Data Interface
244///
245/// # Safety
246///
247/// This struct assumes that the incoming data agrees with the C data interface.
248pub unsafe fn from_ffi(array: FFI_ArrowArray, schema: &FFI_ArrowSchema) -> Result<ArrayData> {
249    let dt = DataType::try_from(schema)?;
250    let array = Arc::new(array);
251    let tmp = ImportedArrowArray {
252        array: &array,
253        data_type: dt,
254        owner: &array,
255    };
256    let mut data = tmp.consume()?;
257    // arrow-rs has stricter alignment requirements than the C Data Interface spec;
258    // a no-op when buffers are already aligned. Unreachable under
259    // `cfg(feature = "force_validate")`; tracked in #10034.
260    // See https://github.com/apache/arrow/issues/43552 and
261    // https://github.com/apache/arrow-rs/issues/10028 for context.
262    data.align_buffers();
263    Ok(data)
264}
265
266/// Import [ArrayData] from the C Data Interface
267///
268/// # Safety
269///
270/// This struct assumes that the incoming data agrees with the C data interface.
271pub unsafe fn from_ffi_and_data_type(
272    array: FFI_ArrowArray,
273    data_type: DataType,
274) -> Result<ArrayData> {
275    let array = Arc::new(array);
276    let tmp = ImportedArrowArray {
277        array: &array,
278        data_type,
279        owner: &array,
280    };
281    let mut data = tmp.consume()?;
282    // arrow-rs has stricter alignment requirements than the C Data Interface spec;
283    // a no-op when buffers are already aligned. Unreachable under
284    // `cfg(feature = "force_validate")`; tracked in #10034.
285    // See https://github.com/apache/arrow/issues/43552 and
286    // https://github.com/apache/arrow-rs/issues/10028 for context.
287    data.align_buffers();
288    Ok(data)
289}
290
291#[derive(Debug)]
292struct ImportedArrowArray<'a> {
293    array: &'a FFI_ArrowArray,
294    data_type: DataType,
295    owner: &'a Arc<FFI_ArrowArray>,
296}
297
298impl ImportedArrowArray<'_> {
299    fn consume(self) -> Result<ArrayData> {
300        let len = self.array.len();
301        let offset = self.array.offset();
302        let null_count = match &self.data_type {
303            DataType::Null => Some(0),
304            _ => self.array.null_count_opt(),
305        };
306
307        let data_layout = layout(&self.data_type);
308        let buffers = self.buffers(data_layout.can_contain_null_mask, data_layout.variadic)?;
309
310        let null_bit_buffer = if data_layout.can_contain_null_mask {
311            self.null_bit_buffer()
312        } else {
313            None
314        };
315
316        let mut child_data = self.consume_children()?;
317
318        if let Some(d) = self.dictionary()? {
319            // For dictionary type there should only be a single child, so we don't need to worry if
320            // there are other children added above.
321            assert!(child_data.is_empty());
322            child_data.push(d.consume()?);
323        }
324
325        // Should FFI be checking validity?
326        Ok(unsafe {
327            ArrayData::new_unchecked(
328                self.data_type,
329                len,
330                null_count,
331                null_bit_buffer,
332                offset,
333                buffers,
334                child_data,
335            )
336        })
337    }
338
339    fn consume_children(&self) -> Result<Vec<ArrayData>> {
340        match &self.data_type {
341            DataType::List(field)
342            | DataType::FixedSizeList(field, _)
343            | DataType::LargeList(field)
344            | DataType::ListView(field)
345            | DataType::LargeListView(field)
346            | DataType::Map(field, _) => Ok([self.consume_child(0, field.data_type())?].to_vec()),
347            DataType::Struct(fields) => {
348                assert!(fields.len() == self.array.num_children());
349                fields
350                    .iter()
351                    .enumerate()
352                    .map(|(i, field)| self.consume_child(i, field.data_type()))
353                    .collect::<Result<Vec<_>>>()
354            }
355            DataType::Union(union_fields, _) => {
356                assert!(union_fields.len() == self.array.num_children());
357                union_fields
358                    .iter()
359                    .enumerate()
360                    .map(|(i, (_, field))| self.consume_child(i, field.data_type()))
361                    .collect::<Result<Vec<_>>>()
362            }
363            DataType::RunEndEncoded(run_ends_field, values_field) => Ok([
364                self.consume_child(0, run_ends_field.data_type())?,
365                self.consume_child(1, values_field.data_type())?,
366            ]
367            .to_vec()),
368            _ => Ok(Vec::new()),
369        }
370    }
371
372    fn consume_child(&self, index: usize, child_type: &DataType) -> Result<ArrayData> {
373        ImportedArrowArray {
374            array: self.array.child(index),
375            data_type: child_type.clone(),
376            owner: self.owner,
377        }
378        .consume()
379    }
380
381    /// returns all buffers, as organized by Rust (i.e. null buffer is skipped if it's present
382    /// in the spec of the type)
383    fn buffers(&self, can_contain_null_mask: bool, variadic: bool) -> Result<Vec<Buffer>> {
384        // + 1: skip null buffer
385        let buffer_begin = can_contain_null_mask as usize;
386        let buffer_end = self.array.num_buffers() - usize::from(variadic);
387
388        let variadic_buffer_lens = if variadic {
389            // Each views array has 1 (optional) null buffer, 1 views buffer, 1 lengths buffer.
390            // Rest are variadic.
391            let num_variadic_buffers =
392                self.array.num_buffers() - (2 + usize::from(can_contain_null_mask));
393            if num_variadic_buffers == 0 {
394                &[]
395            } else {
396                let lengths = self.array.buffer(self.array.num_buffers() - 1);
397                // SAFETY: is lengths is non-null, then it must be valid for up to num_variadic_buffers.
398                unsafe { std::slice::from_raw_parts(lengths.cast::<i64>(), num_variadic_buffers) }
399            }
400        } else {
401            &[]
402        };
403
404        (buffer_begin..buffer_end)
405            .map(|index| {
406                let len = self.buffer_len(index, variadic_buffer_lens, &self.data_type)?;
407                match unsafe { create_buffer(self.owner.clone(), self.array, index, len) } {
408                    Some(buf) => {
409                        // External libraries may use a dangling pointer for a buffer with length 0.
410                        // We respect the array length specified in the C Data Interface. Actually,
411                        // if the length is incorrect, we cannot create a correct buffer even if
412                        // the pointer is valid.
413                        if buf.is_empty() {
414                            Ok(MutableBuffer::new(0).into())
415                        } else {
416                            Ok(buf)
417                        }
418                    }
419                    None if len == 0 => {
420                        // Null data buffer, which Rust doesn't allow. So create
421                        // an empty buffer.
422                        Ok(MutableBuffer::new(0).into())
423                    }
424                    None => Err(ArrowError::CDataInterface(format!(
425                        "The external buffer at position {index} is null."
426                    ))),
427                }
428            })
429            .collect()
430    }
431
432    /// Returns the length, in bytes, of the buffer `i` (indexed according to the C data interface)
433    /// Rust implementation uses fixed-sized buffers, which require knowledge of their `len`.
434    /// for variable-sized buffers, such as the second buffer of a stringArray, we need
435    /// to fetch offset buffer's len to build the second buffer.
436    fn buffer_len(
437        &self,
438        i: usize,
439        variadic_buffer_lengths: &[i64],
440        dt: &DataType,
441    ) -> Result<usize> {
442        // Special handling for dictionary type as we only care about the key type in the case.
443        let data_type = match dt {
444            DataType::Dictionary(key_data_type, _) => key_data_type.as_ref(),
445            dt => dt,
446        };
447
448        // `ffi::ArrowArray` records array offset, we need to add it back to the
449        // buffer length to get the actual buffer length.
450        let length = self.array.len() + self.array.offset();
451
452        // Inner type is not important for buffer length.
453        Ok(match (&data_type, i) {
454            (
455                DataType::Utf8
456                | DataType::LargeUtf8
457                | DataType::Binary
458                | DataType::LargeBinary
459                | DataType::List(_)
460                | DataType::LargeList(_)
461                | DataType::Map(_, _),
462                1,
463            ) => {
464                // the len of the offset buffer (buffer 1) equals length + 1
465                let bits = bit_width(data_type, i)?;
466                debug_assert_eq!(bits % 8, 0);
467                (length + 1) * (bits / 8)
468            }
469            (DataType::ListView(_) | DataType::LargeListView(_), 1 | 2) => {
470                let bits = bit_width(data_type, i)?;
471                debug_assert_eq!(bits % 8, 0);
472                length * (bits / 8)
473            }
474            (DataType::Utf8 | DataType::Binary, 2) => {
475                if self.array.is_empty() {
476                    return Ok(0);
477                }
478
479                // the len of the data buffer (buffer 2) equals the last value of the offset buffer (buffer 1)
480                let len = self.buffer_len(1, variadic_buffer_lengths, dt)?;
481                // first buffer is the null buffer => add(1)
482                // we assume that pointer is aligned for `i32`, as Utf8 uses `i32` offsets.
483                #[expect(clippy::cast_ptr_alignment)]
484                let offset_buffer = self.array.buffer(1) as *const i32;
485                // get last offset
486                (unsafe { *offset_buffer.add(len / size_of::<i32>() - 1) }) as usize
487            }
488            (DataType::LargeUtf8 | DataType::LargeBinary, 2) => {
489                if self.array.is_empty() {
490                    return Ok(0);
491                }
492
493                // the len of the data buffer (buffer 2) equals the last value of the offset buffer (buffer 1)
494                let len = self.buffer_len(1, variadic_buffer_lengths, dt)?;
495                // first buffer is the null buffer => add(1)
496                // we assume that pointer is aligned for `i64`, as Large uses `i64` offsets.
497                #[expect(clippy::cast_ptr_alignment)]
498                let offset_buffer = self.array.buffer(1) as *const i64;
499                // get last offset
500                (unsafe { *offset_buffer.add(len / size_of::<i64>() - 1) }) as usize
501            }
502            // View types: these have variadic buffers.
503            // Buffer 1 is the views buffer, which stores 1 u128 per length of the array.
504            // Buffers 2..N-1 are the buffers holding the byte data. Their lengths are variable.
505            // Buffer N is of length (N - 2) and stores i64 containing the lengths of buffers 2..N-1
506            (DataType::Utf8View | DataType::BinaryView, 1) => std::mem::size_of::<u128>() * length,
507            (DataType::Utf8View | DataType::BinaryView, i) => {
508                variadic_buffer_lengths[i - 2] as usize
509            }
510            // buffer len of primitive types
511            _ => {
512                let bits = bit_width(data_type, i)?;
513                bit_util::ceil(length * bits, 8)
514            }
515        })
516    }
517
518    /// returns the null bit buffer.
519    /// Rust implementation uses a buffer that is not part of the array of buffers.
520    /// The C Data interface's null buffer is part of the array of buffers.
521    fn null_bit_buffer(&self) -> Option<Buffer> {
522        // similar to `self.buffer_len(0)`, but without `Result`.
523        // `ffi::ArrowArray` records array offset, we need to add it back to the
524        // buffer length to get the actual buffer length.
525        let length = self.array.len() + self.array.offset();
526        let buffer_len = bit_util::ceil(length, 8);
527
528        unsafe { create_buffer(self.owner.clone(), self.array, 0, buffer_len) }
529    }
530
531    fn dictionary(&self) -> Result<Option<ImportedArrowArray<'_>>> {
532        match (self.array.dictionary(), &self.data_type) {
533            (Some(array), DataType::Dictionary(_, value_type)) => Ok(Some(ImportedArrowArray {
534                array,
535                data_type: value_type.as_ref().clone(),
536                owner: self.owner,
537            })),
538            (Some(_), _) => Err(ArrowError::CDataInterface(
539                "Got dictionary in FFI_ArrowArray for non-dictionary data type".to_string(),
540            )),
541            (None, DataType::Dictionary(_, _)) => Err(ArrowError::CDataInterface(
542                "Missing dictionary in FFI_ArrowArray for dictionary data type".to_string(),
543            )),
544            (_, _) => Ok(None),
545        }
546    }
547}
548
549#[cfg(test)]
550mod tests_to_then_from_ffi {
551    use std::collections::HashMap;
552    use std::mem::ManuallyDrop;
553
554    use arrow_buffer::{ArrowNativeType, NullBuffer};
555    use arrow_schema::Field;
556
557    use crate::builder::UnionBuilder;
558    use crate::cast::AsArray;
559    use crate::types::{Float64Type, Int8Type, Int32Type};
560    use crate::*;
561
562    use super::*;
563
564    #[test]
565    fn test_round_trip() {
566        // create an array natively
567        let array = Int32Array::from(vec![1, 2, 3]);
568
569        // export it
570        let (array, schema) = to_ffi(&array.into_data()).unwrap();
571
572        // (simulate consumer) import it
573        let array = Int32Array::from(unsafe { from_ffi(array, &schema) }.unwrap());
574
575        // verify
576        assert_eq!(array, Int32Array::from(vec![1, 2, 3]));
577    }
578
579    #[test]
580    fn test_import() {
581        // Model receiving const pointers from an external system
582
583        // Create an array natively
584        let data = Int32Array::from(vec![1, 2, 3]).into_data();
585        let schema = FFI_ArrowSchema::try_from(data.data_type()).unwrap();
586        let array = FFI_ArrowArray::new(&data);
587
588        // Use ManuallyDrop to avoid Box:Drop recursing
589        let schema = Box::new(ManuallyDrop::new(schema));
590        let array = Box::new(ManuallyDrop::new(array));
591
592        let schema_ptr = &**schema as *const _;
593        let array_ptr = &**array as *const _;
594
595        // We can read them back to memory
596        // SAFETY:
597        // Pointers are aligned and valid
598        let data =
599            unsafe { from_ffi(std::ptr::read(array_ptr), &std::ptr::read(schema_ptr)).unwrap() };
600
601        let array = Int32Array::from(data);
602        assert_eq!(array, Int32Array::from(vec![1, 2, 3]));
603    }
604
605    #[test]
606    fn test_round_trip_with_offset() -> Result<()> {
607        // create an array natively
608        let array = Int32Array::from(vec![Some(1), Some(2), None, Some(3), None]);
609
610        let array = array.slice(1, 2);
611
612        // export it
613        let (array, schema) = to_ffi(&array.to_data())?;
614
615        // (simulate consumer) import it
616        let data = unsafe { from_ffi(array, &schema) }?;
617        let array = make_array(data);
618        let array = array.as_any().downcast_ref::<Int32Array>().unwrap();
619
620        assert_eq!(array, &Int32Array::from(vec![Some(2), None]));
621
622        // (drop/release)
623        Ok(())
624    }
625
626    #[test]
627    #[cfg(not(feature = "force_validate"))]
628    fn test_decimal_round_trip() -> Result<()> {
629        // create an array natively
630        let original_array = [Some(12345_i128), Some(-12345_i128), None]
631            .into_iter()
632            .collect::<Decimal128Array>()
633            .with_precision_and_scale(6, 2)
634            .unwrap();
635
636        // export it
637        let (array, schema) = to_ffi(&original_array.to_data())?;
638
639        // (simulate consumer) import it
640        let data = unsafe { from_ffi(array, &schema) }?;
641        let array = make_array(data);
642
643        // perform some operation
644        let array = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
645
646        // verify
647        assert_eq!(array, &original_array);
648
649        // (drop/release)
650        Ok(())
651    }
652    // case with nulls is tested in the docs, through the example on this module.
653
654    #[test]
655    #[cfg(not(feature = "force_validate"))]
656    fn test_decimal128_under_aligned_round_trip() -> Result<()> {
657        // Construct an 8-aligned-but-not-16-aligned i128 data buffer to model
658        // an FFI producer that only guarantees the C Data Interface's
659        // recommended 8-byte alignment (e.g. arrow-java).
660        let aligned = Buffer::from_vec(vec![0_i128, 1_i128, 2_i128]);
661        let under_aligned = aligned.slice(8);
662        assert_eq!(under_aligned.as_ptr().align_offset(8), 0);
663        assert_ne!(under_aligned.as_ptr().align_offset(16), 0);
664
665        // SAFETY: buffer is large enough for 2 i128 elements; misaligned
666        // input is the condition under test.
667        let data = unsafe {
668            ArrayData::builder(DataType::Decimal128(10, 2))
669                .len(2)
670                .add_buffer(under_aligned)
671                .build_unchecked()
672        };
673
674        let schema = FFI_ArrowSchema::try_from(data.data_type()).unwrap();
675        let array = FFI_ArrowArray::new(&data);
676
677        let imported = unsafe { from_ffi(array, &schema) }?;
678        let array = Decimal128Array::from(imported);
679
680        // The little-endian byte layout of [0i128, 1, 2] sliced 8 bytes in
681        // yields elements `1 << 64` and `2 << 64`.
682        assert_eq!(array.len(), 2);
683        assert_eq!(array.value(0), 1_i128 << 64);
684        assert_eq!(array.value(1), 2_i128 << 64);
685        Ok(())
686    }
687
688    #[test]
689    fn test_null_count_handling() {
690        let int32_data = ArrayData::builder(DataType::Int32)
691            .len(10)
692            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
693            .null_bit_buffer(Some(Buffer::from([0b01011111, 0b00000001])))
694            .build()
695            .unwrap();
696        let mut ffi_array = FFI_ArrowArray::new(&int32_data);
697        assert_eq!(3, ffi_array.null_count());
698        assert_eq!(Some(3), ffi_array.null_count_opt());
699        // Simulating uninitialized state
700        unsafe {
701            ffi_array.set_null_count(-1);
702        }
703        assert_eq!(None, ffi_array.null_count_opt());
704        let int32_data = unsafe { from_ffi_and_data_type(ffi_array, DataType::Int32) }.unwrap();
705        assert_eq!(3, int32_data.null_count());
706
707        let null_data = &ArrayData::new_null(&DataType::Null, 10);
708        let mut ffi_array = FFI_ArrowArray::new(null_data);
709        assert_eq!(10, ffi_array.null_count());
710        assert_eq!(Some(10), ffi_array.null_count_opt());
711        // Simulating uninitialized state
712        unsafe {
713            ffi_array.set_null_count(-1);
714        }
715        assert_eq!(None, ffi_array.null_count_opt());
716        let null_data = unsafe { from_ffi_and_data_type(ffi_array, DataType::Null) }.unwrap();
717        assert_eq!(0, null_data.null_count());
718    }
719
720    fn test_generic_string<Offset: OffsetSizeTrait>() -> Result<()> {
721        // create an array natively
722        let array = GenericStringArray::<Offset>::from(vec![Some("a"), None, Some("aaa")]);
723
724        // export it
725        let (array, schema) = to_ffi(&array.to_data())?;
726
727        // (simulate consumer) import it
728        let data = unsafe { from_ffi(array, &schema) }?;
729        let array = make_array(data);
730
731        // perform some operation
732        let array = array
733            .as_any()
734            .downcast_ref::<GenericStringArray<Offset>>()
735            .unwrap();
736
737        // verify
738        let expected = GenericStringArray::<Offset>::from(vec![Some("a"), None, Some("aaa")]);
739        assert_eq!(array, &expected);
740
741        // (drop/release)
742        Ok(())
743    }
744
745    #[test]
746    fn test_string() -> Result<()> {
747        test_generic_string::<i32>()
748    }
749
750    #[test]
751    fn test_large_string() -> Result<()> {
752        test_generic_string::<i64>()
753    }
754
755    fn test_generic_list<Offset: OffsetSizeTrait>() -> Result<()> {
756        // Construct a value array
757        let value_data = ArrayData::builder(DataType::Int32)
758            .len(8)
759            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7]))
760            .build()
761            .unwrap();
762
763        // Construct a buffer for value offsets, for the nested array:
764        //  [[0, 1, 2], [3, 4, 5], [6, 7]]
765        let value_offsets = [0_usize, 3, 6, 8]
766            .iter()
767            .map(|i| Offset::from_usize(*i).unwrap())
768            .collect::<Buffer>();
769
770        // Construct a list array from the above two
771        let list_data_type = GenericListArray::<Offset>::DATA_TYPE_CONSTRUCTOR(Arc::new(
772            Field::new_list_field(DataType::Int32, false),
773        ));
774
775        let list_data = ArrayData::builder(list_data_type)
776            .len(3)
777            .add_buffer(value_offsets)
778            .add_child_data(value_data)
779            .build()
780            .unwrap();
781
782        // create an array natively
783        let array = GenericListArray::<Offset>::from(list_data.clone());
784
785        // export it
786        let (array, schema) = to_ffi(&array.to_data())?;
787
788        // (simulate consumer) import it
789        let data = unsafe { from_ffi(array, &schema) }?;
790        let array = make_array(data);
791
792        // downcast
793        let array = array
794            .as_any()
795            .downcast_ref::<GenericListArray<Offset>>()
796            .unwrap();
797
798        // verify
799        let expected = GenericListArray::<Offset>::from(list_data);
800        assert_eq!(&array.value(0), &expected.value(0));
801        assert_eq!(&array.value(1), &expected.value(1));
802        assert_eq!(&array.value(2), &expected.value(2));
803
804        // (drop/release)
805        Ok(())
806    }
807
808    #[test]
809    fn test_list() -> Result<()> {
810        test_generic_list::<i32>()
811    }
812
813    #[test]
814    fn test_large_list() -> Result<()> {
815        test_generic_list::<i64>()
816    }
817
818    fn test_generic_list_view<Offset: OffsetSizeTrait + ArrowNativeType>() -> Result<()> {
819        // Construct a value array
820        let value_data = ArrayData::builder(DataType::Int16)
821            .len(8)
822            .add_buffer(Buffer::from_slice_ref([0_i16, 1, 2, 3, 4, 5, 6, 7]))
823            .build()
824            .unwrap();
825
826        // Construct a buffer for value offsets, for the nested array:
827        //  [[0, 1, 2], [3, 4, 5], [6, 7]]
828        let value_offsets = [0_usize, 3, 6]
829            .iter()
830            .map(|i| Offset::from_usize(*i).unwrap())
831            .collect::<Buffer>();
832
833        let sizes_buffer = [3_usize, 3, 2]
834            .iter()
835            .map(|i| Offset::from_usize(*i).unwrap())
836            .collect::<Buffer>();
837
838        // Construct a list array from the above two
839        let list_view_dt = GenericListViewArray::<Offset>::DATA_TYPE_CONSTRUCTOR(Arc::new(
840            Field::new_list_field(DataType::Int16, false),
841        ));
842
843        let list_data = ArrayData::builder(list_view_dt)
844            .len(3)
845            .add_buffer(value_offsets)
846            .add_buffer(sizes_buffer)
847            .add_child_data(value_data)
848            .build()
849            .unwrap();
850
851        let original = GenericListViewArray::<Offset>::from(list_data.clone());
852
853        // export it
854        let (array, schema) = to_ffi(&original.to_data())?;
855
856        // (simulate consumer) import it
857        let data = unsafe { from_ffi(array, &schema) }?;
858        let array = make_array(data);
859
860        // downcast
861        let array = array
862            .as_any()
863            .downcast_ref::<GenericListViewArray<Offset>>()
864            .unwrap();
865
866        assert_eq!(&array.value(0), &original.value(0));
867        assert_eq!(&array.value(1), &original.value(1));
868        assert_eq!(&array.value(2), &original.value(2));
869
870        Ok(())
871    }
872
873    #[test]
874    fn test_list_view() -> Result<()> {
875        test_generic_list_view::<i32>()
876    }
877
878    #[test]
879    fn test_large_list_view() -> Result<()> {
880        test_generic_list_view::<i64>()
881    }
882
883    fn test_generic_binary<Offset: OffsetSizeTrait>() -> Result<()> {
884        // create an array natively
885        let array: Vec<Option<&[u8]>> = vec![Some(b"a"), None, Some(b"aaa")];
886        let array = GenericBinaryArray::<Offset>::from(array);
887
888        // export it
889        let (array, schema) = to_ffi(&array.to_data())?;
890
891        // (simulate consumer) import it
892        let data = unsafe { from_ffi(array, &schema) }?;
893        let array = make_array(data);
894        let array = array
895            .as_any()
896            .downcast_ref::<GenericBinaryArray<Offset>>()
897            .unwrap();
898
899        // verify
900        let expected: Vec<Option<&[u8]>> = vec![Some(b"a"), None, Some(b"aaa")];
901        let expected = GenericBinaryArray::<Offset>::from(expected);
902        assert_eq!(array, &expected);
903
904        // (drop/release)
905        Ok(())
906    }
907
908    #[test]
909    fn test_binary() -> Result<()> {
910        test_generic_binary::<i32>()
911    }
912
913    #[test]
914    fn test_large_binary() -> Result<()> {
915        test_generic_binary::<i64>()
916    }
917
918    #[test]
919    fn test_bool() -> Result<()> {
920        // create an array natively
921        let array = BooleanArray::from(vec![None, Some(true), Some(false)]);
922
923        // export it
924        let (array, schema) = to_ffi(&array.to_data())?;
925
926        // (simulate consumer) import it
927        let data = unsafe { from_ffi(array, &schema) }?;
928        let array = make_array(data);
929        let array = array.as_any().downcast_ref::<BooleanArray>().unwrap();
930
931        // verify
932        assert_eq!(
933            array,
934            &BooleanArray::from(vec![None, Some(true), Some(false)])
935        );
936
937        // (drop/release)
938        Ok(())
939    }
940
941    #[test]
942    fn test_time32() -> Result<()> {
943        // create an array natively
944        let array = Time32MillisecondArray::from(vec![None, Some(1), Some(2)]);
945
946        // export it
947        let (array, schema) = to_ffi(&array.to_data())?;
948
949        // (simulate consumer) import it
950        let data = unsafe { from_ffi(array, &schema) }?;
951        let array = make_array(data);
952        let array = array
953            .as_any()
954            .downcast_ref::<Time32MillisecondArray>()
955            .unwrap();
956
957        // verify
958        assert_eq!(
959            array,
960            &Time32MillisecondArray::from(vec![None, Some(1), Some(2)])
961        );
962
963        // (drop/release)
964        Ok(())
965    }
966
967    #[test]
968    fn test_timestamp() -> Result<()> {
969        // create an array natively
970        let array = TimestampMillisecondArray::from(vec![None, Some(1), Some(2)]);
971
972        // export it
973        let (array, schema) = to_ffi(&array.to_data())?;
974
975        // (simulate consumer) import it
976        let data = unsafe { from_ffi(array, &schema) }?;
977        let array = make_array(data);
978        let array = array
979            .as_any()
980            .downcast_ref::<TimestampMillisecondArray>()
981            .unwrap();
982
983        // verify
984        assert_eq!(
985            array,
986            &TimestampMillisecondArray::from(vec![None, Some(1), Some(2)])
987        );
988
989        // (drop/release)
990        Ok(())
991    }
992
993    #[test]
994    fn test_fixed_size_binary_array() -> Result<()> {
995        let values = vec![
996            None,
997            Some(vec![10, 10, 10]),
998            None,
999            Some(vec![20, 20, 20]),
1000            Some(vec![30, 30, 30]),
1001            None,
1002        ];
1003        let array = FixedSizeBinaryArray::try_from_sparse_iter_with_size(values.into_iter(), 3)?;
1004
1005        // export it
1006        let (array, schema) = to_ffi(&array.to_data())?;
1007
1008        // (simulate consumer) import it
1009        let data = unsafe { from_ffi(array, &schema) }?;
1010        let array = make_array(data);
1011        let array = array
1012            .as_any()
1013            .downcast_ref::<FixedSizeBinaryArray>()
1014            .unwrap();
1015
1016        // verify
1017        assert_eq!(
1018            array,
1019            &FixedSizeBinaryArray::try_from_sparse_iter_with_size(
1020                vec![
1021                    None,
1022                    Some(vec![10, 10, 10]),
1023                    None,
1024                    Some(vec![20, 20, 20]),
1025                    Some(vec![30, 30, 30]),
1026                    None,
1027                ]
1028                .into_iter(),
1029                3
1030            )?
1031        );
1032
1033        // (drop/release)
1034        Ok(())
1035    }
1036
1037    #[test]
1038    fn test_fixed_size_list_array() -> Result<()> {
1039        // 0000 0100
1040        let mut validity_bits: [u8; 1] = [0; 1];
1041        bit_util::set_bit(&mut validity_bits, 2);
1042
1043        let v: Vec<i32> = (0..9).collect();
1044        let value_data = ArrayData::builder(DataType::Int32)
1045            .len(9)
1046            .add_buffer(Buffer::from_slice_ref(&v))
1047            .build()?;
1048
1049        let list_data_type =
1050            DataType::FixedSizeList(Arc::new(Field::new("f", DataType::Int32, false)), 3);
1051        let list_data = ArrayData::builder(list_data_type.clone())
1052            .len(3)
1053            .null_bit_buffer(Some(Buffer::from(validity_bits)))
1054            .add_child_data(value_data)
1055            .build()?;
1056
1057        // export it
1058        let (array, schema) = to_ffi(&list_data)?;
1059
1060        // (simulate consumer) import it
1061        let data = unsafe { from_ffi(array, &schema) }?;
1062        let array = make_array(data);
1063        let array = array.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
1064
1065        // 0010 0100
1066        let mut expected_validity_bits: [u8; 1] = [0; 1];
1067        bit_util::set_bit(&mut expected_validity_bits, 2);
1068        bit_util::set_bit(&mut expected_validity_bits, 5);
1069
1070        let mut w = vec![];
1071        w.extend_from_slice(&v);
1072
1073        let expected_value_data = ArrayData::builder(DataType::Int32)
1074            .len(9)
1075            .add_buffer(Buffer::from_slice_ref(&w))
1076            .build()?;
1077
1078        let expected_list_data = ArrayData::builder(list_data_type)
1079            .len(3)
1080            .null_bit_buffer(Some(Buffer::from(expected_validity_bits)))
1081            .add_child_data(expected_value_data)
1082            .build()?;
1083        let expected_array = FixedSizeListArray::from(expected_list_data);
1084
1085        // verify
1086        assert_eq!(array, &expected_array);
1087
1088        // (drop/release)
1089        Ok(())
1090    }
1091
1092    #[test]
1093    fn test_dictionary() -> Result<()> {
1094        // create an array natively
1095        let values = vec!["a", "aaa", "aaa"];
1096        let dict_array: DictionaryArray<Int8Type> = values.into_iter().collect();
1097
1098        // export it
1099        let (array, schema) = to_ffi(&dict_array.to_data())?;
1100
1101        // (simulate consumer) import it
1102        let data = unsafe { from_ffi(array, &schema) }?;
1103        let array = make_array(data);
1104        let actual = array
1105            .as_any()
1106            .downcast_ref::<DictionaryArray<Int8Type>>()
1107            .unwrap();
1108
1109        // verify
1110        let new_values = vec!["a", "aaa", "aaa"];
1111        let expected: DictionaryArray<Int8Type> = new_values.into_iter().collect();
1112        assert_eq!(actual, &expected);
1113
1114        // (drop/release)
1115        Ok(())
1116    }
1117
1118    #[test]
1119    fn test_duration() -> Result<()> {
1120        // create an array natively
1121        let array = DurationSecondArray::from(vec![None, Some(1), Some(2)]);
1122
1123        // export it
1124        let (array, schema) = to_ffi(&array.to_data())?;
1125
1126        // (simulate consumer) import it
1127        let data = unsafe { from_ffi(array, &schema) }?;
1128        let array = make_array(data);
1129        let array = array
1130            .as_any()
1131            .downcast_ref::<DurationSecondArray>()
1132            .unwrap();
1133
1134        // verify
1135        assert_eq!(
1136            array,
1137            &DurationSecondArray::from(vec![None, Some(1), Some(2)])
1138        );
1139
1140        // (drop/release)
1141        Ok(())
1142    }
1143
1144    #[test]
1145    fn test_map_array() -> Result<()> {
1146        let keys = vec!["a", "b", "c", "d", "e", "f", "g", "h"];
1147        let values_data = UInt32Array::from(vec![0u32, 10, 20, 30, 40, 50, 60, 70]);
1148
1149        // Construct a buffer for value offsets, for the nested array:
1150        //  [[a, b, c], [d, e, f], [g, h]]
1151        let entry_offsets = [0, 3, 6, 8];
1152
1153        let map_array =
1154            MapArray::new_from_strings(keys.clone().into_iter(), &values_data, &entry_offsets)
1155                .unwrap();
1156
1157        // export it
1158        let (array, schema) = to_ffi(&map_array.to_data())?;
1159
1160        // (simulate consumer) import it
1161        let data = unsafe { from_ffi(array, &schema) }?;
1162        let array = make_array(data);
1163
1164        // perform some operation
1165        let array = array.as_any().downcast_ref::<MapArray>().unwrap();
1166        assert_eq!(array, &map_array);
1167
1168        Ok(())
1169    }
1170
1171    #[test]
1172    fn test_struct_array() -> Result<()> {
1173        let metadata: HashMap<String, String> =
1174            [("Hello".to_string(), "World! 😊".to_string())].into();
1175        let struct_array = StructArray::from(vec![(
1176            Arc::new(Field::new("a", DataType::Int32, false).with_metadata(metadata)),
1177            Arc::new(Int32Array::from(vec![2, 4, 6])) as Arc<dyn Array>,
1178        )]);
1179
1180        // export it
1181        let (array, schema) = to_ffi(&struct_array.to_data())?;
1182
1183        // (simulate consumer) import it
1184        let data = unsafe { from_ffi(array, &schema) }?;
1185        let array = make_array(data);
1186
1187        // perform some operation
1188        let array = array.as_any().downcast_ref::<StructArray>().unwrap();
1189        assert_eq!(array.data_type(), struct_array.data_type());
1190        assert_eq!(array, &struct_array);
1191
1192        Ok(())
1193    }
1194
1195    #[test]
1196    fn test_union_sparse_array() -> Result<()> {
1197        let mut builder = UnionBuilder::new_sparse();
1198        builder.append::<Int32Type>("a", 1).unwrap();
1199        builder.append_null::<Int32Type>("a").unwrap();
1200        builder.append::<Float64Type>("c", 3.0).unwrap();
1201        builder.append::<Int32Type>("a", 4).unwrap();
1202        let union = builder.build().unwrap();
1203
1204        // export it
1205        let (array, schema) = to_ffi(&union.to_data())?;
1206
1207        // (simulate consumer) import it
1208        let data = unsafe { from_ffi(array, &schema) }?;
1209        let array = make_array(data);
1210
1211        let array = array.as_any().downcast_ref::<UnionArray>().unwrap();
1212
1213        let expected_type_ids = vec![0_i8, 0, 1, 0];
1214
1215        // Check type ids
1216        assert_eq!(*array.type_ids(), expected_type_ids);
1217        for (i, id) in expected_type_ids.iter().enumerate() {
1218            assert_eq!(id, &array.type_id(i));
1219        }
1220
1221        // Check offsets, sparse union should only have a single buffer, i.e. no offsets
1222        assert!(array.offsets().is_none());
1223
1224        for i in 0..array.len() {
1225            let slot = array.value(i);
1226            match i {
1227                0 => {
1228                    let slot = slot.as_primitive::<Int32Type>();
1229                    assert!(!slot.is_null(0));
1230                    assert_eq!(slot.len(), 1);
1231                    let value = slot.value(0);
1232                    assert_eq!(1_i32, value);
1233                }
1234                1 => assert!(slot.is_null(0)),
1235                2 => {
1236                    let slot = slot.as_primitive::<Float64Type>();
1237                    assert!(!slot.is_null(0));
1238                    assert_eq!(slot.len(), 1);
1239                    let value = slot.value(0);
1240                    assert_eq!(value, 3_f64);
1241                }
1242                3 => {
1243                    let slot = slot.as_primitive::<Int32Type>();
1244                    assert!(!slot.is_null(0));
1245                    assert_eq!(slot.len(), 1);
1246                    let value = slot.value(0);
1247                    assert_eq!(4_i32, value);
1248                }
1249                _ => unreachable!(),
1250            }
1251        }
1252
1253        Ok(())
1254    }
1255
1256    #[test]
1257    fn test_union_dense_array() -> Result<()> {
1258        let mut builder = UnionBuilder::new_dense();
1259        builder.append::<Int32Type>("a", 1).unwrap();
1260        builder.append_null::<Int32Type>("a").unwrap();
1261        builder.append::<Float64Type>("c", 3.0).unwrap();
1262        builder.append::<Int32Type>("a", 4).unwrap();
1263        let union = builder.build().unwrap();
1264
1265        // export it
1266        let (array, schema) = to_ffi(&union.to_data())?;
1267
1268        // (simulate consumer) import it
1269        let data = unsafe { from_ffi(array, &schema) }?;
1270        let array = UnionArray::from(data);
1271
1272        let expected_type_ids = vec![0_i8, 0, 1, 0];
1273
1274        // Check type ids
1275        assert_eq!(*array.type_ids(), expected_type_ids);
1276        for (i, id) in expected_type_ids.iter().enumerate() {
1277            assert_eq!(id, &array.type_id(i));
1278        }
1279
1280        assert!(array.offsets().is_some());
1281
1282        for i in 0..array.len() {
1283            let slot = array.value(i);
1284            match i {
1285                0 => {
1286                    let slot = slot.as_primitive::<Int32Type>();
1287                    assert!(!slot.is_null(0));
1288                    assert_eq!(slot.len(), 1);
1289                    let value = slot.value(0);
1290                    assert_eq!(1_i32, value);
1291                }
1292                1 => assert!(slot.is_null(0)),
1293                2 => {
1294                    let slot = slot.as_primitive::<Float64Type>();
1295                    assert!(!slot.is_null(0));
1296                    assert_eq!(slot.len(), 1);
1297                    let value = slot.value(0);
1298                    assert_eq!(value, 3_f64);
1299                }
1300                3 => {
1301                    let slot = slot.as_primitive::<Int32Type>();
1302                    assert!(!slot.is_null(0));
1303                    assert_eq!(slot.len(), 1);
1304                    let value = slot.value(0);
1305                    assert_eq!(4_i32, value);
1306                }
1307                _ => unreachable!(),
1308            }
1309        }
1310
1311        Ok(())
1312    }
1313
1314    #[test]
1315    fn test_run_array() -> Result<()> {
1316        let value_data =
1317            PrimitiveArray::<Int8Type>::from_iter_values([10_i8, 11, 12, 13, 14, 15, 16, 17]);
1318
1319        // Construct a run_ends array:
1320        let run_ends_values = [4_i32, 6, 7, 9, 13, 18, 20, 22];
1321        let run_ends_data =
1322            PrimitiveArray::<Int32Type>::from_iter_values(run_ends_values.iter().copied());
1323
1324        // Construct a run ends encoded array from the above two
1325        let ree_array = RunArray::<Int32Type>::try_new(&run_ends_data, &value_data).unwrap();
1326
1327        // export it
1328        let (array, schema) = to_ffi(&ree_array.to_data())?;
1329
1330        // (simulate consumer) import it
1331        let data = unsafe { from_ffi(array, &schema) }?;
1332        let array = make_array(data);
1333
1334        // perform some operation
1335        let array = array
1336            .as_any()
1337            .downcast_ref::<RunArray<Int32Type>>()
1338            .unwrap();
1339        assert_eq!(array.data_type(), ree_array.data_type());
1340        assert_eq!(array.run_ends().values(), ree_array.run_ends().values());
1341        assert_eq!(array.values(), ree_array.values());
1342
1343        Ok(())
1344    }
1345
1346    #[test]
1347    fn test_nullable_run_array() -> Result<()> {
1348        let nulls = NullBuffer::from(vec![true, false, true, true, false]);
1349        let value_data =
1350            PrimitiveArray::<Int8Type>::new(vec![1_i8, 2, 3, 4, 5].into(), Some(nulls));
1351
1352        // Construct a run_ends array:
1353        let run_ends_values = [5_i32, 6, 7, 8, 10];
1354        let run_ends_data =
1355            PrimitiveArray::<Int32Type>::from_iter_values(run_ends_values.iter().copied());
1356
1357        // Construct a run ends encoded array from the above two
1358        let ree_array = RunArray::<Int32Type>::try_new(&run_ends_data, &value_data).unwrap();
1359
1360        // export it
1361        let (array, schema) = to_ffi(&ree_array.to_data())?;
1362
1363        // (simulate consumer) import it
1364        let data = unsafe { from_ffi(array, &schema) }?;
1365        let array = make_array(data);
1366
1367        // perform some operation
1368        let array = array
1369            .as_any()
1370            .downcast_ref::<RunArray<Int32Type>>()
1371            .unwrap();
1372        assert_eq!(array.data_type(), ree_array.data_type());
1373        assert_eq!(array.run_ends().values(), ree_array.run_ends().values());
1374        assert_eq!(array.values(), ree_array.values());
1375
1376        Ok(())
1377    }
1378}
1379
1380#[cfg(test)]
1381mod tests_from_ffi {
1382    #[cfg(not(feature = "force_validate"))]
1383    use std::ptr::NonNull;
1384    use std::sync::Arc;
1385
1386    use arrow_buffer::NullBuffer;
1387    #[cfg(not(feature = "force_validate"))]
1388    use arrow_buffer::{ScalarBuffer, bit_util, buffer::Buffer};
1389    #[cfg(feature = "force_validate")]
1390    use arrow_buffer::{bit_util, buffer::Buffer};
1391
1392    use arrow_data::ArrayData;
1393    use arrow_data::transform::MutableArrayData;
1394    use arrow_schema::{DataType, Field};
1395
1396    use super::Result;
1397
1398    use crate::builder::GenericByteViewBuilder;
1399    use crate::types::{BinaryViewType, ByteViewType, Int32Type, StringViewType};
1400    use crate::{
1401        ArrayRef, GenericByteViewArray, ListArray,
1402        array::{
1403            Array, BooleanArray, DictionaryArray, FixedSizeBinaryArray, FixedSizeListArray,
1404            Int32Array, Int64Array, StringArray, StructArray, UInt32Array, UInt64Array,
1405        },
1406        ffi::{FFI_ArrowArray, FFI_ArrowSchema, from_ffi},
1407        make_array,
1408    };
1409
1410    fn test_round_trip(expected: &ArrayData) -> Result<()> {
1411        // here we export the array
1412        let array = FFI_ArrowArray::new(expected);
1413        let schema = FFI_ArrowSchema::try_from(expected.data_type())?;
1414
1415        // simulate an external consumer by being the consumer
1416        let result = &unsafe { from_ffi(array, &schema) }?;
1417
1418        assert_eq!(result, expected);
1419        Ok(())
1420    }
1421
1422    #[test]
1423    fn test_u32() -> Result<()> {
1424        let array = UInt32Array::from(vec![Some(2), None, Some(1), None]);
1425        let data = array.into_data();
1426        test_round_trip(&data)
1427    }
1428
1429    #[test]
1430    fn test_u64() -> Result<()> {
1431        let array = UInt64Array::from(vec![Some(2), None, Some(1), None]);
1432        let data = array.into_data();
1433        test_round_trip(&data)
1434    }
1435
1436    #[test]
1437    fn test_i64() -> Result<()> {
1438        let array = Int64Array::from(vec![Some(2), None, Some(1), None]);
1439        let data = array.into_data();
1440        test_round_trip(&data)
1441    }
1442
1443    #[test]
1444    fn test_struct() -> Result<()> {
1445        let inner = StructArray::from(vec![
1446            (
1447                Arc::new(Field::new("a1", DataType::Boolean, false)),
1448                Arc::new(BooleanArray::from(vec![true, true, false, false])) as Arc<dyn Array>,
1449            ),
1450            (
1451                Arc::new(Field::new("a2", DataType::UInt32, false)),
1452                Arc::new(UInt32Array::from(vec![1, 2, 3, 4])),
1453            ),
1454        ]);
1455
1456        let array = StructArray::from(vec![
1457            (
1458                Arc::new(Field::new("a", inner.data_type().clone(), false)),
1459                Arc::new(inner) as Arc<dyn Array>,
1460            ),
1461            (
1462                Arc::new(Field::new("b", DataType::Boolean, false)),
1463                Arc::new(BooleanArray::from(vec![false, false, true, true])) as Arc<dyn Array>,
1464            ),
1465            (
1466                Arc::new(Field::new("c", DataType::UInt32, false)),
1467                Arc::new(UInt32Array::from(vec![42, 28, 19, 31])),
1468            ),
1469        ]);
1470        let data = array.into_data();
1471        test_round_trip(&data)
1472    }
1473
1474    #[test]
1475    fn test_dictionary() -> Result<()> {
1476        let values = StringArray::from(vec![Some("foo"), Some("bar"), None]);
1477        let keys = Int32Array::from(vec![
1478            Some(0),
1479            Some(1),
1480            None,
1481            Some(1),
1482            Some(1),
1483            None,
1484            Some(1),
1485            Some(2),
1486            Some(1),
1487            None,
1488        ]);
1489        let array = DictionaryArray::new(keys, Arc::new(values));
1490
1491        let data = array.into_data();
1492        test_round_trip(&data)
1493    }
1494
1495    #[test]
1496    fn test_fixed_size_binary() -> Result<()> {
1497        let values = vec![vec![10, 10, 10], vec![20, 20, 20], vec![30, 30, 30]];
1498        let array = FixedSizeBinaryArray::try_from_iter(values.into_iter())?;
1499
1500        let data = array.into_data();
1501        test_round_trip(&data)
1502    }
1503
1504    #[test]
1505    fn test_fixed_size_binary_with_nulls() -> Result<()> {
1506        let values = vec![
1507            None,
1508            Some(vec![10, 10, 10]),
1509            None,
1510            Some(vec![20, 20, 20]),
1511            Some(vec![30, 30, 30]),
1512            None,
1513        ];
1514        let array = FixedSizeBinaryArray::try_from_sparse_iter_with_size(values.into_iter(), 3)?;
1515
1516        let data = array.into_data();
1517        test_round_trip(&data)
1518    }
1519
1520    #[test]
1521    fn test_fixed_size_list() -> Result<()> {
1522        let v: Vec<i64> = (0..9).collect();
1523        let value_data = ArrayData::builder(DataType::Int64)
1524            .len(9)
1525            .add_buffer(Buffer::from_slice_ref(v))
1526            .build()?;
1527        let list_data_type =
1528            DataType::FixedSizeList(Arc::new(Field::new("f", DataType::Int64, false)), 3);
1529        let list_data = ArrayData::builder(list_data_type)
1530            .len(3)
1531            .add_child_data(value_data)
1532            .build()?;
1533        let array = FixedSizeListArray::from(list_data);
1534
1535        let data = array.into_data();
1536        test_round_trip(&data)
1537    }
1538
1539    #[test]
1540    fn test_fixed_size_list_with_nulls() -> Result<()> {
1541        // 0100 0110
1542        let mut validity_bits: [u8; 1] = [0; 1];
1543        bit_util::set_bit(&mut validity_bits, 1);
1544        bit_util::set_bit(&mut validity_bits, 2);
1545        bit_util::set_bit(&mut validity_bits, 6);
1546
1547        let v: Vec<i16> = (0..16).collect();
1548        let value_data = ArrayData::builder(DataType::Int16)
1549            .len(16)
1550            .add_buffer(Buffer::from_slice_ref(v))
1551            .build()?;
1552        let list_data_type =
1553            DataType::FixedSizeList(Arc::new(Field::new("f", DataType::Int16, false)), 2);
1554        let list_data = ArrayData::builder(list_data_type)
1555            .len(8)
1556            .null_bit_buffer(Some(Buffer::from(validity_bits)))
1557            .add_child_data(value_data)
1558            .build()?;
1559        let array = FixedSizeListArray::from(list_data);
1560
1561        let data = array.into_data();
1562        test_round_trip(&data)
1563    }
1564
1565    #[test]
1566    fn test_fixed_size_list_nested() -> Result<()> {
1567        let v: Vec<i32> = (0..16).collect();
1568        let value_data = ArrayData::builder(DataType::Int32)
1569            .len(16)
1570            .add_buffer(Buffer::from_slice_ref(v))
1571            .build()?;
1572
1573        let offsets: Vec<i32> = vec![0, 2, 4, 6, 8, 10, 12, 14, 16];
1574        let value_offsets = Buffer::from_slice_ref(offsets);
1575        let inner_list_data_type =
1576            DataType::List(Arc::new(Field::new_list_field(DataType::Int32, false)));
1577        let inner_list_data = ArrayData::builder(inner_list_data_type.clone())
1578            .len(8)
1579            .add_buffer(value_offsets)
1580            .add_child_data(value_data)
1581            .build()?;
1582
1583        // 0000 0100
1584        let mut validity_bits: [u8; 1] = [0; 1];
1585        bit_util::set_bit(&mut validity_bits, 2);
1586
1587        let list_data_type =
1588            DataType::FixedSizeList(Arc::new(Field::new("f", inner_list_data_type, false)), 2);
1589        let list_data = ArrayData::builder(list_data_type)
1590            .len(4)
1591            .null_bit_buffer(Some(Buffer::from(validity_bits)))
1592            .add_child_data(inner_list_data)
1593            .build()?;
1594
1595        let array = FixedSizeListArray::from(list_data);
1596
1597        let data = array.into_data();
1598        test_round_trip(&data)
1599    }
1600
1601    #[test]
1602    fn test_list_view() -> Result<()> {
1603        // Construct a value array
1604        let value_data = ArrayData::builder(DataType::Int16)
1605            .len(8)
1606            .add_buffer(Buffer::from_slice_ref([0_i16, 1, 2, 3, 4, 5, 6, 7]))
1607            .build()
1608            .unwrap();
1609
1610        // Construct a buffer for value offsets, for the nested array:
1611        //  [[0, 1, 2], [3, 4, 5], [6, 7]]
1612        let value_offsets = Buffer::from(vec![0_i32, 3, 6]);
1613        let sizes_buffer = Buffer::from(vec![3_i32, 3, 2]);
1614
1615        // Construct a list array from the above two
1616        let list_view_dt =
1617            DataType::ListView(Arc::new(Field::new_list_field(DataType::Int16, false)));
1618
1619        let list_view_data = ArrayData::builder(list_view_dt)
1620            .len(3)
1621            .add_buffer(value_offsets)
1622            .add_buffer(sizes_buffer)
1623            .add_child_data(value_data)
1624            .build()
1625            .unwrap();
1626
1627        test_round_trip(&list_view_data)
1628    }
1629
1630    #[test]
1631    fn test_list_view_with_nulls() -> Result<()> {
1632        // Construct a value array
1633        let value_data = ArrayData::builder(DataType::Int16)
1634            .len(8)
1635            .add_buffer(Buffer::from_slice_ref([0_i16, 1, 2, 3, 4, 5, 6, 7]))
1636            .build()
1637            .unwrap();
1638
1639        // Construct a buffer for value offsets, for the nested array:
1640        //  [[0, 1, 2], [3, 4, 5], [6, 7], null]
1641        let value_offsets = Buffer::from(vec![0_i32, 3, 6, 8]);
1642        let sizes_buffer = Buffer::from(vec![3_i32, 3, 2, 0]);
1643
1644        // Construct a list array from the above two
1645        let list_view_dt =
1646            DataType::ListView(Arc::new(Field::new_list_field(DataType::Int16, true)));
1647
1648        let list_view_data = ArrayData::builder(list_view_dt)
1649            .len(4)
1650            .add_buffer(value_offsets)
1651            .add_buffer(sizes_buffer)
1652            .add_child_data(value_data)
1653            .nulls(Some(NullBuffer::from(vec![true, true, true, false])))
1654            .build()
1655            .unwrap();
1656
1657        test_round_trip(&list_view_data)
1658    }
1659
1660    #[test]
1661    #[cfg(not(feature = "force_validate"))]
1662    fn test_empty_string_with_non_zero_offset() -> Result<()> {
1663        use super::ImportedArrowArray;
1664        use arrow_buffer::{MutableBuffer, OffsetBuffer};
1665
1666        // Simulate an empty string array with a non-zero offset from a producer
1667        let data: Buffer = MutableBuffer::new(0).into();
1668        let offsets = OffsetBuffer::new(vec![123].into());
1669        let string_array =
1670            unsafe { StringArray::new_unchecked(offsets.clone(), data.clone(), None) };
1671
1672        let data = string_array.into_data();
1673
1674        let array = FFI_ArrowArray::new(&data);
1675        let schema = FFI_ArrowSchema::try_from(data.data_type())?;
1676
1677        let dt = DataType::try_from(&schema)?;
1678        let array = Arc::new(array);
1679        let imported_array = ImportedArrowArray {
1680            array: &array,
1681            data_type: dt,
1682            owner: &array,
1683        };
1684
1685        let offset_buf_len = imported_array.buffer_len(1, &[], &imported_array.data_type)?;
1686        let data_buf_len = imported_array.buffer_len(2, &[], &imported_array.data_type)?;
1687
1688        assert_eq!(offset_buf_len, 4);
1689        assert_eq!(data_buf_len, 0);
1690
1691        test_round_trip(&imported_array.consume()?)
1692    }
1693
1694    fn roundtrip_string_array(array: StringArray) -> StringArray {
1695        let data = array.into_data();
1696
1697        let array = FFI_ArrowArray::new(&data);
1698        let schema = FFI_ArrowSchema::try_from(data.data_type()).unwrap();
1699
1700        let array = unsafe { from_ffi(array, &schema) }.unwrap();
1701        StringArray::from(array)
1702    }
1703
1704    fn roundtrip_byte_view_array<T: ByteViewType>(
1705        array: GenericByteViewArray<T>,
1706    ) -> GenericByteViewArray<T> {
1707        let data = array.into_data();
1708
1709        let array = FFI_ArrowArray::new(&data);
1710        let schema = FFI_ArrowSchema::try_from(data.data_type()).unwrap();
1711
1712        let array = unsafe { from_ffi(array, &schema) }.unwrap();
1713        GenericByteViewArray::<T>::from(array)
1714    }
1715
1716    fn extend_array(array: &dyn Array) -> ArrayRef {
1717        let len = array.len();
1718        let data = array.to_data();
1719
1720        let mut mutable = MutableArrayData::new(vec![&data], false, len);
1721        mutable.try_extend(0, 0, len).unwrap();
1722        make_array(mutable.freeze())
1723    }
1724
1725    #[test]
1726    fn test_extend_imported_string_slice() {
1727        let mut strings = vec![];
1728
1729        for i in 0..1000 {
1730            strings.push(format!("string: {i}"));
1731        }
1732
1733        let string_array = StringArray::from(strings);
1734
1735        let imported = roundtrip_string_array(string_array.clone());
1736        assert_eq!(imported.len(), 1000);
1737        assert_eq!(imported.value(0), "string: 0");
1738        assert_eq!(imported.value(499), "string: 499");
1739
1740        let copied = extend_array(&imported);
1741        assert_eq!(
1742            copied.as_any().downcast_ref::<StringArray>().unwrap(),
1743            &imported
1744        );
1745
1746        let slice = string_array.slice(500, 500);
1747
1748        let imported = roundtrip_string_array(slice);
1749        assert_eq!(imported.len(), 500);
1750        assert_eq!(imported.value(0), "string: 500");
1751        assert_eq!(imported.value(499), "string: 999");
1752
1753        let copied = extend_array(&imported);
1754        assert_eq!(
1755            copied.as_any().downcast_ref::<StringArray>().unwrap(),
1756            &imported
1757        );
1758    }
1759
1760    fn roundtrip_list_array(array: ListArray) -> ListArray {
1761        let data = array.into_data();
1762
1763        let array = FFI_ArrowArray::new(&data);
1764        let schema = FFI_ArrowSchema::try_from(data.data_type()).unwrap();
1765
1766        let array = unsafe { from_ffi(array, &schema) }.unwrap();
1767        ListArray::from(array)
1768    }
1769
1770    #[test]
1771    fn test_extend_imported_list_slice() {
1772        let mut data = vec![];
1773
1774        for i in 0..1000 {
1775            let mut list = vec![];
1776            for j in 0..100 {
1777                list.push(Some(i * 1000 + j));
1778            }
1779            data.push(Some(list));
1780        }
1781
1782        let list_array = ListArray::from_iter_primitive::<Int32Type, _, _>(data);
1783
1784        let slice = list_array.slice(500, 500);
1785        let imported = roundtrip_list_array(slice.clone());
1786        assert_eq!(imported.len(), 500);
1787        assert_eq!(&slice, &imported);
1788
1789        let copied = extend_array(&imported);
1790        assert_eq!(
1791            copied.as_any().downcast_ref::<ListArray>().unwrap(),
1792            &imported
1793        );
1794    }
1795
1796    /// Helper trait to allow us to use easily strings as either BinaryViewType::Native or
1797    /// StringViewType::Native scalars.
1798    trait NativeFromStr {
1799        fn from_str(value: &str) -> &Self;
1800    }
1801
1802    impl NativeFromStr for str {
1803        fn from_str(value: &str) -> &Self {
1804            value
1805        }
1806    }
1807
1808    impl NativeFromStr for [u8] {
1809        fn from_str(value: &str) -> &Self {
1810            value.as_bytes()
1811        }
1812    }
1813
1814    #[test]
1815    #[cfg(not(feature = "force_validate"))]
1816    fn test_utf8_view_ffi_from_dangling_pointer() {
1817        let empty = GenericByteViewBuilder::<StringViewType>::new().finish();
1818        let buffers = empty.data_buffers().to_vec();
1819        let nulls = empty.nulls().cloned();
1820
1821        // Create a dangling pointer to a view buffer with zero length.
1822        let alloc = Arc::new(1);
1823        let buffer = unsafe { Buffer::from_custom_allocation(NonNull::<u8>::dangling(), 0, alloc) };
1824        let views = unsafe { ScalarBuffer::new_unchecked(buffer) };
1825
1826        let str_view: GenericByteViewArray<StringViewType> =
1827            unsafe { GenericByteViewArray::new_unchecked(views, buffers, nulls) };
1828        let imported = roundtrip_byte_view_array(str_view);
1829        assert_eq!(imported.len(), 0);
1830        assert_eq!(&imported, &empty);
1831    }
1832
1833    #[test]
1834    fn test_round_trip_byte_view() {
1835        fn test_case<T>()
1836        where
1837            T: ByteViewType,
1838            T::Native: NativeFromStr,
1839        {
1840            macro_rules! run_test_case {
1841                ($array:expr) => {{
1842                    // round-trip through C  Data Interface
1843                    let len = $array.len();
1844                    let imported = roundtrip_byte_view_array($array);
1845                    assert_eq!(imported.len(), len);
1846
1847                    let copied = extend_array(&imported);
1848                    assert_eq!(
1849                        copied
1850                            .as_any()
1851                            .downcast_ref::<GenericByteViewArray<T>>()
1852                            .unwrap(),
1853                        &imported
1854                    );
1855                }};
1856            }
1857
1858            // Empty test case.
1859            let empty = GenericByteViewBuilder::<T>::new().finish();
1860            run_test_case!(empty);
1861
1862            // All inlined strings test case.
1863            let mut all_inlined = GenericByteViewBuilder::<T>::new();
1864            all_inlined.append_value(T::Native::from_str("inlined1"));
1865            all_inlined.append_value(T::Native::from_str("inlined2"));
1866            all_inlined.append_value(T::Native::from_str("inlined3"));
1867            let all_inlined = all_inlined.finish();
1868            assert_eq!(all_inlined.data_buffers().len(), 0);
1869            run_test_case!(all_inlined);
1870
1871            // some inlined + non-inlined, 1 variadic buffer.
1872            let mixed_one_variadic = {
1873                let mut builder = GenericByteViewBuilder::<T>::new();
1874                builder.append_value(T::Native::from_str("inlined"));
1875                let block_id =
1876                    builder.append_block(Buffer::from("non-inlined-string-buffer".as_bytes()));
1877                builder.try_append_view(block_id, 0, 25).unwrap();
1878                builder.finish()
1879            };
1880            assert_eq!(mixed_one_variadic.data_buffers().len(), 1);
1881            run_test_case!(mixed_one_variadic);
1882
1883            // inlined + non-inlined, 2 variadic buffers.
1884            let mixed_two_variadic = {
1885                let mut builder = GenericByteViewBuilder::<T>::new();
1886                builder.append_value(T::Native::from_str("inlined"));
1887                let block_id =
1888                    builder.append_block(Buffer::from("non-inlined-string-buffer".as_bytes()));
1889                builder.try_append_view(block_id, 0, 25).unwrap();
1890
1891                let block_id = builder
1892                    .append_block(Buffer::from("another-non-inlined-string-buffer".as_bytes()));
1893                builder.try_append_view(block_id, 0, 33).unwrap();
1894                builder.finish()
1895            };
1896            assert_eq!(mixed_two_variadic.data_buffers().len(), 2);
1897            run_test_case!(mixed_two_variadic);
1898        }
1899
1900        test_case::<StringViewType>();
1901        test_case::<BinaryViewType>();
1902    }
1903}