diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index 964e7cbe348b..89da9552701c 100644 --- a/arrow-array/src/array/byte_view_array.rs +++ b/arrow-array/src/array/byte_view_array.rs @@ -306,6 +306,23 @@ impl GenericByteViewArray { &self.buffers } + /// Returns a cloned `Arc` of the buffers storing non-inline string or binary data. + /// + /// This is useful when needing to construct a new byte view array from this existing + /// array, but [`into_parts`] is not feasible (e.g. need to keep both arrays around), + /// and trying to reconstruct the buffers from [`data_buffers`] would require a + /// `Vec` allocation and cloning of each buffer element, which can be expensive + /// if there is a large number of buffers. + /// + /// This operation is `O(1)` and clones only the collection's `Arc`. + /// + /// [`into_parts`]: Self::into_parts + /// [`data_buffers`]: Self::data_buffers + #[inline] + pub fn data_buffers_cloned(&self) -> Arc<[Buffer]> { + Arc::clone(&self.buffers) + } + /// Returns the element at index `i` /// /// Note: This method does not check for nulls and the value is arbitrary diff --git a/arrow-select/src/filter.rs b/arrow-select/src/filter.rs index 1c49826fe3d3..c7497d23e652 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 = array.data_buffers_cloned(); 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_cloned(); + let input_buffers = array.data_buffers_cloned(); + assert!(Arc::ptr_eq(&actual_buffers, &input_buffers)); let expected = { // ["hello", null, "large payload over 12 bytes"] diff --git a/arrow-select/src/take.rs b/arrow-select/src/take.rs index f8ff461d2cee..8f7d5287bd45 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 = array.data_buffers_cloned(); // 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_cloned(); + let input_buffers = array.data_buffers_cloned(); + assert!(Arc::ptr_eq(&actual_buffers, &input_buffers)); let expected = { // ["large payload over 12 bytes", null, "world", "large payload over 12 bytes", "lulu", null]