From 46c989ee07854da2f35c758b31256d0241c95f93 Mon Sep 17 00:00:00 2001 From: yiqiao Date: Sun, 16 Aug 2026 23:31:23 +0800 Subject: [PATCH 1/3] perf: avoid cloning ByteView buffer lists in take and filter --- arrow-array/src/array/byte_view_array.rs | 10 +++++++ arrow-select/src/filter.rs | 5 +++- arrow-select/src/take.rs | 8 ++++-- arrow/benches/take_kernels.rs | 35 +++++++++++++++++++++++- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index 964e7cbe348b..5d70b797936f 100644 --- a/arrow-array/src/array/byte_view_array.rs +++ b/arrow-array/src/array/byte_view_array.rs @@ -306,6 +306,16 @@ impl GenericByteViewArray { &self.buffers } + /// Returns shared ownership of the buffers storing non-inline values + /// + /// This operation is `O(1)` and does not clone the individual buffers or + /// their contents. See [`Self::data_buffers`] to inspect the buffers + /// without taking shared ownership. + #[inline] + pub fn data_buffers_shared(&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..a66478aabd93 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_shared(); 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_shared(); + let input_buffers = array.data_buffers_shared(); + 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..56676c13a751 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_shared(); // 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_shared(); + let input_buffers = array.data_buffers_shared(); + 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/arrow/benches/take_kernels.rs b/arrow/benches/take_kernels.rs index 4e0be5b9a50d..f6fead3040d2 100644 --- a/arrow/benches/take_kernels.rs +++ b/arrow/benches/take_kernels.rs @@ -17,10 +17,11 @@ #[macro_use] extern crate criterion; -use criterion::Criterion; +use criterion::{BenchmarkId, Criterion}; use rand::RngExt; +use arrow::buffer::{Buffer, ScalarBuffer}; use arrow::compute::{TakeOptions, take, take_record_batch}; use arrow::datatypes::*; use arrow::record_batch::RecordBatch; @@ -71,6 +72,26 @@ fn bench_take_bounds_check(values: &dyn Array, indices: &UInt32Array) { hint::black_box(take(values, indices, Some(TakeOptions { check_bounds: true })).unwrap()); } +/// Creates an array whose buffer entries all share the same payload allocation. +/// +/// Views reference every entry in turn, isolating the cost of cloning the buffer +/// collection from the size of the underlying string payload. +fn create_string_view_array_with_buffers(size: usize, buffer_count: usize) -> StringViewArray { + const VALUE: &[u8] = b"a string longer than twelve bytes"; + + let buffer = Buffer::from(VALUE); + let buffers = vec![buffer; buffer_count]; + let views = (0..size) + .map(|i| { + ByteView::new(VALUE.len() as u32, &VALUE[..4]) + .with_buffer_index((i % buffer_count) as u32) + .as_u128() + }) + .collect::>(); + + StringViewArray::new(views, buffers, None) +} + fn add_benchmark(c: &mut Criterion) { let values = create_primitive_array::(512, 0.0); let indices = create_random_index(512, 0.0); @@ -206,6 +227,18 @@ fn add_benchmark(c: &mut Criterion) { b.iter(|| bench_take(&values, &indices)) }); + let indices = create_random_index(8192, 0.0); + let mut group = c.benchmark_group("take stringview by buffer count"); + for buffer_count in [1, 16, 256, 4096] { + let values = create_string_view_array_with_buffers(8192, buffer_count); + group.bench_with_input( + BenchmarkId::from_parameter(buffer_count), + &buffer_count, + |b, _| b.iter(|| bench_take(&values, &indices)), + ); + } + group.finish(); + let values = create_primitive_list_array::(512, 0.0, 0.0, 20); let indices = create_random_index(512, 0.0); c.bench_function("take list i32 512", |b| { From 78ce9c6cbf495d803cc83d4c1e90c61fa2a71285 Mon Sep 17 00:00:00 2001 From: yiqiao Date: Mon, 17 Aug 2026 16:11:07 +0800 Subject: [PATCH 2/3] refine byte view buffer sharing API --- arrow-array/src/array/byte_view_array.rs | 17 ++++++++---- arrow-select/src/filter.rs | 6 ++-- arrow-select/src/take.rs | 6 ++-- arrow/benches/take_kernels.rs | 35 +----------------------- 4 files changed, 19 insertions(+), 45 deletions(-) diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index 5d70b797936f..89da9552701c 100644 --- a/arrow-array/src/array/byte_view_array.rs +++ b/arrow-array/src/array/byte_view_array.rs @@ -306,13 +306,20 @@ impl GenericByteViewArray { &self.buffers } - /// Returns shared ownership of the buffers storing non-inline values + /// Returns a cloned `Arc` of the buffers storing non-inline string or binary data. /// - /// This operation is `O(1)` and does not clone the individual buffers or - /// their contents. See [`Self::data_buffers`] to inspect the buffers - /// without taking shared ownership. + /// 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_shared(&self) -> Arc<[Buffer]> { + pub fn data_buffers_cloned(&self) -> Arc<[Buffer]> { Arc::clone(&self.buffers) } diff --git a/arrow-select/src/filter.rs b/arrow-select/src/filter.rs index a66478aabd93..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_shared(); + 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,8 +1297,8 @@ mod tests { let actual = filter(&array, &predicate).unwrap(); assert_eq!(actual.len(), 3); - let actual_buffers = actual.as_byte_view::().data_buffers_shared(); - let input_buffers = array.data_buffers_shared(); + 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 = { diff --git a/arrow-select/src/take.rs b/arrow-select/src/take.rs index 56676c13a751..8f7d5287bd45 100644 --- a/arrow-select/src/take.rs +++ b/arrow-select/src/take.rs @@ -636,7 +636,7 @@ 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_shared(); + 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, buffers, new_nulls) }) } @@ -1805,8 +1805,8 @@ mod tests { let actual = take(&array, &index, None).unwrap(); assert_eq!(actual.len(), index.len()); - let actual_buffers = actual.as_byte_view::().data_buffers_shared(); - let input_buffers = array.data_buffers_shared(); + 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 = { diff --git a/arrow/benches/take_kernels.rs b/arrow/benches/take_kernels.rs index f6fead3040d2..4e0be5b9a50d 100644 --- a/arrow/benches/take_kernels.rs +++ b/arrow/benches/take_kernels.rs @@ -17,11 +17,10 @@ #[macro_use] extern crate criterion; -use criterion::{BenchmarkId, Criterion}; +use criterion::Criterion; use rand::RngExt; -use arrow::buffer::{Buffer, ScalarBuffer}; use arrow::compute::{TakeOptions, take, take_record_batch}; use arrow::datatypes::*; use arrow::record_batch::RecordBatch; @@ -72,26 +71,6 @@ fn bench_take_bounds_check(values: &dyn Array, indices: &UInt32Array) { hint::black_box(take(values, indices, Some(TakeOptions { check_bounds: true })).unwrap()); } -/// Creates an array whose buffer entries all share the same payload allocation. -/// -/// Views reference every entry in turn, isolating the cost of cloning the buffer -/// collection from the size of the underlying string payload. -fn create_string_view_array_with_buffers(size: usize, buffer_count: usize) -> StringViewArray { - const VALUE: &[u8] = b"a string longer than twelve bytes"; - - let buffer = Buffer::from(VALUE); - let buffers = vec![buffer; buffer_count]; - let views = (0..size) - .map(|i| { - ByteView::new(VALUE.len() as u32, &VALUE[..4]) - .with_buffer_index((i % buffer_count) as u32) - .as_u128() - }) - .collect::>(); - - StringViewArray::new(views, buffers, None) -} - fn add_benchmark(c: &mut Criterion) { let values = create_primitive_array::(512, 0.0); let indices = create_random_index(512, 0.0); @@ -227,18 +206,6 @@ fn add_benchmark(c: &mut Criterion) { b.iter(|| bench_take(&values, &indices)) }); - let indices = create_random_index(8192, 0.0); - let mut group = c.benchmark_group("take stringview by buffer count"); - for buffer_count in [1, 16, 256, 4096] { - let values = create_string_view_array_with_buffers(8192, buffer_count); - group.bench_with_input( - BenchmarkId::from_parameter(buffer_count), - &buffer_count, - |b, _| b.iter(|| bench_take(&values, &indices)), - ); - } - group.finish(); - let values = create_primitive_list_array::(512, 0.0, 0.0, 20); let indices = create_random_index(512, 0.0); c.bench_function("take list i32 512", |b| { From 2c3a411493853f5a4a43fcdfb5ddddd0f56d6dba Mon Sep 17 00:00:00 2001 From: yiqiao Date: Fri, 21 Aug 2026 11:07:40 +0800 Subject: [PATCH 3/3] refine ByteView buffer ownership APIs --- arrow-array/src/array/byte_view_array.rs | 44 ++++++------------- .../src/builder/generic_bytes_view_builder.rs | 4 +- arrow-array/src/ffi.rs | 2 +- arrow-ipc/src/reader.rs | 2 +- arrow-row/src/variable.rs | 2 +- arrow-select/src/coalesce/byte_view.rs | 5 ++- arrow-select/src/filter.rs | 8 ++-- arrow-select/src/interleave.rs | 2 +- arrow-select/src/take.rs | 8 ++-- parquet/src/arrow/buffer/view_buffer.rs | 5 ++- parquet/tests/arrow_reader/invalid_utf8.rs | 2 +- 11 files changed, 35 insertions(+), 49 deletions(-) diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index 89da9552701c..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,27 +297,14 @@ impl GenericByteViewArray { &self.views } - /// Returns the buffers storing string data - #[inline] - pub fn data_buffers(&self) -> &[Buffer] { - &self.buffers - } - - /// Returns a cloned `Arc` of the buffers storing non-inline string or binary data. + /// Returns the shared collection of 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 + /// 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_cloned(&self) -> Arc<[Buffer]> { - Arc::clone(&self.buffers) + pub fn data_buffers(&self) -> &Arc<[Buffer]> { + &self.buffers } /// Returns the element at index `i` @@ -555,7 +539,7 @@ impl GenericByteViewArray { return unsafe { GenericByteViewArray::new_unchecked( self.views().clone(), - vec![], // empty data blocks + Arc::from([]), // empty data blocks nulls, ) }; @@ -570,7 +554,7 @@ impl GenericByteViewArray { return unsafe { GenericByteViewArray::new_unchecked( self.views().clone(), - vec![], // empty data blocks + Arc::from([]), // empty data blocks nulls, ) }; @@ -669,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. @@ -1648,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 c7497d23e652..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_cloned(); + 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,9 +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 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 8f7d5287bd45..89901806bb55 100644 --- a/arrow-select/src/take.rs +++ b/arrow-select/src/take.rs @@ -636,7 +636,7 @@ 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(); + 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, buffers, new_nulls) }) } @@ -1805,9 +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 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(), ) };