From 06a0a2de8fb542547a0db9dedc4c4b7e63e27dd7 Mon Sep 17 00:00:00 2001 From: Matt Butrovich Date: Fri, 10 Apr 2026 11:41:23 -0400 Subject: [PATCH 1/8] Add bitand_many, union_many, and relevant tests. --- arrow-buffer/src/buffer/boolean.rs | 39 +++++++++++++++++++ arrow-buffer/src/buffer/null.rs | 62 ++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/arrow-buffer/src/buffer/boolean.rs b/arrow-buffer/src/buffer/boolean.rs index 420bbf59f3be..d15ca8fab27c 100644 --- a/arrow-buffer/src/buffer/boolean.rs +++ b/arrow-buffer/src/buffer/boolean.rs @@ -625,6 +625,20 @@ impl BooleanBuffer { pub fn set_slices(&self) -> BitSliceIterator<'_> { BitSliceIterator::new(self.values(), self.bit_offset, self.bit_len) } + + /// Bitwise AND of multiple [`BooleanBuffer`]s + /// + /// # Panics + /// + /// Panics if `buffers` is empty, or if the buffers have different lengths + pub fn bitand_many(buffers: &[&BooleanBuffer]) -> BooleanBuffer { + assert!(!buffers.is_empty(), "requires at least one buffer"); + let mut result = buffers[0].clone(); + for buf in &buffers[1..] { + result &= *buf; + } + result + } } impl Not for &BooleanBuffer { @@ -1245,4 +1259,29 @@ mod tests { let buffer = BooleanBuffer::new_unset(100); assert_eq!(buffer.clone().find_nth_set_bit_position(0, 1), 100); } + + #[test] + fn test_bitand_many_single() { + let a = BooleanBuffer::from(&[true, false, true, true] as &[bool]); + let result = BooleanBuffer::bitand_many(&[&a]); + assert_eq!(result, a); + } + + #[test] + fn test_bitand_many_two() { + let a = BooleanBuffer::from(&[true, false, true, true] as &[bool]); + let b = BooleanBuffer::from(&[true, true, false, true] as &[bool]); + let result = BooleanBuffer::bitand_many(&[&a, &b]); + assert_eq!(result, &a & &b); + } + + #[test] + fn test_bitand_many_three() { + let a = BooleanBuffer::from(&[true, false, true, true] as &[bool]); + let b = BooleanBuffer::from(&[true, true, false, true] as &[bool]); + let c = BooleanBuffer::from(&[false, true, true, true] as &[bool]); + let result = BooleanBuffer::bitand_many(&[&a, &b, &c]); + let expected = BooleanBuffer::from(&[false, false, false, true] as &[bool]); + assert_eq!(result, expected); + } } diff --git a/arrow-buffer/src/buffer/null.rs b/arrow-buffer/src/buffer/null.rs index 6046369c62a7..8ee26ee2d490 100644 --- a/arrow-buffer/src/buffer/null.rs +++ b/arrow-buffer/src/buffer/null.rs @@ -84,6 +84,21 @@ impl NullBuffer { } } + /// Computes the union of the nulls in multiple optional [`NullBuffer`]s + /// + /// See [`union`](Self::union) + pub fn union_many(nulls: &[Option<&NullBuffer>]) -> Option { + // Unwrap to BooleanBuffer because BitAndAssign is not implemented for NullBuffer + let buffers: Vec<&BooleanBuffer> = nulls + .iter() + .filter_map(|nb| nb.map(NullBuffer::inner)) + .collect(); + if buffers.is_empty() { + return None; + } + Some(Self::new(BooleanBuffer::bitand_many(&buffers))) + } + /// Returns true if all nulls in `other` also exist in self pub fn contains(&self, other: &NullBuffer) -> bool { if other.null_count == 0 { @@ -336,4 +351,51 @@ mod tests { let result = NullBuffer::from_unsliced_buffer(buf, 0); assert!(result.is_none()); } + + #[test] + fn test_union_many_all_none() { + let result = NullBuffer::union_many(&[None, None, None]); + assert!(result.is_none()); + } + + #[test] + fn test_union_many_single_some() { + let a = NullBuffer::from(&[true, false, true, true]); + let result = NullBuffer::union_many(&[Some(&a)]); + assert_eq!(result, Some(a)); + } + + #[test] + fn test_union_many_two_inputs() { + let a = NullBuffer::from(&[true, false, true, true]); + let b = NullBuffer::from(&[true, true, false, true]); + let result = NullBuffer::union_many(&[Some(&a), Some(&b)]); + let expected = NullBuffer::union(Some(&a), Some(&b)); + assert_eq!(result, expected); + } + + #[test] + fn test_union_many_three_inputs() { + let a = NullBuffer::from(&[true, false, true, true]); + let b = NullBuffer::from(&[true, true, false, true]); + let c = NullBuffer::from(&[false, true, true, true]); + let result = NullBuffer::union_many(&[Some(&a), Some(&b), Some(&c)]); + let expected = NullBuffer::from(&[false, false, false, true]); + assert_eq!(result, Some(expected)); + } + + #[test] + fn test_union_many_mixed_none() { + let a = NullBuffer::from(&[true, false, true, true]); + let b = NullBuffer::from(&[false, true, true, true]); + let result = NullBuffer::union_many(&[Some(&a), None, Some(&b)]); + let expected = NullBuffer::union(Some(&a), Some(&b)); + assert_eq!(result, expected); + } + + #[test] + fn test_union_many_empty_slice() { + let result = NullBuffer::union_many(&[]); + assert!(result.is_none()); + } } From d4d4fe58402e061113d06b097713007a0d91ccc0 Mon Sep 17 00:00:00 2001 From: Matt Butrovich Date: Fri, 10 Apr 2026 14:40:37 -0400 Subject: [PATCH 2/8] Implement #8809 and modify union_many to use that. Remove BooleanBuffer's bitand_many and associated tests. --- arrow-array/src/array/boolean_array.rs | 415 +++++++++++++++++++++++++ arrow-buffer/src/buffer/boolean.rs | 39 --- arrow-buffer/src/buffer/null.rs | 13 +- 3 files changed, 421 insertions(+), 46 deletions(-) diff --git a/arrow-array/src/array/boolean_array.rs b/arrow-array/src/array/boolean_array.rs index ee3413e1833d..a3c8d8da77a4 100644 --- a/arrow-array/src/array/boolean_array.rs +++ b/arrow-array/src/array/boolean_array.rs @@ -364,6 +364,256 @@ impl BooleanArray { Self::new(values, nulls) } + /// Apply a bitwise operation to this array's values using u64 operations, + /// returning a new [`BooleanArray`]. + /// + /// The null buffer is preserved unchanged. + /// + /// See [`BooleanBuffer::from_bitwise_unary_op`] for details on the operation. + /// + /// # Example + /// + /// ``` + /// # use arrow_array::BooleanArray; + /// let array = BooleanArray::from(vec![true, false, true]); + /// let result = array.bitwise_unary(|x| !x); + /// assert_eq!(result, BooleanArray::from(vec![false, true, false])); + /// ``` + pub fn bitwise_unary(&self, op: F) -> BooleanArray + where + F: FnMut(u64) -> u64, + { + let values = BooleanBuffer::from_bitwise_unary_op( + self.values.values(), + self.values.offset(), + self.values.len(), + op, + ); + BooleanArray::new(values, self.nulls.clone()) + } + + /// Try to apply a bitwise operation to this array's values in place using + /// u64 operations. + /// + /// If the underlying buffer is uniquely owned, the operation is applied + /// in place and `Ok` is returned. If the buffer is shared, `Err(self)` is + /// returned so the caller can fall back to [`bitwise_unary`](Self::bitwise_unary). + /// + /// The null buffer is preserved unchanged. + /// + /// # Example + /// + /// ``` + /// # use arrow_array::BooleanArray; + /// let array = BooleanArray::from(vec![true, false, true]); + /// let result = array.bitwise_unary_mut(|x| !x).unwrap(); + /// assert_eq!(result, BooleanArray::from(vec![false, true, false])); + /// ``` + pub fn bitwise_unary_mut(self, op: F) -> Result + where + F: FnMut(u64) -> u64, + { + let (values, nulls) = self.into_parts(); + let offset = values.offset(); + let len = values.len(); + let buffer = values.into_inner(); + match buffer.into_mutable() { + Ok(mut buf) => { + bit_util::apply_bitwise_unary_op(buf.as_slice_mut(), offset, len, op); + let values = BooleanBuffer::new(buf.into(), offset, len); + Ok(BooleanArray::new(values, nulls)) + } + Err(buffer) => { + let values = BooleanBuffer::new(buffer, offset, len); + Err(BooleanArray::new(values, nulls)) + } + } + } + + /// Apply a bitwise operation to this array's values in place if the buffer + /// is uniquely owned, or clone and apply if shared. + /// + /// This is a convenience wrapper around [`bitwise_unary_mut`](Self::bitwise_unary_mut) + /// that falls back to [`bitwise_unary`](Self::bitwise_unary) when the buffer is shared. + /// + /// The null buffer is preserved unchanged. + /// + /// # Example + /// + /// ``` + /// # use arrow_array::BooleanArray; + /// let array = BooleanArray::from(vec![true, false, true]); + /// let result = array.bitwise_unary_mut_or_clone(|x| !x); + /// assert_eq!(result, BooleanArray::from(vec![false, true, false])); + /// ``` + pub fn bitwise_unary_mut_or_clone(self, mut op: F) -> BooleanArray + where + F: FnMut(u64) -> u64, + { + let (values, nulls) = self.into_parts(); + let offset = values.offset(); + let len = values.len(); + let buffer = values.into_inner(); + match buffer.into_mutable() { + Ok(mut buf) => { + bit_util::apply_bitwise_unary_op(buf.as_slice_mut(), offset, len, &mut op); + let values = BooleanBuffer::new(buf.into(), offset, len); + BooleanArray::new(values, nulls) + } + Err(buffer) => { + let values = BooleanBuffer::new(buffer, offset, len); + let arr = BooleanArray::new(values, nulls); + arr.bitwise_unary(op) + } + } + } + + /// Apply a bitwise binary operation to this array and `rhs` using u64 + /// operations, returning a new [`BooleanArray`]. + /// + /// Null buffers are unioned: the result is null where either input is null. + /// + /// See [`BooleanBuffer::from_bitwise_binary_op`] for details on the operation. + /// + /// # Panics + /// + /// Panics if `self` and `rhs` have different lengths. + /// + /// # Example + /// + /// ``` + /// # use arrow_array::BooleanArray; + /// let a = BooleanArray::from(vec![true, false, true, true]); + /// let b = BooleanArray::from(vec![true, true, false, true]); + /// let result = a.bitwise_bin_op(&b, |a, b| a & b); + /// assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + /// ``` + pub fn bitwise_bin_op(&self, rhs: &BooleanArray, op: F) -> BooleanArray + where + F: FnMut(u64, u64) -> u64, + { + assert_eq!(self.len(), rhs.len()); + let nulls = NullBuffer::union(self.nulls(), rhs.nulls()); + let values = BooleanBuffer::from_bitwise_binary_op( + self.values.values(), + self.values.offset(), + rhs.values.values(), + rhs.values.offset(), + self.values.len(), + op, + ); + BooleanArray::new(values, nulls) + } + + /// Try to apply a bitwise binary operation to this array and `rhs` in + /// place using u64 operations. + /// + /// If this array's underlying buffer is uniquely owned, the operation is + /// applied in place and `Ok` is returned. If the buffer is shared, + /// `Err(self)` is returned so the caller can fall back to + /// [`bitwise_bin_op`](Self::bitwise_bin_op). + /// + /// Null buffers are unioned: the result is null where either input is null. + /// + /// # Panics + /// + /// Panics if `self` and `rhs` have different lengths. + /// + /// # Example + /// + /// ``` + /// # use arrow_array::BooleanArray; + /// let a = BooleanArray::from(vec![true, false, true, true]); + /// let b = BooleanArray::from(vec![true, true, false, true]); + /// let result = a.bitwise_bin_op_mut(&b, |a, b| a & b).unwrap(); + /// assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + /// ``` + pub fn bitwise_bin_op_mut( + self, + rhs: &BooleanArray, + op: F, + ) -> Result + where + F: FnMut(u64, u64) -> u64, + { + assert_eq!(self.len(), rhs.len()); + let nulls = NullBuffer::union(self.nulls(), rhs.nulls()); + let (values, _) = self.into_parts(); + let offset = values.offset(); + let len = values.len(); + let buffer = values.into_inner(); + match buffer.into_mutable() { + Ok(mut buf) => { + bit_util::apply_bitwise_binary_op( + buf.as_slice_mut(), + offset, + rhs.values.inner(), + rhs.values.offset(), + len, + op, + ); + let values = BooleanBuffer::new(buf.into(), offset, len); + Ok(BooleanArray::new(values, nulls)) + } + Err(buffer) => { + let values = BooleanBuffer::new(buffer, offset, len); + Err(BooleanArray::new(values, nulls)) + } + } + } + + /// Apply a bitwise binary operation to this array and `rhs` in place if the + /// buffer is uniquely owned, or clone and apply if shared. + /// + /// This is a convenience wrapper around [`bitwise_bin_op_mut`](Self::bitwise_bin_op_mut) + /// that falls back to [`bitwise_bin_op`](Self::bitwise_bin_op) when the buffer is shared. + /// + /// Null buffers are unioned: the result is null where either input is null. + /// + /// # Panics + /// + /// Panics if `self` and `rhs` have different lengths. + /// + /// # Example + /// + /// ``` + /// # use arrow_array::BooleanArray; + /// let a = BooleanArray::from(vec![true, false, true, true]); + /// let b = BooleanArray::from(vec![true, true, false, true]); + /// let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); + /// assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + /// ``` + pub fn bitwise_bin_op_mut_or_clone(self, rhs: &BooleanArray, mut op: F) -> BooleanArray + where + F: FnMut(u64, u64) -> u64, + { + assert_eq!(self.len(), rhs.len()); + let nulls = NullBuffer::union(self.nulls(), rhs.nulls()); + let (values, _) = self.into_parts(); + let offset = values.offset(); + let len = values.len(); + let buffer = values.into_inner(); + match buffer.into_mutable() { + Ok(mut buf) => { + bit_util::apply_bitwise_binary_op( + buf.as_slice_mut(), + offset, + rhs.values.inner(), + rhs.values.offset(), + len, + &mut op, + ); + let values = BooleanBuffer::new(buf.into(), offset, len); + BooleanArray::new(values, nulls) + } + Err(buffer) => { + let values = BooleanBuffer::new(buffer, offset, len); + let arr = BooleanArray::new(values, nulls); + arr.bitwise_bin_op(rhs, op) + } + } + } + /// Deconstruct this array into its constituent parts pub fn into_parts(self) -> (BooleanBuffer, Option) { (self.values, self.nulls) @@ -1062,4 +1312,169 @@ mod tests { assert_eq!(arr.has_false(), expected_has_false, "len={len}"); } } + + #[test] + fn test_bitwise_unary_not() { + let arr = BooleanArray::from(vec![true, false, true, false]); + let result = arr.bitwise_unary(|x| !x); + let expected = BooleanArray::from(vec![false, true, false, true]); + assert_eq!(result, expected); + } + + #[test] + fn test_bitwise_unary_preserves_nulls() { + let arr = BooleanArray::from(vec![Some(true), None, Some(false), Some(true)]); + let result = arr.bitwise_unary(|x| !x); + + assert_eq!(result.null_count(), 1); + assert!(result.is_null(1)); + assert_eq!(result.value(0), false); + assert_eq!(result.value(2), true); + assert_eq!(result.value(3), false); + } + + #[test] + fn test_bitwise_unary_mut_unshared() { + let arr = BooleanArray::from(vec![true, false, true, false]); + let result = arr.bitwise_unary_mut(|x| !x).unwrap(); + let expected = BooleanArray::from(vec![false, true, false, true]); + assert_eq!(result, expected); + } + + #[test] + fn test_bitwise_unary_mut_shared() { + let arr = BooleanArray::from(vec![true, false, true, false]); + let _shared = arr.clone(); + let result = arr.bitwise_unary_mut(|x| !x); + assert!(result.is_err()); + + let returned = result.unwrap_err(); + assert_eq!(returned, BooleanArray::from(vec![true, false, true, false])); + } + + #[test] + fn test_bitwise_unary_mut_with_nulls() { + let arr = BooleanArray::from(vec![Some(true), None, Some(false)]); + let result = arr.bitwise_unary_mut(|x| !x).unwrap(); + + assert_eq!(result.null_count(), 1); + assert!(result.is_null(1)); + assert_eq!(result.value(0), false); + assert_eq!(result.value(2), true); + } + + #[test] + fn test_bitwise_unary_mut_or_clone_unshared() { + let arr = BooleanArray::from(vec![true, false, true]); + let result = arr.bitwise_unary_mut_or_clone(|x| !x); + assert_eq!(result, BooleanArray::from(vec![false, true, false])); + } + + #[test] + fn test_bitwise_unary_mut_or_clone_shared() { + let arr = BooleanArray::from(vec![true, false, true]); + let _shared = arr.clone(); + let result = arr.bitwise_unary_mut_or_clone(|x| !x); + assert_eq!(result, BooleanArray::from(vec![false, true, false])); + } + + #[test] + fn test_bitwise_bin_op_and() { + let a = BooleanArray::from(vec![true, false, true, true]); + let b = BooleanArray::from(vec![true, true, false, true]); + let result = a.bitwise_bin_op(&b, |a, b| a & b); + assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + } + + #[test] + fn test_bitwise_bin_op_or() { + let a = BooleanArray::from(vec![true, false, true, false]); + let b = BooleanArray::from(vec![false, true, false, false]); + let result = a.bitwise_bin_op(&b, |a, b| a | b); + assert_eq!(result, BooleanArray::from(vec![true, true, true, false])); + } + + #[test] + fn test_bitwise_bin_op_null_union() { + let a = BooleanArray::from(vec![Some(true), None, Some(true), Some(false)]); + let b = BooleanArray::from(vec![Some(true), Some(true), None, Some(true)]); + let result = a.bitwise_bin_op(&b, |a, b| a & b); + + assert_eq!(result.null_count(), 2); + assert!(result.is_null(1)); + assert!(result.is_null(2)); + assert_eq!(result.value(0), true); + assert_eq!(result.value(3), false); + } + + #[test] + fn test_bitwise_bin_op_one_nullable() { + let a = BooleanArray::from(vec![Some(true), None, Some(true)]); + let b = BooleanArray::from(vec![false, true, true]); + let result = a.bitwise_bin_op(&b, |a, b| a & b); + + assert_eq!(result.null_count(), 1); + assert!(result.is_null(1)); + assert_eq!(result.value(0), false); + assert_eq!(result.value(2), true); + } + + #[test] + fn test_bitwise_bin_op_no_nulls() { + let a = BooleanArray::from(vec![true, false, true]); + let b = BooleanArray::from(vec![false, true, true]); + let result = a.bitwise_bin_op(&b, |a, b| a | b); + + assert!(result.nulls().is_none()); + assert_eq!(result, BooleanArray::from(vec![true, true, true])); + } + + #[test] + fn test_bitwise_bin_op_mut_unshared() { + let a = BooleanArray::from(vec![true, false, true, true]); + let b = BooleanArray::from(vec![true, true, false, true]); + let result = a.bitwise_bin_op_mut(&b, |a, b| a & b).unwrap(); + assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + } + + #[test] + fn test_bitwise_bin_op_mut_shared() { + let a = BooleanArray::from(vec![true, false, true, true]); + let _shared = a.clone(); + let result = a.bitwise_bin_op_mut( + &BooleanArray::from(vec![true, true, false, true]), + |a, b| a & b, + ); + assert!(result.is_err()); + } + + #[test] + fn test_bitwise_bin_op_mut_with_nulls() { + let a = BooleanArray::from(vec![Some(true), None, Some(true), Some(false)]); + let b = BooleanArray::from(vec![Some(true), Some(true), None, Some(true)]); + let result = a.bitwise_bin_op_mut(&b, |a, b| a & b).unwrap(); + + assert_eq!(result.null_count(), 2); + assert!(result.is_null(1)); + assert!(result.is_null(2)); + assert_eq!(result.value(0), true); + assert_eq!(result.value(3), false); + } + + #[test] + fn test_bitwise_bin_op_mut_or_clone_unshared() { + let a = BooleanArray::from(vec![true, false, true, true]); + let b = BooleanArray::from(vec![true, true, false, true]); + let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); + assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + } + + #[test] + fn test_bitwise_bin_op_mut_or_clone_shared() { + let a = BooleanArray::from(vec![true, false, true, true]); + let _shared = a.clone(); + let b = BooleanArray::from(vec![true, true, false, true]); + let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); + assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + } } diff --git a/arrow-buffer/src/buffer/boolean.rs b/arrow-buffer/src/buffer/boolean.rs index d15ca8fab27c..420bbf59f3be 100644 --- a/arrow-buffer/src/buffer/boolean.rs +++ b/arrow-buffer/src/buffer/boolean.rs @@ -625,20 +625,6 @@ impl BooleanBuffer { pub fn set_slices(&self) -> BitSliceIterator<'_> { BitSliceIterator::new(self.values(), self.bit_offset, self.bit_len) } - - /// Bitwise AND of multiple [`BooleanBuffer`]s - /// - /// # Panics - /// - /// Panics if `buffers` is empty, or if the buffers have different lengths - pub fn bitand_many(buffers: &[&BooleanBuffer]) -> BooleanBuffer { - assert!(!buffers.is_empty(), "requires at least one buffer"); - let mut result = buffers[0].clone(); - for buf in &buffers[1..] { - result &= *buf; - } - result - } } impl Not for &BooleanBuffer { @@ -1259,29 +1245,4 @@ mod tests { let buffer = BooleanBuffer::new_unset(100); assert_eq!(buffer.clone().find_nth_set_bit_position(0, 1), 100); } - - #[test] - fn test_bitand_many_single() { - let a = BooleanBuffer::from(&[true, false, true, true] as &[bool]); - let result = BooleanBuffer::bitand_many(&[&a]); - assert_eq!(result, a); - } - - #[test] - fn test_bitand_many_two() { - let a = BooleanBuffer::from(&[true, false, true, true] as &[bool]); - let b = BooleanBuffer::from(&[true, true, false, true] as &[bool]); - let result = BooleanBuffer::bitand_many(&[&a, &b]); - assert_eq!(result, &a & &b); - } - - #[test] - fn test_bitand_many_three() { - let a = BooleanBuffer::from(&[true, false, true, true] as &[bool]); - let b = BooleanBuffer::from(&[true, true, false, true] as &[bool]); - let c = BooleanBuffer::from(&[false, true, true, true] as &[bool]); - let result = BooleanBuffer::bitand_many(&[&a, &b, &c]); - let expected = BooleanBuffer::from(&[false, false, false, true] as &[bool]); - assert_eq!(result, expected); - } } diff --git a/arrow-buffer/src/buffer/null.rs b/arrow-buffer/src/buffer/null.rs index 8ee26ee2d490..21c0a0c4f21f 100644 --- a/arrow-buffer/src/buffer/null.rs +++ b/arrow-buffer/src/buffer/null.rs @@ -89,14 +89,13 @@ impl NullBuffer { /// See [`union`](Self::union) pub fn union_many(nulls: &[Option<&NullBuffer>]) -> Option { // Unwrap to BooleanBuffer because BitAndAssign is not implemented for NullBuffer - let buffers: Vec<&BooleanBuffer> = nulls - .iter() - .filter_map(|nb| nb.map(NullBuffer::inner)) - .collect(); - if buffers.is_empty() { - return None; + let mut buffers = nulls.iter().filter_map(|nb| nb.map(NullBuffer::inner)); + let first = buffers.next()?; + let mut result = first.clone(); + for buf in buffers { + result &= buf; } - Some(Self::new(BooleanBuffer::bitand_many(&buffers))) + Some(Self::new(result)) } /// Returns true if all nulls in `other` also exist in self From ad6674f2ea594518b2edc17f6c6d5bec5ffcf2ff Mon Sep 17 00:00:00 2001 From: Matt Butrovich Date: Fri, 10 Apr 2026 14:44:28 -0400 Subject: [PATCH 3/8] Refactor to reduce code duplication with a private fn try_bitwise_unary_in_place. --- arrow-array/src/array/boolean_array.rs | 91 +++++++++++--------------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/arrow-array/src/array/boolean_array.rs b/arrow-array/src/array/boolean_array.rs index a3c8d8da77a4..a6662a4a19ee 100644 --- a/arrow-array/src/array/boolean_array.rs +++ b/arrow-array/src/array/boolean_array.rs @@ -413,21 +413,8 @@ impl BooleanArray { where F: FnMut(u64) -> u64, { - let (values, nulls) = self.into_parts(); - let offset = values.offset(); - let len = values.len(); - let buffer = values.into_inner(); - match buffer.into_mutable() { - Ok(mut buf) => { - bit_util::apply_bitwise_unary_op(buf.as_slice_mut(), offset, len, op); - let values = BooleanBuffer::new(buf.into(), offset, len); - Ok(BooleanArray::new(values, nulls)) - } - Err(buffer) => { - let values = BooleanBuffer::new(buffer, offset, len); - Err(BooleanArray::new(values, nulls)) - } - } + self.try_bitwise_unary_in_place(op) + .map_err(|(array, _op)| array) } /// Apply a bitwise operation to this array's values in place if the buffer @@ -446,7 +433,18 @@ impl BooleanArray { /// let result = array.bitwise_unary_mut_or_clone(|x| !x); /// assert_eq!(result, BooleanArray::from(vec![false, true, false])); /// ``` - pub fn bitwise_unary_mut_or_clone(self, mut op: F) -> BooleanArray + pub fn bitwise_unary_mut_or_clone(self, op: F) -> BooleanArray + where + F: FnMut(u64) -> u64, + { + match self.try_bitwise_unary_in_place(op) { + Ok(array) => array, + Err((array, op)) => array.bitwise_unary(op), + } + } + + /// Try to apply a unary op in place, returning `op` back on failure. + fn try_bitwise_unary_in_place(self, op: F) -> Result where F: FnMut(u64) -> u64, { @@ -456,14 +454,13 @@ impl BooleanArray { let buffer = values.into_inner(); match buffer.into_mutable() { Ok(mut buf) => { - bit_util::apply_bitwise_unary_op(buf.as_slice_mut(), offset, len, &mut op); + bit_util::apply_bitwise_unary_op(buf.as_slice_mut(), offset, len, op); let values = BooleanBuffer::new(buf.into(), offset, len); - BooleanArray::new(values, nulls) + Ok(BooleanArray::new(values, nulls)) } Err(buffer) => { let values = BooleanBuffer::new(buffer, offset, len); - let arr = BooleanArray::new(values, nulls); - arr.bitwise_unary(op) + Err((BooleanArray::new(values, nulls), op)) } } } @@ -536,30 +533,8 @@ impl BooleanArray { where F: FnMut(u64, u64) -> u64, { - assert_eq!(self.len(), rhs.len()); - let nulls = NullBuffer::union(self.nulls(), rhs.nulls()); - let (values, _) = self.into_parts(); - let offset = values.offset(); - let len = values.len(); - let buffer = values.into_inner(); - match buffer.into_mutable() { - Ok(mut buf) => { - bit_util::apply_bitwise_binary_op( - buf.as_slice_mut(), - offset, - rhs.values.inner(), - rhs.values.offset(), - len, - op, - ); - let values = BooleanBuffer::new(buf.into(), offset, len); - Ok(BooleanArray::new(values, nulls)) - } - Err(buffer) => { - let values = BooleanBuffer::new(buffer, offset, len); - Err(BooleanArray::new(values, nulls)) - } - } + self.try_bitwise_bin_op_in_place(rhs, op) + .map_err(|(array, _op)| array) } /// Apply a bitwise binary operation to this array and `rhs` in place if the @@ -583,13 +558,27 @@ impl BooleanArray { /// let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); /// assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); /// ``` - pub fn bitwise_bin_op_mut_or_clone(self, rhs: &BooleanArray, mut op: F) -> BooleanArray + pub fn bitwise_bin_op_mut_or_clone(self, rhs: &BooleanArray, op: F) -> BooleanArray + where + F: FnMut(u64, u64) -> u64, + { + match self.try_bitwise_bin_op_in_place(rhs, op) { + Ok(array) => array, + Err((array, op)) => array.bitwise_bin_op(rhs, op), + } + } + + /// Try to apply a binary op in place, returning `op` back on failure. + fn try_bitwise_bin_op_in_place( + self, + rhs: &BooleanArray, + op: F, + ) -> Result where F: FnMut(u64, u64) -> u64, { assert_eq!(self.len(), rhs.len()); - let nulls = NullBuffer::union(self.nulls(), rhs.nulls()); - let (values, _) = self.into_parts(); + let (values, nulls) = self.into_parts(); let offset = values.offset(); let len = values.len(); let buffer = values.into_inner(); @@ -601,15 +590,15 @@ impl BooleanArray { rhs.values.inner(), rhs.values.offset(), len, - &mut op, + op, ); + let nulls = NullBuffer::union(nulls.as_ref(), rhs.nulls()); let values = BooleanBuffer::new(buf.into(), offset, len); - BooleanArray::new(values, nulls) + Ok(BooleanArray::new(values, nulls)) } Err(buffer) => { let values = BooleanBuffer::new(buffer, offset, len); - let arr = BooleanArray::new(values, nulls); - arr.bitwise_bin_op(rhs, op) + Err((BooleanArray::new(values, nulls), op)) } } } From 4c5b52d236224af4cf0fc7668edca4504310f7c0 Mon Sep 17 00:00:00 2001 From: Matt Butrovich Date: Fri, 10 Apr 2026 14:47:44 -0400 Subject: [PATCH 4/8] Add regression test for refactor. --- arrow-array/src/array/boolean_array.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/arrow-array/src/array/boolean_array.rs b/arrow-array/src/array/boolean_array.rs index a6662a4a19ee..faeb8b0cb96e 100644 --- a/arrow-array/src/array/boolean_array.rs +++ b/arrow-array/src/array/boolean_array.rs @@ -443,7 +443,8 @@ impl BooleanArray { } } - /// Try to apply a unary op in place, returning `op` back on failure. + /// Try to apply a unary op in place. Returns `op` back on failure so + /// callers can fall back to an allocating path without requiring `F: Clone`. fn try_bitwise_unary_in_place(self, op: F) -> Result where F: FnMut(u64) -> u64, @@ -568,7 +569,8 @@ impl BooleanArray { } } - /// Try to apply a binary op in place, returning `op` back on failure. + /// Try to apply a binary op in place. Returns `op` back on failure so + /// callers can fall back to an allocating path without requiring `F: Clone`. fn try_bitwise_bin_op_in_place( self, rhs: &BooleanArray, @@ -592,6 +594,9 @@ impl BooleanArray { len, op, ); + // Defer null union to the success path so the Err path returns + // self's original nulls, avoiding a redundant union in callers + // that fall back to bitwise_bin_op. let nulls = NullBuffer::union(nulls.as_ref(), rhs.nulls()); let values = BooleanBuffer::new(buf.into(), offset, len); Ok(BooleanArray::new(values, nulls)) @@ -1466,4 +1471,21 @@ mod tests { let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); } + + #[test] + fn test_bitwise_bin_op_mut_or_clone_shared_with_nulls() { + // When the buffer is shared, _mut_or_clone falls back to bitwise_bin_op. + // The null union must only be applied once, not double-applied. + let a = BooleanArray::from(vec![Some(true), None, Some(true), Some(false)]); + let _shared = a.clone(); + let b = BooleanArray::from(vec![Some(true), Some(true), None, Some(true)]); + + let expected = a.bitwise_bin_op(&b, |a, b| a & b); + let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); + + assert_eq!(result, expected); + assert_eq!(result.null_count(), 2); + assert!(result.is_null(1)); + assert!(result.is_null(2)); + } } From 87af20b15aa8bb042294905f123b5b635a04eda0 Mon Sep 17 00:00:00 2001 From: Matt Butrovich Date: Fri, 10 Apr 2026 14:53:42 -0400 Subject: [PATCH 5/8] Test cleanup. --- arrow-array/src/array/boolean_array.rs | 95 ++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/arrow-array/src/array/boolean_array.rs b/arrow-array/src/array/boolean_array.rs index faeb8b0cb96e..9c81468eed6e 100644 --- a/arrow-array/src/array/boolean_array.rs +++ b/arrow-array/src/array/boolean_array.rs @@ -1357,13 +1357,6 @@ mod tests { assert_eq!(result.value(2), true); } - #[test] - fn test_bitwise_unary_mut_or_clone_unshared() { - let arr = BooleanArray::from(vec![true, false, true]); - let result = arr.bitwise_unary_mut_or_clone(|x| !x); - assert_eq!(result, BooleanArray::from(vec![false, true, false])); - } - #[test] fn test_bitwise_unary_mut_or_clone_shared() { let arr = BooleanArray::from(vec![true, false, true]); @@ -1455,14 +1448,6 @@ mod tests { assert_eq!(result.value(3), false); } - #[test] - fn test_bitwise_bin_op_mut_or_clone_unshared() { - let a = BooleanArray::from(vec![true, false, true, true]); - let b = BooleanArray::from(vec![true, true, false, true]); - let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); - assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); - } - #[test] fn test_bitwise_bin_op_mut_or_clone_shared() { let a = BooleanArray::from(vec![true, false, true, true]); @@ -1488,4 +1473,84 @@ mod tests { assert!(result.is_null(1)); assert!(result.is_null(2)); } + + #[test] + fn test_bitwise_unary_empty() { + let arr = BooleanArray::from(Vec::::new()); + let result = arr.bitwise_unary(|x| !x); + assert_eq!(result.len(), 0); + } + + #[test] + fn test_bitwise_bin_op_empty() { + let a = BooleanArray::from(Vec::::new()); + let b = BooleanArray::from(Vec::::new()); + let result = a.bitwise_bin_op(&b, |a, b| a & b); + assert_eq!(result.len(), 0); + } + + #[test] + fn test_bitwise_unary_sliced() { + // Slicing creates a non-zero offset into the underlying buffer. + let arr = BooleanArray::from(vec![true, false, true, true, false]); + let sliced = arr.slice(1, 3); // [false, true, true] + + let result = sliced.bitwise_unary(|x| !x); + assert_eq!(result.len(), 3); + assert_eq!(result.value(0), true); + assert_eq!(result.value(1), false); + assert_eq!(result.value(2), false); + } + + #[test] + fn test_bitwise_unary_mut_sliced() { + // Slicing shares the buffer, so _mut must return Err. + let arr = BooleanArray::from(vec![true, false, true, true, false]); + let sliced = arr.slice(1, 3); + assert!(sliced.bitwise_unary_mut(|x| !x).is_err()); + } + + #[test] + fn test_bitwise_unary_mut_or_clone_sliced() { + // Slicing shares the buffer, so _mut_or_clone falls back to allocating. + let arr = BooleanArray::from(vec![true, false, true, true, false]); + let sliced = arr.slice(1, 3); // [false, true, true] + + let result = sliced.bitwise_unary_mut_or_clone(|x| !x); + assert_eq!(result.len(), 3); + assert_eq!(result.value(0), true); + assert_eq!(result.value(1), false); + assert_eq!(result.value(2), false); + } + + #[test] + fn test_bitwise_bin_op_different_offsets() { + // Left and right sliced to different offsets exercises misaligned + // bit handling in from_bitwise_binary_op. + let left_full = BooleanArray::from(vec![false, true, false, true, true]); + let right_full = BooleanArray::from(vec![true, true, true, false, true, false]); + + let left = left_full.slice(1, 3); // [true, false, true] + let right = right_full.slice(2, 3); // [true, false, true] + + let result = left.bitwise_bin_op(&right, |a, b| a & b); + assert_eq!(result.len(), 3); + assert_eq!(result.value(0), true); + assert_eq!(result.value(1), false); + assert_eq!(result.value(2), true); + } + + #[test] + fn test_bitwise_bin_op_mut_or_clone_different_offsets() { + // Both sliced (shared buffers), so falls back to allocating path. + let left_full = BooleanArray::from(vec![false, true, true, false, true]); + let right_full = BooleanArray::from(vec![true, true, false, false, true, false]); + + let left = left_full.slice(1, 3); // [true, true, false] + let right = right_full.slice(2, 3); // [false, false, true] + + let expected = left.bitwise_bin_op(&right, |a, b| a & b); + let result = left.bitwise_bin_op_mut_or_clone(&right, |a, b| a & b); + assert_eq!(result, expected); + } } From cd2d688c47613bd7c3a409a76406f121533f94b1 Mon Sep 17 00:00:00 2001 From: Matt Butrovich Date: Fri, 10 Apr 2026 15:00:50 -0400 Subject: [PATCH 6/8] Fix clippy. --- arrow-array/src/array/boolean_array.rs | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/arrow-array/src/array/boolean_array.rs b/arrow-array/src/array/boolean_array.rs index 9c81468eed6e..1ff627701ee0 100644 --- a/arrow-array/src/array/boolean_array.rs +++ b/arrow-array/src/array/boolean_array.rs @@ -1322,9 +1322,9 @@ mod tests { assert_eq!(result.null_count(), 1); assert!(result.is_null(1)); - assert_eq!(result.value(0), false); - assert_eq!(result.value(2), true); - assert_eq!(result.value(3), false); + assert!(!result.value(0)); + assert!(result.value(2)); + assert!(!result.value(3)); } #[test] @@ -1353,8 +1353,8 @@ mod tests { assert_eq!(result.null_count(), 1); assert!(result.is_null(1)); - assert_eq!(result.value(0), false); - assert_eq!(result.value(2), true); + assert!(!result.value(0)); + assert!(result.value(2)); } #[test] @@ -1390,8 +1390,8 @@ mod tests { assert_eq!(result.null_count(), 2); assert!(result.is_null(1)); assert!(result.is_null(2)); - assert_eq!(result.value(0), true); - assert_eq!(result.value(3), false); + assert!(result.value(0)); + assert!(!result.value(3)); } #[test] @@ -1402,8 +1402,8 @@ mod tests { assert_eq!(result.null_count(), 1); assert!(result.is_null(1)); - assert_eq!(result.value(0), false); - assert_eq!(result.value(2), true); + assert!(!result.value(0)); + assert!(result.value(2)); } #[test] @@ -1444,8 +1444,8 @@ mod tests { assert_eq!(result.null_count(), 2); assert!(result.is_null(1)); assert!(result.is_null(2)); - assert_eq!(result.value(0), true); - assert_eq!(result.value(3), false); + assert!(result.value(0)); + assert!(!result.value(3)); } #[test] @@ -1497,9 +1497,9 @@ mod tests { let result = sliced.bitwise_unary(|x| !x); assert_eq!(result.len(), 3); - assert_eq!(result.value(0), true); - assert_eq!(result.value(1), false); - assert_eq!(result.value(2), false); + assert!(result.value(0)); + assert!(!result.value(1)); + assert!(!result.value(2)); } #[test] @@ -1518,9 +1518,9 @@ mod tests { let result = sliced.bitwise_unary_mut_or_clone(|x| !x); assert_eq!(result.len(), 3); - assert_eq!(result.value(0), true); - assert_eq!(result.value(1), false); - assert_eq!(result.value(2), false); + assert!(result.value(0)); + assert!(!result.value(1)); + assert!(!result.value(2)); } #[test] @@ -1535,9 +1535,9 @@ mod tests { let result = left.bitwise_bin_op(&right, |a, b| a & b); assert_eq!(result.len(), 3); - assert_eq!(result.value(0), true); - assert_eq!(result.value(1), false); - assert_eq!(result.value(2), true); + assert!(result.value(0)); + assert!(!result.value(1)); + assert!(result.value(2)); } #[test] From 304c8f521bb4a88c4ccd2cdc9ea6b0d083e2c53e Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Mon, 13 Apr 2026 15:21:39 -0400 Subject: [PATCH 7/8] test coverage and ensure buffer resuse --- arrow-array/src/array/boolean_array.rs | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/arrow-array/src/array/boolean_array.rs b/arrow-array/src/array/boolean_array.rs index 1ff627701ee0..22a1ba7653ac 100644 --- a/arrow-array/src/array/boolean_array.rs +++ b/arrow-array/src/array/boolean_array.rs @@ -887,6 +887,41 @@ impl From for BooleanArray { #[cfg(test)] mod tests { use super::*; + + // Captures the values-buffer identity for a BooleanArray so tests can assert + // whether an operation reused the original allocation or produced a new one. + struct PointerInfo { + ptr: *const u8, + offset: usize, + len: usize, + } + + impl PointerInfo { + // Record the current values buffer pointer plus bit offset/length. The + // offset/length checks ensure a logically equivalent slice wasn't rebuilt + // with a different view over the same allocation. + fn new(array: &BooleanArray) -> Self { + Self { + ptr: array.values().inner().as_ptr(), + offset: array.values().offset(), + len: array.values().len(), + } + } + + // Assert that the array still points at the exact same values buffer and + // preserves the same bit view. + fn assert_same(&self, array: &BooleanArray) { + assert_eq!(array.values().inner().as_ptr(), self.ptr); + assert_eq!(array.values().offset(), self.offset); + assert_eq!(array.values().len(), self.len); + } + + // Assert that the array now points at a different values allocation, + // indicating the operation fell back to an allocating path. + fn assert_different(&self, array: &BooleanArray) { + assert_ne!(array.values().inner().as_ptr(), self.ptr); + } + } use arrow_buffer::Buffer; use rand::{Rng, rng}; @@ -1330,20 +1365,24 @@ mod tests { #[test] fn test_bitwise_unary_mut_unshared() { let arr = BooleanArray::from(vec![true, false, true, false]); + let info = PointerInfo::new(&arr); let result = arr.bitwise_unary_mut(|x| !x).unwrap(); let expected = BooleanArray::from(vec![false, true, false, true]); assert_eq!(result, expected); + info.assert_same(&result); } #[test] fn test_bitwise_unary_mut_shared() { let arr = BooleanArray::from(vec![true, false, true, false]); + let info = PointerInfo::new(&arr); let _shared = arr.clone(); let result = arr.bitwise_unary_mut(|x| !x); assert!(result.is_err()); let returned = result.unwrap_err(); assert_eq!(returned, BooleanArray::from(vec![true, false, true, false])); + info.assert_same(&returned); } #[test] @@ -1360,9 +1399,21 @@ mod tests { #[test] fn test_bitwise_unary_mut_or_clone_shared() { let arr = BooleanArray::from(vec![true, false, true]); + let info = PointerInfo::new(&arr); let _shared = arr.clone(); let result = arr.bitwise_unary_mut_or_clone(|x| !x); assert_eq!(result, BooleanArray::from(vec![false, true, false])); + info.assert_different(&result); + } + + #[test] + fn test_bitwise_unary_mut_or_clone_unshared() { + // Covers the uniquely-owned fast path in bitwise_unary_mut_or_clone. + let arr = BooleanArray::from(vec![true, false, true]); + let info = PointerInfo::new(&arr); + let result = arr.bitwise_unary_mut_or_clone(|x| !x); + assert_eq!(result, BooleanArray::from(vec![false, true, false])); + info.assert_same(&result); } #[test] @@ -1419,20 +1470,25 @@ mod tests { #[test] fn test_bitwise_bin_op_mut_unshared() { let a = BooleanArray::from(vec![true, false, true, true]); + let info = PointerInfo::new(&a); let b = BooleanArray::from(vec![true, true, false, true]); let result = a.bitwise_bin_op_mut(&b, |a, b| a & b).unwrap(); assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + info.assert_same(&result); } #[test] fn test_bitwise_bin_op_mut_shared() { let a = BooleanArray::from(vec![true, false, true, true]); + let info = PointerInfo::new(&a); let _shared = a.clone(); let result = a.bitwise_bin_op_mut( &BooleanArray::from(vec![true, true, false, true]), |a, b| a & b, ); assert!(result.is_err()); + let returned = result.unwrap_err(); + info.assert_same(&returned); } #[test] @@ -1451,10 +1507,12 @@ mod tests { #[test] fn test_bitwise_bin_op_mut_or_clone_shared() { let a = BooleanArray::from(vec![true, false, true, true]); + let info = PointerInfo::new(&a); let _shared = a.clone(); let b = BooleanArray::from(vec![true, true, false, true]); let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); assert_eq!(result, BooleanArray::from(vec![true, false, false, true])); + info.assert_different(&result); } #[test] @@ -1462,6 +1520,7 @@ mod tests { // When the buffer is shared, _mut_or_clone falls back to bitwise_bin_op. // The null union must only be applied once, not double-applied. let a = BooleanArray::from(vec![Some(true), None, Some(true), Some(false)]); + let info = PointerInfo::new(&a); let _shared = a.clone(); let b = BooleanArray::from(vec![Some(true), Some(true), None, Some(true)]); @@ -1472,6 +1531,24 @@ mod tests { assert_eq!(result.null_count(), 2); assert!(result.is_null(1)); assert!(result.is_null(2)); + info.assert_different(&result); + } + + #[test] + fn test_bitwise_bin_op_mut_or_clone_unshared_with_nulls() { + // Covers the uniquely-owned fast path in bitwise_bin_op_mut_or_clone, + // including null union on the in-place path. + let a = BooleanArray::from(vec![Some(true), None, Some(true), Some(false)]); + let info = PointerInfo::new(&a); + let b = BooleanArray::from(vec![Some(true), Some(true), None, Some(true)]); + let result = a.bitwise_bin_op_mut_or_clone(&b, |a, b| a & b); + + assert_eq!(result.null_count(), 2); + assert!(result.is_null(1)); + assert!(result.is_null(2)); + assert!(result.value(0)); + assert!(!result.value(3)); + info.assert_same(&result); } #[test] From 2b3596909c9772e409d7e19aeecf5adc1f8ed2d3 Mon Sep 17 00:00:00 2001 From: Matt Butrovich Date: Mon, 13 Apr 2026 16:49:36 -0400 Subject: [PATCH 8/8] Address PR feedback. --- arrow-buffer/src/buffer/null.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/arrow-buffer/src/buffer/null.rs b/arrow-buffer/src/buffer/null.rs index 21c0a0c4f21f..729beaa0612f 100644 --- a/arrow-buffer/src/buffer/null.rs +++ b/arrow-buffer/src/buffer/null.rs @@ -87,9 +87,11 @@ impl NullBuffer { /// Computes the union of the nulls in multiple optional [`NullBuffer`]s /// /// See [`union`](Self::union) - pub fn union_many(nulls: &[Option<&NullBuffer>]) -> Option { + pub fn union_many<'a>( + nulls: impl IntoIterator>, + ) -> Option { // Unwrap to BooleanBuffer because BitAndAssign is not implemented for NullBuffer - let mut buffers = nulls.iter().filter_map(|nb| nb.map(NullBuffer::inner)); + let mut buffers = nulls.into_iter().filter_map(|nb| nb.map(NullBuffer::inner)); let first = buffers.next()?; let mut result = first.clone(); for buf in buffers { @@ -353,14 +355,14 @@ mod tests { #[test] fn test_union_many_all_none() { - let result = NullBuffer::union_many(&[None, None, None]); + let result = NullBuffer::union_many([None, None, None]); assert!(result.is_none()); } #[test] fn test_union_many_single_some() { let a = NullBuffer::from(&[true, false, true, true]); - let result = NullBuffer::union_many(&[Some(&a)]); + let result = NullBuffer::union_many([Some(&a)]); assert_eq!(result, Some(a)); } @@ -368,7 +370,7 @@ mod tests { fn test_union_many_two_inputs() { let a = NullBuffer::from(&[true, false, true, true]); let b = NullBuffer::from(&[true, true, false, true]); - let result = NullBuffer::union_many(&[Some(&a), Some(&b)]); + let result = NullBuffer::union_many([Some(&a), Some(&b)]); let expected = NullBuffer::union(Some(&a), Some(&b)); assert_eq!(result, expected); } @@ -378,7 +380,7 @@ mod tests { let a = NullBuffer::from(&[true, false, true, true]); let b = NullBuffer::from(&[true, true, false, true]); let c = NullBuffer::from(&[false, true, true, true]); - let result = NullBuffer::union_many(&[Some(&a), Some(&b), Some(&c)]); + let result = NullBuffer::union_many([Some(&a), Some(&b), Some(&c)]); let expected = NullBuffer::from(&[false, false, false, true]); assert_eq!(result, Some(expected)); } @@ -387,14 +389,14 @@ mod tests { fn test_union_many_mixed_none() { let a = NullBuffer::from(&[true, false, true, true]); let b = NullBuffer::from(&[false, true, true, true]); - let result = NullBuffer::union_many(&[Some(&a), None, Some(&b)]); + let result = NullBuffer::union_many([Some(&a), None, Some(&b)]); let expected = NullBuffer::union(Some(&a), Some(&b)); assert_eq!(result, expected); } #[test] fn test_union_many_empty_slice() { - let result = NullBuffer::union_many(&[]); + let result = NullBuffer::union_many([] as [Option<&NullBuffer>; 0]); assert!(result.is_none()); } }