diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index 964e7cbe348b..7b6afe8649c3 100644 --- a/arrow-array/src/array/byte_view_array.rs +++ b/arrow-array/src/array/byte_view_array.rs @@ -238,14 +238,11 @@ impl GenericByteViewArray { /// # Safety /// /// Safe if [`Self::try_new`] would not error - pub unsafe fn new_unchecked( + pub unsafe fn new_unchecked( views: ScalarBuffer, - buffers: U, + buffers: Arc<[Buffer]>, nulls: Option, - ) -> Self - where - U: Into>, - { + ) -> Self { if cfg!(feature = "force_validate") { return Self::new(views, buffers, nulls); } @@ -254,7 +251,7 @@ impl GenericByteViewArray { data_type: T::DATA_TYPE, phantom: Default::default(), views, - buffers: buffers.into(), + buffers, nulls, } } @@ -300,9 +297,13 @@ impl GenericByteViewArray { &self.views } - /// Returns the buffers storing string data + /// Returns the shared collection of buffers storing non-inline string or binary data. + /// + /// The returned `Arc` can be cloned to share the buffers with another array without + /// allocating a new collection or cloning the individual buffers. To consume this + /// array and take ownership of its buffers, use [`Self::into_parts`]. #[inline] - pub fn data_buffers(&self) -> &[Buffer] { + pub fn data_buffers(&self) -> &Arc<[Buffer]> { &self.buffers } @@ -538,7 +539,7 @@ impl GenericByteViewArray { return unsafe { GenericByteViewArray::new_unchecked( self.views().clone(), - vec![], // empty data blocks + Arc::from([]), // empty data blocks nulls, ) }; @@ -553,7 +554,7 @@ impl GenericByteViewArray { return unsafe { GenericByteViewArray::new_unchecked( self.views().clone(), - vec![], // empty data blocks + Arc::from([]), // empty data blocks nulls, ) }; @@ -652,7 +653,7 @@ impl GenericByteViewArray { let views_scalar = ScalarBuffer::from(views_buf); // SAFETY: views_scalar, data_blocks, and nulls are correctly aligned and sized - unsafe { GenericByteViewArray::new_unchecked(views_scalar, data_blocks, nulls) } + unsafe { GenericByteViewArray::new_unchecked(views_scalar, data_blocks.into(), nulls) } } /// Copy the i‑th view into `data_buf` if it refers to an out‑of‑line buffer. @@ -1631,7 +1632,7 @@ mod tests { gced.data_buffers().len() ); // No output buffer may exceed the cap. - for buf in gced.data_buffers() { + for buf in gced.data_buffers().iter() { assert!(buf.len() <= max_buffer_size, "buffer exceeded max size"); } // Every value (inline, large, and null) is unchanged and in order. diff --git a/arrow-array/src/builder/generic_bytes_view_builder.rs b/arrow-array/src/builder/generic_bytes_view_builder.rs index cb7ecf85ebe9..98156188ed25 100644 --- a/arrow-array/src/builder/generic_bytes_view_builder.rs +++ b/arrow-array/src/builder/generic_bytes_view_builder.rs @@ -500,7 +500,7 @@ impl GenericByteViewBuilder { } let views = std::mem::take(&mut self.views_buffer); // SAFETY: valid by construction - unsafe { GenericByteViewArray::new_unchecked(views.into(), completed, nulls) } + unsafe { GenericByteViewArray::new_unchecked(views.into(), completed.into(), nulls) } } /// Builds the [`GenericByteViewArray`] without resetting the builder @@ -514,7 +514,7 @@ impl GenericByteViewBuilder { let views = ScalarBuffer::new(views, 0, len); let nulls = self.null_buffer_builder.finish_cloned(); // SAFETY: valid by construction - unsafe { GenericByteViewArray::new_unchecked(views, completed, nulls) } + unsafe { GenericByteViewArray::new_unchecked(views, completed.into(), nulls) } } /// Returns the current null buffer as a slice diff --git a/arrow-array/src/ffi.rs b/arrow-array/src/ffi.rs index 8227034a9ebc..635b6d355055 100644 --- a/arrow-array/src/ffi.rs +++ b/arrow-array/src/ffi.rs @@ -1815,7 +1815,7 @@ mod tests_from_ffi { #[cfg(not(feature = "force_validate"))] fn test_utf8_view_ffi_from_dangling_pointer() { let empty = GenericByteViewBuilder::::new().finish(); - let buffers = empty.data_buffers().to_vec(); + let buffers = Arc::clone(empty.data_buffers()); let nulls = empty.nulls().cloned(); // Create a dangling pointer to a view buffer with zero length. diff --git a/arrow-ipc/src/reader.rs b/arrow-ipc/src/reader.rs index f501e4c34467..1e1ed0e7edb1 100644 --- a/arrow-ipc/src/reader.rs +++ b/arrow-ipc/src/reader.rs @@ -3567,7 +3567,7 @@ mod tests { let array = unsafe { StringViewArray::new_unchecked( binary_view_array.views().clone(), - binary_view_array.data_buffers().to_vec(), + Arc::clone(binary_view_array.data_buffers()), binary_view_array.nulls().cloned(), ) }; diff --git a/arrow-row/src/variable.rs b/arrow-row/src/variable.rs index f196e0e22091..2dfc1807b6c6 100644 --- a/arrow-row/src/variable.rs +++ b/arrow-row/src/variable.rs @@ -375,7 +375,7 @@ fn decode_binary_view_inner( // SAFETY: // Valid by construction above - unsafe { BinaryViewArray::new_unchecked(views.into(), [values.into()], nulls) } + unsafe { BinaryViewArray::new_unchecked(views.into(), [values.into()].into(), nulls) } } /// Decodes a binary view array from `rows` with the provided `options` diff --git a/arrow-select/src/coalesce/byte_view.rs b/arrow-select/src/coalesce/byte_view.rs index 5bfee774b81d..48d7024d2903 100644 --- a/arrow-select/src/coalesce/byte_view.rs +++ b/arrow-select/src/coalesce/byte_view.rs @@ -501,8 +501,9 @@ impl InProgressArray for InProgressByteViewArray { // Safety: we created valid views and buffers above and the // input arrays had value data and nulls - let new_array = - unsafe { GenericByteViewArray::::new_unchecked(views.into(), buffers, nulls) }; + let new_array = unsafe { + GenericByteViewArray::::new_unchecked(views.into(), buffers.into(), nulls) + }; Ok(Arc::new(new_array)) } diff --git a/arrow-select/src/filter.rs b/arrow-select/src/filter.rs index 1c49826fe3d3..cea7250c9b73 100644 --- a/arrow-select/src/filter.rs +++ b/arrow-select/src/filter.rs @@ -934,7 +934,7 @@ fn filter_byte_view( ) -> GenericByteViewArray { let new_view_buffer = filter_native(array.views(), predicate); let views = ScalarBuffer::new(new_view_buffer, 0, predicate.count); - let buffers = array.data_buffers().to_vec(); + let buffers = Arc::clone(array.data_buffers()); let nulls = predicate.filter_nulls(array.nulls()); // SAFETY: each view is copied unchanged from `array.views()` and `buffers` @@ -1297,6 +1297,9 @@ mod tests { let actual = filter(&array, &predicate).unwrap(); assert_eq!(actual.len(), 3); + let actual_buffers = actual.as_byte_view::().data_buffers(); + let input_buffers = array.data_buffers(); + assert!(Arc::ptr_eq(actual_buffers, input_buffers)); let expected = { // ["hello", null, "large payload over 12 bytes"] diff --git a/arrow-select/src/interleave.rs b/arrow-select/src/interleave.rs index d84370947957..31679ba2388b 100644 --- a/arrow-select/src/interleave.rs +++ b/arrow-select/src/interleave.rs @@ -340,7 +340,7 @@ fn interleave_views( .collect(); let array = unsafe { - GenericByteViewArray::::new_unchecked(views.into(), buffers, interleaved.nulls) + GenericByteViewArray::::new_unchecked(views.into(), buffers.into(), interleaved.nulls) }; Ok(Arc::new(array)) } diff --git a/arrow-select/src/take.rs b/arrow-select/src/take.rs index f8ff461d2cee..89901806bb55 100644 --- a/arrow-select/src/take.rs +++ b/arrow-select/src/take.rs @@ -636,10 +636,9 @@ fn take_byte_view( ) -> Result, ArrowError> { let new_views = take_native(array.views(), indices); let new_nulls = take_nulls(array.nulls(), indices); + let buffers = Arc::clone(array.data_buffers()); // Safety: array.views was valid, and take_native copies only valid values, and verifies bounds - Ok(unsafe { - GenericByteViewArray::new_unchecked(new_views, array.data_buffers().to_vec(), new_nulls) - }) + Ok(unsafe { GenericByteViewArray::new_unchecked(new_views, buffers, new_nulls) }) } /// `take` implementation for list arrays @@ -1806,6 +1805,9 @@ mod tests { let actual = take(&array, &index, None).unwrap(); assert_eq!(actual.len(), index.len()); + let actual_buffers = actual.as_byte_view::().data_buffers(); + let input_buffers = array.data_buffers(); + assert!(Arc::ptr_eq(actual_buffers, input_buffers)); let expected = { // ["large payload over 12 bytes", null, "world", "large payload over 12 bytes", "lulu", null] diff --git a/parquet/src/arrow/buffer/view_buffer.rs b/parquet/src/arrow/buffer/view_buffer.rs index 9670d0e9bb76..61458eec0b9b 100644 --- a/parquet/src/arrow/buffer/view_buffer.rs +++ b/parquet/src/arrow/buffer/view_buffer.rs @@ -57,14 +57,15 @@ impl ViewBuffer { let len = self.views.len(); let views = ScalarBuffer::from(self.views); let nulls = null_buffer.and_then(|b| NullBuffer::from_unsliced_buffer(b, len)); + let buffers = self.buffers.into(); match data_type { ArrowType::Utf8View => { // Safety: views were created correctly, and checked that the data is utf8 when building the buffer - unsafe { Arc::new(StringViewArray::new_unchecked(views, self.buffers, nulls)) } + unsafe { Arc::new(StringViewArray::new_unchecked(views, buffers, nulls)) } } ArrowType::BinaryView => { // Safety: views were created correctly - unsafe { Arc::new(BinaryViewArray::new_unchecked(views, self.buffers, nulls)) } + unsafe { Arc::new(BinaryViewArray::new_unchecked(views, buffers, nulls)) } } _ => panic!("Unsupported data type: {data_type}"), } diff --git a/parquet/tests/arrow_reader/invalid_utf8.rs b/parquet/tests/arrow_reader/invalid_utf8.rs index 1124737a7fe5..ff1adac3748e 100644 --- a/parquet/tests/arrow_reader/invalid_utf8.rs +++ b/parquet/tests/arrow_reader/invalid_utf8.rs @@ -125,7 +125,7 @@ fn test_invalid_utf8_string_view_array() { let array = unsafe { StringViewArray::new_unchecked( array.views().clone(), - array.data_buffers().to_vec(), + Arc::clone(array.data_buffers()), array.nulls().cloned(), ) };