diff --git a/arrow-buffer/src/buffer/immutable.rs b/arrow-buffer/src/buffer/immutable.rs index 20eb966a8f08..5e1ad637b6f7 100644 --- a/arrow-buffer/src/buffer/immutable.rs +++ b/arrow-buffer/src/buffer/immutable.rs @@ -460,6 +460,12 @@ impl From<&[u8]> for Buffer { } } +impl AsRef<[u8]> for &Buffer { + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + impl From<[u8; N]> for Buffer { fn from(p: [u8; N]) -> Self { Self::from_slice_ref(p) diff --git a/arrow-buffer/src/buffer/mod.rs b/arrow-buffer/src/buffer/mod.rs index 676d64152e47..11275df77e4d 100644 --- a/arrow-buffer/src/buffer/mod.rs +++ b/arrow-buffer/src/buffer/mod.rs @@ -26,7 +26,6 @@ pub use mutable::*; mod ops; pub use ops::*; mod mutable_ops; -pub use mutable_ops::*; mod scalar; pub use scalar::*; mod boolean; diff --git a/arrow-buffer/src/buffer/mutable.rs b/arrow-buffer/src/buffer/mutable.rs index 93d9d6b9ad84..1f60583ad5ea 100644 --- a/arrow-buffer/src/buffer/mutable.rs +++ b/arrow-buffer/src/buffer/mutable.rs @@ -723,6 +723,12 @@ impl MutableBuffer { } } +impl AsRef<[u8]> for &MutableBuffer { + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + impl Default for MutableBuffer { fn default() -> Self { Self::with_capacity(0) diff --git a/arrow-buffer/src/buffer/mutable_ops.rs b/arrow-buffer/src/buffer/mutable_ops.rs index 0784672bedaf..d64f7e643351 100644 --- a/arrow-buffer/src/buffer/mutable_ops.rs +++ b/arrow-buffer/src/buffer/mutable_ops.rs @@ -15,196 +15,126 @@ // specific language governing permissions and limitations // under the License. -use super::{Buffer, MutableBuffer}; -use crate::BooleanBufferBuilder; +use super::MutableBuffer; use crate::bit_chunk_iterator::BitChunks; use crate::util::bit_util; -/// What can be used as the right-hand side (RHS) buffer in mutable operations. -/// -/// this is not mutated. -/// -/// # Implementation notes -/// -/// ## Why `pub(crate)`? -/// This is because we don't want this trait to expose the inner buffer to the public. -/// this is the trait implementor choice. -/// -pub(crate) trait BufferSupportedRhs { - fn as_slice(&self) -> &[u8]; -} - -impl BufferSupportedRhs for Buffer { - fn as_slice(&self) -> &[u8] { - self.as_slice() - } -} - -impl BufferSupportedRhs for MutableBuffer { - fn as_slice(&self) -> &[u8] { - self.as_slice() - } -} - -impl BufferSupportedRhs for BooleanBufferBuilder { - fn as_slice(&self) -> &[u8] { - self.as_slice() - } -} - -/// Trait that will be operated on as the left-hand side (LHS) buffer in mutable operations. -/// -/// This consumer of the trait must satisfies the following guarantees: -/// 1. It will not change the length of the buffer. -/// -/// # Implementation notes -/// -/// ## Why is this trait `pub(crate)`? -/// Because we don't wanna expose the inner mutable buffer to the public. -/// as this is the choice of the implementor of the trait and sometimes it is not desirable -/// (e.g. `BooleanBufferBuilder`). -/// -/// ## Why this trait is needed, can't we just use `MutableBuffer` directly? -/// Sometimes we don't want to expose the inner `MutableBuffer` -/// so it can't be misused. -/// -/// For example, [`BooleanBufferBuilder`] does not expose the inner `MutableBuffer` -/// as exposing it will allow the user to change the length of the buffer that will make the -/// `BooleanBufferBuilder` invalid. -/// -pub(crate) trait MutableOpsBufferSupportedLhs { - /// Get a mutable reference to the inner `MutableBuffer`. +impl MutableBuffer { + /// Apply a binary bitwise operation to two bit-packed buffers. /// - /// This is used to perform in-place operations on the buffer. + /// This is the main entry point for binary operations. It handles both byte-aligned + /// and non-byte-aligned cases by delegating to specialized helper functions. /// - /// the caller must ensure that the length of the buffer is not changed. - fn inner_mutable_buffer(&mut self) -> &mut MutableBuffer; -} - -impl MutableOpsBufferSupportedLhs for MutableBuffer { - fn inner_mutable_buffer(&mut self) -> &mut MutableBuffer { - self - } -} - -/// Apply a binary bitwise operation to two bit-packed buffers. -/// -/// This is the main entry point for binary operations. It handles both byte-aligned -/// and non-byte-aligned cases by delegating to specialized helper functions. -/// -/// # Arguments -/// -/// * `left` - The left mutable buffer to be modified in-place -/// * `left_offset_in_bits` - Starting bit offset in the left buffer -/// * `right` - The right buffer (as byte slice) -/// * `right_offset_in_bits` - Starting bit offset in the right buffer -/// * `len_in_bits` - Number of bits to process -/// * `op` - Binary operation to apply (e.g., `|a, b| a & b`) -/// -#[allow( - private_bounds, - reason = "MutableOpsBufferSupportedLhs and BufferSupportedRhs exposes the inner internals which is the implementor choice and we dont want to leak internals" -)] -pub fn mutable_bitwise_bin_op_helper( - left: &mut impl MutableOpsBufferSupportedLhs, - left_offset_in_bits: usize, - right: &impl BufferSupportedRhs, - right_offset_in_bits: usize, - len_in_bits: usize, - mut op: F, -) where - F: FnMut(u64, u64) -> u64, -{ - if len_in_bits == 0 { - return; - } - - let mutable_buffer = left.inner_mutable_buffer(); + /// # Arguments + /// + /// * `left` - The left mutable buffer to be modified in-place + /// * `left_offset_in_bits` - Starting bit offset in the left buffer + /// * `right` - The right buffer (as byte slice) + /// * `right_offset_in_bits` - Starting bit offset in the right buffer + /// * `len_in_bits` - Number of bits to process + /// * `op` - Binary operation to apply (e.g., `|a, b| a & b`) + /// + pub fn mutable_bitwise_bin_op_helper( + &mut self, + left_offset_in_bits: usize, + right: impl AsRef<[u8]>, + right_offset_in_bits: usize, + len_in_bits: usize, + mut op: F, + ) where + F: FnMut(u64, u64) -> u64, + { + if len_in_bits == 0 { + return; + } - let mutable_buffer_len = mutable_buffer.len(); - let mutable_buffer_cap = mutable_buffer.capacity(); + let mutable_buffer = self; - // offset inside a byte - let left_bit_offset = left_offset_in_bits % 8; + let mutable_buffer_len = mutable_buffer.len(); + let mutable_buffer_cap = mutable_buffer.capacity(); - let is_mutable_buffer_byte_aligned = left_bit_offset == 0; + // offset inside a byte + let left_bit_offset = left_offset_in_bits % 8; - if is_mutable_buffer_byte_aligned { - mutable_buffer_byte_aligned_bitwise_bin_op_helper( - mutable_buffer, - left_offset_in_bits, - right, - right_offset_in_bits, - len_in_bits, - op, - ); - } else { - // If we are not byte aligned, run `op` on the first few bits to reach byte alignment - let bits_to_next_byte = 8 - left_bit_offset; - - { - let right_byte_offset = right_offset_in_bits / 8; - - // Read the same amount of bits from the right buffer - let right_first_byte: u8 = read_up_to_byte_from_offset( - &right.as_slice()[right_byte_offset..], - bits_to_next_byte, - // Right bit offset - right_offset_in_bits % 8, - ); + let is_mutable_buffer_byte_aligned = left_bit_offset == 0; - align_to_byte( - // Hope it gets inlined - &mut |left| op(left, right_first_byte as u64), + if is_mutable_buffer_byte_aligned { + mutable_buffer_byte_aligned_bitwise_bin_op_helper( mutable_buffer, left_offset_in_bits, + right, + right_offset_in_bits, + len_in_bits, + op, ); - } + } else { + // If we are not byte aligned, run `op` on the first few bits to reach byte alignment + let bits_to_next_byte = 8 - left_bit_offset; + + { + let right_byte_offset = right_offset_in_bits / 8; + + // Read the same amount of bits from the right buffer + let right_first_byte: u8 = read_up_to_byte_from_offset( + &right.as_ref()[right_byte_offset..], + bits_to_next_byte, + // Right bit offset + right_offset_in_bits % 8, + ); + + align_to_byte( + // Hope it gets inlined + &mut |left| op(left, right_first_byte as u64), + mutable_buffer, + left_offset_in_bits, + ); + } - let left_offset_in_bits = left_offset_in_bits + bits_to_next_byte; - let right_offset_in_bits = right_offset_in_bits + bits_to_next_byte; - let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte); + let left_offset_in_bits = left_offset_in_bits + bits_to_next_byte; + let right_offset_in_bits = right_offset_in_bits + bits_to_next_byte; + let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte); + + if len_in_bits == 0 { + // Making sure that our guarantee that the length and capacity of the mutable buffer + // will not change is upheld + assert_eq!( + mutable_buffer.len(), + mutable_buffer_len, + "The length of the mutable buffer must not change" + ); + assert_eq!( + mutable_buffer.capacity(), + mutable_buffer_cap, + "The capacity of the mutable buffer must not change" + ); + + return; + } - if len_in_bits == 0 { - // Making sure that our guarantee that the length and capacity of the mutable buffer - // will not change is upheld - assert_eq!( - mutable_buffer.len(), - mutable_buffer_len, - "The length of the mutable buffer must not change" - ); - assert_eq!( - mutable_buffer.capacity(), - mutable_buffer_cap, - "The capacity of the mutable buffer must not change" + // We are now byte aligned + mutable_buffer_byte_aligned_bitwise_bin_op_helper( + mutable_buffer, + left_offset_in_bits, + right, + right_offset_in_bits, + len_in_bits, + op, ); - - return; } - // We are now byte aligned - mutable_buffer_byte_aligned_bitwise_bin_op_helper( - mutable_buffer, - left_offset_in_bits, - right, - right_offset_in_bits, - len_in_bits, - op, + // Making sure that our guarantee that the length and capacity of the mutable buffer + // will not change is upheld + assert_eq!( + mutable_buffer.len(), + mutable_buffer_len, + "The length of the mutable buffer must not change" + ); + assert_eq!( + mutable_buffer.capacity(), + mutable_buffer_cap, + "The capacity of the mutable buffer must not change" ); } - - // Making sure that our guarantee that the length and capacity of the mutable buffer - // will not change is upheld - assert_eq!( - mutable_buffer.len(), - mutable_buffer_len, - "The length of the mutable buffer must not change" - ); - assert_eq!( - mutable_buffer.capacity(), - mutable_buffer_cap, - "The capacity of the mutable buffer must not change" - ); } /// Align to byte boundary by applying operation to bits before the next byte boundary. @@ -307,7 +237,7 @@ fn read_up_to_byte_from_offset( fn mutable_buffer_byte_aligned_bitwise_bin_op_helper( left: &mut MutableBuffer, left_offset_in_bits: usize, - right: &impl BufferSupportedRhs, + right: impl AsRef<[u8]>, right_offset_in_bits: usize, len_in_bits: usize, mut op: F, @@ -325,7 +255,7 @@ fn mutable_buffer_byte_aligned_bitwise_bin_op_helper( let (complete_u64_chunks, remainder_bytes) = U64UnalignedSlice::split(left, left_offset_in_bits, len_in_bits); - let right_chunks = BitChunks::new(right.as_slice(), right_offset_in_bits, len_in_bits); + let right_chunks = BitChunks::new(right.as_ref(), right_offset_in_bits, len_in_bits); assert_eq!( bit_util::ceil(right_chunks.remainder_len(), 8), remainder_bytes.len() @@ -715,225 +645,98 @@ fn handle_mutable_buffer_remainder_unary( set_remainder_bits(start_remainder_mut, rem, remainder_len); } -/// Apply a bitwise operation to a mutable buffer and update it in-place. -/// -/// This is the main entry point for unary operations. It handles both byte-aligned -/// and non-byte-aligned cases. -/// -/// The input is treated as a bitmap, meaning that offset and length are specified -/// in number of bits. -/// -/// # Arguments -/// -/// * `buffer` - The mutable buffer to modify -/// * `offset_in_bits` - Starting bit offset -/// * `len_in_bits` - Number of bits to process -/// * `op` - Unary operation to apply (e.g., `|a| !a`) -#[allow( - private_bounds, - reason = "MutableOpsBufferSupportedLhs exposes the inner internals which is the implementor choice and we dont want to leak internals" -)] -pub fn mutable_bitwise_unary_op_helper( - buffer: &mut impl MutableOpsBufferSupportedLhs, - offset_in_bits: usize, - len_in_bits: usize, - mut op: F, -) where - F: FnMut(u64) -> u64, -{ - if len_in_bits == 0 { - return; - } - - let mutable_buffer = buffer.inner_mutable_buffer(); - - let mutable_buffer_len = mutable_buffer.len(); - let mutable_buffer_cap = mutable_buffer.capacity(); - - // offset inside a byte - let left_bit_offset = offset_in_bits % 8; +impl MutableBuffer { + /// Apply a bitwise operation to a mutable buffer and update it in-place. + /// + /// This is the main entry point for unary operations. It handles both byte-aligned + /// and non-byte-aligned cases. + /// + /// The input is treated as a bitmap, meaning that offset and length are specified + /// in number of bits. + /// + /// # Arguments + /// + /// * `buffer` - The mutable buffer to modify + /// * `offset_in_bits` - Starting bit offset + /// * `len_in_bits` - Number of bits to process + /// * `op` - Unary operation to apply (e.g., `|a| !a`) + pub fn mutable_bitwise_unary_op_helper( + &mut self, + offset_in_bits: usize, + len_in_bits: usize, + mut op: F, + ) where + F: FnMut(u64) -> u64, + { + if len_in_bits == 0 { + return; + } - let is_mutable_buffer_byte_aligned = left_bit_offset == 0; + let mutable_buffer = self; - if is_mutable_buffer_byte_aligned { - mutable_byte_aligned_bitwise_unary_op_helper( - mutable_buffer, - offset_in_bits, - len_in_bits, - op, - ); - } else { - // If we are not byte aligned we will read the first few bits - let bits_to_next_byte = 8 - left_bit_offset; + let mutable_buffer_len = mutable_buffer.len(); + let mutable_buffer_cap = mutable_buffer.capacity(); - align_to_byte(&mut op, mutable_buffer, offset_in_bits); + // offset inside a byte + let left_bit_offset = offset_in_bits % 8; - let offset_in_bits = offset_in_bits + bits_to_next_byte; - let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte); + let is_mutable_buffer_byte_aligned = left_bit_offset == 0; - if len_in_bits == 0 { - // Making sure that our guarantee that the length and capacity of the mutable buffer - // will not change is upheld - assert_eq!( - mutable_buffer.len(), - mutable_buffer_len, - "The length of the mutable buffer must not change" - ); - assert_eq!( - mutable_buffer.capacity(), - mutable_buffer_cap, - "The capacity of the mutable buffer must not change" + if is_mutable_buffer_byte_aligned { + mutable_byte_aligned_bitwise_unary_op_helper( + mutable_buffer, + offset_in_bits, + len_in_bits, + op, ); + } else { + // If we are not byte aligned we will read the first few bits + let bits_to_next_byte = 8 - left_bit_offset; + + align_to_byte(&mut op, mutable_buffer, offset_in_bits); + + let offset_in_bits = offset_in_bits + bits_to_next_byte; + let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte); + + if len_in_bits == 0 { + // Making sure that our guarantee that the length and capacity of the mutable buffer + // will not change is upheld + assert_eq!( + mutable_buffer.len(), + mutable_buffer_len, + "The length of the mutable buffer must not change" + ); + assert_eq!( + mutable_buffer.capacity(), + mutable_buffer_cap, + "The capacity of the mutable buffer must not change" + ); + + return; + } - return; + // We are now byte aligned + mutable_byte_aligned_bitwise_unary_op_helper( + mutable_buffer, + offset_in_bits, + len_in_bits, + op, + ); } - // We are now byte aligned - mutable_byte_aligned_bitwise_unary_op_helper( - mutable_buffer, - offset_in_bits, - len_in_bits, - op, + // Making sure that our guarantee that the length and capacity of the mutable buffer + // will not change is upheld + assert_eq!( + mutable_buffer.len(), + mutable_buffer_len, + "The length of the mutable buffer must not change" + ); + assert_eq!( + mutable_buffer.capacity(), + mutable_buffer_cap, + "The capacity of the mutable buffer must not change" ); } - - // Making sure that our guarantee that the length and capacity of the mutable buffer - // will not change is upheld - assert_eq!( - mutable_buffer.len(), - mutable_buffer_len, - "The length of the mutable buffer must not change" - ); - assert_eq!( - mutable_buffer.capacity(), - mutable_buffer_cap, - "The capacity of the mutable buffer must not change" - ); -} - -/// Apply a bitwise AND operation to two buffers. -/// -/// The left buffer (mutable) is modified in-place to contain the result. -/// The inputs are treated as bitmaps, meaning that offsets and length are -/// specified in number of bits. -/// -/// # Arguments -/// -/// * `left` - The left mutable buffer (will be modified) -/// * `left_offset_in_bits` - Starting bit offset in the left buffer -/// * `right` - The right buffer -/// * `right_offset_in_bits` - Starting bit offset in the right buffer -/// * `len_in_bits` - Number of bits to process -#[allow( - private_bounds, - reason = "MutableOpsBufferSupportedLhs and BufferSupportedRhs exposes the inner internals which is the implementor choice and we dont want to leak internals" -)] -pub fn mutable_buffer_bin_and( - left: &mut impl MutableOpsBufferSupportedLhs, - left_offset_in_bits: usize, - right: &impl BufferSupportedRhs, - right_offset_in_bits: usize, - len_in_bits: usize, -) { - mutable_bitwise_bin_op_helper( - left, - left_offset_in_bits, - right, - right_offset_in_bits, - len_in_bits, - |a, b| a & b, - ) -} - -/// Apply a bitwise OR operation to two buffers. -/// -/// The left buffer (mutable) is modified in-place to contain the result. -/// The inputs are treated as bitmaps, meaning that offsets and length are -/// specified in number of bits. -/// -/// # Arguments -/// -/// * `left` - The left mutable buffer (will be modified) -/// * `left_offset_in_bits` - Starting bit offset in the left buffer -/// * `right` - The right buffer -/// * `right_offset_in_bits` - Starting bit offset in the right buffer -/// * `len_in_bits` - Number of bits to process -#[allow( - private_bounds, - reason = "MutableOpsBufferSupportedLhs and BufferSupportedRhs exposes the inner internals which is the implementor choice and we dont want to leak internals" -)] -pub fn mutable_buffer_bin_or( - left: &mut impl MutableOpsBufferSupportedLhs, - left_offset_in_bits: usize, - right: &impl BufferSupportedRhs, - right_offset_in_bits: usize, - len_in_bits: usize, -) { - mutable_bitwise_bin_op_helper( - left, - left_offset_in_bits, - right, - right_offset_in_bits, - len_in_bits, - |a, b| a | b, - ) -} - -/// Apply a bitwise XOR operation to two buffers. -/// -/// The left buffer (mutable) is modified in-place to contain the result. -/// The inputs are treated as bitmaps, meaning that offsets and length are -/// specified in number of bits. -/// -/// # Arguments -/// -/// * `left` - The left mutable buffer (will be modified) -/// * `left_offset_in_bits` - Starting bit offset in the left buffer -/// * `right` - The right buffer -/// * `right_offset_in_bits` - Starting bit offset in the right buffer -/// * `len_in_bits` - Number of bits to process -#[allow( - private_bounds, - reason = "MutableOpsBufferSupportedLhs and BufferSupportedRhs exposes the inner internals which is the implementor choice and we dont want to leak internals" -)] -pub fn mutable_buffer_bin_xor( - left: &mut impl MutableOpsBufferSupportedLhs, - left_offset_in_bits: usize, - right: &impl BufferSupportedRhs, - right_offset_in_bits: usize, - len_in_bits: usize, -) { - mutable_bitwise_bin_op_helper( - left, - left_offset_in_bits, - right, - right_offset_in_bits, - len_in_bits, - |a, b| a ^ b, - ) -} - -/// Apply a bitwise NOT operation to the passed buffer. -/// -/// The buffer (mutable) is modified in-place to contain the result. -/// The input is treated as bitmap, meaning that offsets and length are -/// specified in number of bits. -/// -/// # Arguments -/// -/// * `buffer` - The mutable buffer (will be modified) -/// * `offset_in_bits` - Starting bit offset in the buffer -/// * `len_in_bits` - Number of bits to process -#[allow( - private_bounds, - reason = "MutableOpsBufferSupportedLhs exposes the inner internals which is the implementor choice and we dont want to leak internals" -)] -pub fn mutable_buffer_unary_not( - buffer: &mut impl MutableOpsBufferSupportedLhs, - offset_in_bits: usize, - len_in_bits: usize, -) { - mutable_bitwise_unary_op_helper(buffer, offset_in_bits, len_in_bits, |a| !a) } #[cfg(test)] @@ -964,14 +767,16 @@ mod tests { .map(|(l, r)| expected_op(*l, *r)) .collect(); - super::mutable_bitwise_bin_op_helper( - &mut left_buffer, - left_offset_in_bits, - right_buffer.inner(), - right_offset_in_bits, - len_in_bits, - op, - ); + // todo move these tests + unsafe { + left_buffer.mutable_buffer().mutable_bitwise_bin_op_helper( + left_offset_in_bits, + right_buffer.inner(), + right_offset_in_bits, + len_in_bits, + op, + ); + } let result: Vec = BitIterator::new(left_buffer.as_slice(), left_offset_in_bits, len_in_bits).collect(); @@ -1002,7 +807,14 @@ mod tests { .map(|b| expected_op(*b)) .collect(); - super::mutable_bitwise_unary_op_helper(&mut buffer, offset_in_bits, len_in_bits, op); + // TODO move these tests + unsafe { + buffer.mutable_buffer().mutable_bitwise_unary_op_helper( + offset_in_bits, + len_in_bits, + op, + ); + } let result: Vec = BitIterator::new(buffer.as_slice(), offset_in_bits, len_in_bits).collect(); diff --git a/arrow-buffer/src/builder/boolean.rs b/arrow-buffer/src/builder/boolean.rs index ff8a1f66a303..c3b9ee3daea9 100644 --- a/arrow-buffer/src/builder/boolean.rs +++ b/arrow-buffer/src/builder/boolean.rs @@ -15,11 +15,7 @@ // specific language governing permissions and limitations // under the License. -use crate::{ - BooleanBuffer, Buffer, MutableBuffer, MutableOpsBufferSupportedLhs, bit_mask, bit_util, - mutable_buffer_bin_and, mutable_buffer_bin_or, mutable_buffer_bin_xor, - mutable_buffer_unary_not, -}; +use crate::{BooleanBuffer, Buffer, MutableBuffer, bit_mask, bit_util}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Range}; /// Builder for [`BooleanBuffer`] @@ -260,11 +256,14 @@ impl BooleanBufferBuilder { pub fn finish_cloned(&self) -> BooleanBuffer { BooleanBuffer::new(Buffer::from_slice_ref(self.as_slice()), 0, self.len) } -} -/// This trait is not public API so it does not leak the inner mutable buffer -impl MutableOpsBufferSupportedLhs for BooleanBufferBuilder { - fn inner_mutable_buffer(&mut self) -> &mut MutableBuffer { + /// Return a mutable reference to the internal buffer + /// + /// # Safety + /// The caller must ensure that any modifications to the buffer maintain the invariant + /// `self.len < buffer.len() / 8` (that is that the buffer has enough capacity to hold `self.len` bits). + #[inline] + pub unsafe fn mutable_buffer(&mut self) -> &mut MutableBuffer { &mut self.buffer } } @@ -273,11 +272,9 @@ impl Not for BooleanBufferBuilder { type Output = BooleanBufferBuilder; fn not(mut self) -> Self::Output { - mutable_buffer_unary_not(&mut self.buffer, 0, self.len); - Self { - buffer: self.buffer, - len: self.len, - } + self.buffer + .mutable_bitwise_unary_op_helper(0, self.len, |b| !b); + self } } @@ -305,15 +302,21 @@ impl BitAndAssign<&BooleanBuffer> for BooleanBufferBuilder { fn bitand_assign(&mut self, rhs: &BooleanBuffer) { assert_eq!(self.len, rhs.len()); - mutable_buffer_bin_and(&mut self.buffer, 0, rhs.inner(), rhs.offset(), self.len); + self.buffer.mutable_bitwise_bin_op_helper( + 0, + rhs.inner(), + rhs.offset(), + self.len, + |a, b| a & b, + ); } } impl BitAndAssign<&BooleanBufferBuilder> for BooleanBufferBuilder { fn bitand_assign(&mut self, rhs: &BooleanBufferBuilder) { assert_eq!(self.len, rhs.len()); - - mutable_buffer_bin_and(&mut self.buffer, 0, &rhs.buffer, 0, self.len); + self.buffer + .mutable_bitwise_bin_op_helper(0, &rhs.buffer, 0, self.len, |a, b| a & b); } } @@ -340,8 +343,13 @@ impl BitOr<&BooleanBufferBuilder> for BooleanBufferBuilder { impl BitOrAssign<&BooleanBuffer> for BooleanBufferBuilder { fn bitor_assign(&mut self, rhs: &BooleanBuffer) { assert_eq!(self.len, rhs.len()); - - mutable_buffer_bin_or(&mut self.buffer, 0, rhs.inner(), rhs.offset(), self.len); + self.buffer.mutable_bitwise_bin_op_helper( + 0, + rhs.inner(), + rhs.offset(), + self.len, + |a, b| a | b, + ); } } @@ -349,7 +357,8 @@ impl BitOrAssign<&BooleanBufferBuilder> for BooleanBufferBuilder { fn bitor_assign(&mut self, rhs: &BooleanBufferBuilder) { assert_eq!(self.len, rhs.len()); - mutable_buffer_bin_or(&mut self.buffer, 0, &rhs.buffer, 0, self.len); + self.buffer + .mutable_bitwise_bin_op_helper(0, &rhs.buffer, 0, self.len, |a, b| a | b); } } @@ -376,16 +385,21 @@ impl BitXor<&BooleanBufferBuilder> for BooleanBufferBuilder { impl BitXorAssign<&BooleanBuffer> for BooleanBufferBuilder { fn bitxor_assign(&mut self, rhs: &BooleanBuffer) { assert_eq!(self.len, rhs.len()); - - mutable_buffer_bin_xor(&mut self.buffer, 0, rhs.inner(), rhs.offset(), self.len); + self.buffer.mutable_bitwise_bin_op_helper( + 0, + rhs.inner(), + rhs.offset(), + self.len, + |a, b| a ^ b, + ); } } impl BitXorAssign<&BooleanBufferBuilder> for BooleanBufferBuilder { fn bitxor_assign(&mut self, rhs: &BooleanBufferBuilder) { assert_eq!(self.len, rhs.len()); - - mutable_buffer_bin_xor(&mut self.buffer, 0, &rhs.buffer, 0, self.len); + self.buffer + .mutable_bitwise_bin_op_helper(0, &rhs.buffer, 0, self.len, |a, b| a ^ b); } }