Skip to main content

arrow_array/
ffi.rs

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