Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 26 additions & 52 deletions arrow-buffer/src/buffer/mutable_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,41 +52,6 @@ impl BufferSupportedRhs for BooleanBufferBuilder {
}
}

/// 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`.
///
/// This is used to perform in-place operations on the buffer.
///
/// 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
Expand All @@ -106,7 +71,7 @@ impl MutableOpsBufferSupportedLhs for MutableBuffer {
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<F>(
left: &mut impl MutableOpsBufferSupportedLhs,
left: &mut MutableBuffer,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than hiding what is going on with pub/hidden traits, I think it is clearer to simply pass in the MutableBuffer here

If you accept this PR and #1 we can avoid the #allow about private bounds as well

left_offset_in_bits: usize,
right: &impl BufferSupportedRhs,
right_offset_in_bits: usize,
Expand All @@ -119,7 +84,7 @@ pub fn mutable_bitwise_bin_op_helper<F>(
return;
}

let mutable_buffer = left.inner_mutable_buffer();
let mutable_buffer = left;

let mutable_buffer_len = mutable_buffer.len();
let mutable_buffer_cap = mutable_buffer.capacity();
Expand Down Expand Up @@ -734,7 +699,7 @@ fn handle_mutable_buffer_remainder_unary<F>(
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<F>(
buffer: &mut impl MutableOpsBufferSupportedLhs,
buffer: &mut MutableBuffer,
offset_in_bits: usize,
len_in_bits: usize,
mut op: F,
Expand All @@ -745,7 +710,7 @@ pub fn mutable_bitwise_unary_op_helper<F>(
return;
}

let mutable_buffer = buffer.inner_mutable_buffer();
let mutable_buffer = buffer;

let mutable_buffer_len = mutable_buffer.len();
let mutable_buffer_cap = mutable_buffer.capacity();
Expand Down Expand Up @@ -829,7 +794,7 @@ pub fn mutable_bitwise_unary_op_helper<F>(
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: &mut MutableBuffer,
left_offset_in_bits: usize,
right: &impl BufferSupportedRhs,
right_offset_in_bits: usize,
Expand Down Expand Up @@ -863,7 +828,7 @@ pub fn mutable_buffer_bin_and(
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: &mut MutableBuffer,
left_offset_in_bits: usize,
right: &impl BufferSupportedRhs,
right_offset_in_bits: usize,
Expand Down Expand Up @@ -897,7 +862,7 @@ pub fn mutable_buffer_bin_or(
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: &mut MutableBuffer,
left_offset_in_bits: usize,
right: &impl BufferSupportedRhs,
right_offset_in_bits: usize,
Expand Down Expand Up @@ -929,7 +894,7 @@ pub fn mutable_buffer_bin_xor(
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,
buffer: &mut MutableBuffer,
offset_in_bits: usize,
len_in_bits: usize,
) {
Expand Down Expand Up @@ -964,14 +929,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,
);
unsafe {
super::mutable_bitwise_bin_op_helper(
left_buffer.mutable_buffer(),
left_offset_in_bits,
right_buffer.inner(),
right_offset_in_bits,
len_in_bits,
op,
);
}

let result: Vec<bool> =
BitIterator::new(left_buffer.as_slice(), left_offset_in_bits, len_in_bits).collect();
Expand Down Expand Up @@ -1002,7 +969,14 @@ mod tests {
.map(|b| expected_op(*b))
.collect();

super::mutable_bitwise_unary_op_helper(&mut buffer, offset_in_bits, len_in_bits, op);
unsafe {
super::mutable_bitwise_unary_op_helper(
buffer.mutable_buffer(),
offset_in_bits,
len_in_bits,
op,
);
}

let result: Vec<bool> =
BitIterator::new(buffer.as_slice(), offset_in_bits, len_in_bits).collect();
Expand Down
16 changes: 9 additions & 7 deletions arrow-buffer/src/builder/boolean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
// 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,
BooleanBuffer, Buffer, MutableBuffer, bit_mask, bit_util, mutable_buffer_bin_and,
mutable_buffer_bin_or, mutable_buffer_bin_xor, mutable_buffer_unary_not,
};
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Range};

Expand Down Expand Up @@ -260,11 +259,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 {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think the code is much clearer and easier to understand if make a function that directly exposes the inner MutableBuffer (which is what the trait does) but mark it as unsafe, rather than relying on non public traits

&mut self.buffer
}
}
Expand Down