Skip to content

Commit 04776cc

Browse files
authored
Remove BitBuffer::into_mut and require callers to handle failure to acquire unique ownership (#7136)
BitBuffer::into_mut is an escape hatch that will make the callers copy the whole buffer while they can implement better logic by fusing their buffer operation with the copy Signed-off-by: Robert Kruszewski <github@robertk.io> --------- Signed-off-by: Robert Kruszewski <github@robertk.io>
1 parent 8f13bda commit 04776cc

10 files changed

Lines changed: 94 additions & 54 deletions

File tree

fuzz/src/array/fill_null.rs

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ pub fn fill_null_canonical_array(
3434

3535
Ok(match canonical {
3636
Canonical::Null(array) => ConstantArray::new(fill_value.clone(), array.len()).into_array(),
37-
Canonical::Bool(array) => fill_bool_array(&array, fill_value, result_nullability),
38-
Canonical::Primitive(array) => fill_primitive_array(&array, fill_value, result_nullability),
39-
Canonical::Decimal(array) => fill_decimal_array(&array, fill_value, result_nullability),
37+
Canonical::Bool(array) => fill_bool_array(array, fill_value, result_nullability),
38+
Canonical::Primitive(array) => fill_primitive_array(array, fill_value, result_nullability),
39+
Canonical::Decimal(array) => fill_decimal_array(array, fill_value, result_nullability),
4040
Canonical::VarBinView(array) => {
41-
fill_varbinview_array(&array, fill_value, result_nullability)
41+
fill_varbinview_array(array, fill_value, result_nullability)
4242
}
4343
Canonical::Struct(_)
4444
| Canonical::List(_)
@@ -48,7 +48,7 @@ pub fn fill_null_canonical_array(
4848
}
4949

5050
fn fill_bool_array(
51-
array: &BoolArray,
51+
array: BoolArray,
5252
fill_value: &Scalar,
5353
result_nullability: Nullability,
5454
) -> ArrayRef {
@@ -59,28 +59,37 @@ fn fill_bool_array(
5959

6060
match array.validity() {
6161
Validity::NonNullable | Validity::AllValid => {
62-
BoolArray::new(array.to_bit_buffer(), result_nullability.into()).into_array()
62+
BoolArray::new(array.into_bit_buffer(), result_nullability.into()).into_array()
6363
}
6464
Validity::AllInvalid => ConstantArray::new(fill_value.clone(), array.len()).into_array(),
6565
Validity::Array(validity_array) => {
66-
let validity_bool_array = validity_array.to_bool();
67-
let validity_bits = validity_bool_array.to_bit_buffer();
68-
let data_bits = array.to_bit_buffer();
69-
70-
let mut new_bits = data_bits.into_mut();
71-
72-
(!validity_bits)
73-
.set_indices()
74-
.for_each(|i| new_bits.set_to(i, fill_bool));
66+
let validity_bits = validity_array.to_bool().into_bit_buffer();
67+
let data_bits = array.into_bit_buffer();
68+
69+
let new_bits = match data_bits.try_into_mut() {
70+
Ok(mut buf) => {
71+
(!validity_bits)
72+
.set_indices()
73+
.for_each(|i| buf.set_to(i, fill_bool));
74+
buf.freeze()
75+
}
76+
Err(data_bits) => {
77+
if fill_bool {
78+
data_bits | !validity_bits
79+
} else {
80+
data_bits & validity_bits
81+
}
82+
}
83+
};
7584

76-
BoolArray::new(new_bits.freeze(), result_nullability.into()).into_array()
85+
BoolArray::new(new_bits, result_nullability.into()).into_array()
7786
}
7887
}
7988
}
8089

8190
#[expect(clippy::cognitive_complexity)]
8291
fn fill_primitive_array(
83-
array: &PrimitiveArray,
92+
array: PrimitiveArray,
8493
fill_value: &Scalar,
8594
result_nullability: Nullability,
8695
) -> ArrayRef {
@@ -117,7 +126,7 @@ fn fill_primitive_array(
117126
}
118127

119128
fn fill_decimal_array(
120-
array: &DecimalArray,
129+
array: DecimalArray,
121130
fill_value: &Scalar,
122131
result_nullability: Nullability,
123132
) -> ArrayRef {
@@ -161,7 +170,7 @@ fn fill_decimal_array(
161170
}
162171

163172
fn fill_varbinview_array(
164-
array: &VarBinViewArray,
173+
array: VarBinViewArray,
165174
fill_value: &Scalar,
166175
result_nullability: Nullability,
167176
) -> ArrayRef {

vortex-array/src/arrays/bool/array.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,9 @@ impl BoolArray {
224224

225225
/// Returns the underlying [`BitBuffer`] of the array
226226
pub fn into_bit_buffer(self) -> BitBuffer {
227-
self.to_bit_buffer()
227+
let buffer = self.bits.unwrap_host();
228+
229+
BitBuffer::new_with_offset(buffer, self.len, self.offset)
228230
}
229231

230232
pub fn to_mask(&self) -> Mask {

vortex-array/src/arrays/bool/patch.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

44
use itertools::Itertools;
5+
use vortex_buffer::BitBufferMut;
56
use vortex_error::VortexResult;
67

78
use crate::ExecutionCtx;
@@ -26,7 +27,10 @@ impl BoolArray {
2627
ctx,
2728
)?;
2829

29-
let mut own_values = self.into_bit_buffer().into_mut();
30+
let bit_buffer = self.into_bit_buffer();
31+
let mut own_values = bit_buffer
32+
.try_into_mut()
33+
.unwrap_or_else(|bb| BitBufferMut::copy_from(&bb));
3034
match_each_unsigned_integer_ptype!(indices.ptype(), |I| {
3135
for (idx, value) in indices
3236
.as_slice::<I>()

vortex-array/src/arrays/decimal/compute/fill_null.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl FillNullKernel for Decimal {
3636
let is_invalid = is_valid
3737
.clone()
3838
.execute::<BoolArray>(ctx)?
39-
.to_bit_buffer()
39+
.into_bit_buffer()
4040
.not();
4141
let decimal_scalar = fill_value.as_decimal();
4242
let decimal_value = decimal_scalar

vortex-array/src/arrays/primitive/compute/fill_null.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl FillNullKernel for Primitive {
3131
let is_invalid = is_valid
3232
.clone()
3333
.execute::<BoolArray>(ctx)?
34-
.to_bit_buffer()
34+
.into_bit_buffer()
3535
.not();
3636
match_each_native_ptype!(array.ptype(), |T| {
3737
let mut buffer = array.to_buffer::<T>().into_mut();

vortex-buffer/public-api.lock

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,6 @@ impl vortex_buffer::BitBuffer
302302

303303
pub fn vortex_buffer::BitBuffer::into_inner(self) -> (usize, usize, vortex_buffer::ByteBuffer)
304304

305-
pub fn vortex_buffer::BitBuffer::into_mut(self) -> vortex_buffer::BitBufferMut
306-
307305
pub fn vortex_buffer::BitBuffer::try_into_mut(self) -> core::result::Result<vortex_buffer::BitBufferMut, Self>
308306

309307
impl core::clone::Clone for vortex_buffer::BitBuffer

vortex-buffer/src/bit/buf.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use crate::bit::get_bit_unchecked;
2626
use crate::bit::ops::bitwise_binary_op;
2727
use crate::bit::ops::bitwise_unary_op;
2828
use crate::buffer;
29-
use crate::trusted_len::TrustedLenExt;
3029

3130
/// An immutable bitset stored as a packed byte buffer.
3231
#[derive(Debug, Clone, Eq)]
@@ -348,12 +347,8 @@ impl BitBuffer {
348347
self.len,
349348
);
350349
}
351-
// Use Chunk iterator here to reset offset to 0
352-
let iter = self.chunks().iter_padded();
353-
let iter = unsafe { iter.trusted_len() };
354-
let result = Buffer::<u64>::from_trusted_len_iter(iter).into_byte_buffer();
355350

356-
BitBuffer::new(result, self.len())
351+
bitwise_unary_op(self.clone(), |a| a)
357352
}
358353
}
359354

@@ -372,15 +367,6 @@ impl BitBuffer {
372367
Err(buffer) => Err(BitBuffer::new_with_offset(buffer, self.len, self.offset)),
373368
}
374369
}
375-
376-
/// Get a mutable version of this `BitBuffer` along with bit offset in the first byte.
377-
///
378-
/// If the caller doesn't hold only reference to the underlying buffer, a copy is created.
379-
/// The second value of the tuple is a bit_offset of the first value in the first byte
380-
pub fn into_mut(self) -> BitBufferMut {
381-
let (offset, len, inner) = self.into_inner();
382-
BitBufferMut::from_buffer(inner.into_mut(), offset, len)
383-
}
384370
}
385371

386372
impl From<&[bool]> for BitBuffer {
@@ -469,7 +455,7 @@ impl Not for &BitBuffer {
469455

470456
#[inline]
471457
fn not(self) -> Self::Output {
472-
bitwise_unary_op(self, |a| !a)
458+
!self.clone()
473459
}
474460
}
475461

@@ -478,7 +464,7 @@ impl Not for BitBuffer {
478464

479465
#[inline]
480466
fn not(self) -> Self::Output {
481-
(&self).not()
467+
bitwise_unary_op(self, |a| !a)
482468
}
483469
}
484470

vortex-buffer/src/bit/buf_mut.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ impl Not for BitBufferMut {
656656

657657
impl From<&[bool]> for BitBufferMut {
658658
fn from(value: &[bool]) -> Self {
659-
BitBuffer::collect_bool(value.len(), |i| value[i]).into_mut()
659+
BitBufferMut::collect_bool(value.len(), |i| value[i])
660660
}
661661
}
662662

@@ -1183,7 +1183,7 @@ mod tests {
11831183
for i in 0..10 {
11841184
let buf = bitbuffer![0 1 0 1 0 1 0 1 0 1];
11851185

1186-
let mut buf_mut = buf.clone().into_mut();
1186+
let mut buf_mut = BitBufferMut::copy_from(&buf);
11871187
assert_eq!(buf_mut.len(), 10);
11881188

11891189
let tail = buf_mut.split_off(i);
@@ -1201,7 +1201,7 @@ mod tests {
12011201
for i in 0..10 {
12021202
let buf = bitbuffer![0 1 0 1 0 1 0 1 0 1 0 1].slice(2..);
12031203

1204-
let mut buf_mut = buf.clone().into_mut();
1204+
let mut buf_mut = BitBufferMut::copy_from(&buf);
12051205
assert_eq!(buf_mut.len(), 10);
12061206

12071207
let tail = buf_mut.split_off(i);

vortex-buffer/src/bit/ops.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,54 @@
44
use crate::BitBuffer;
55
use crate::BitBufferMut;
66
use crate::Buffer;
7+
use crate::ByteBufferMut;
78
use crate::trusted_len::TrustedLenExt;
89

910
#[inline]
10-
pub(super) fn bitwise_unary_op<F: FnMut(u64) -> u64>(buffer: &BitBuffer, op: F) -> BitBuffer {
11-
let mut buf = buffer.clone().into_mut();
12-
bitwise_unary_op_mut(&mut buf, op);
13-
buf.freeze()
11+
pub(super) fn bitwise_unary_op<F: FnMut(u64) -> u64>(buffer: BitBuffer, mut op: F) -> BitBuffer {
12+
match buffer.try_into_mut() {
13+
Ok(mut buf) => {
14+
bitwise_unary_op_mut(&mut buf, op);
15+
buf.freeze()
16+
}
17+
Err(buffer) => {
18+
let len = buffer.len();
19+
let offset = buffer.offset();
20+
let src = buffer.inner().as_slice();
21+
22+
let mut dst = ByteBufferMut::with_capacity(src.len());
23+
let u64_len = src.len() / 8;
24+
let remainder = src.len() % 8;
25+
26+
let mut src_ptr = src.as_ptr() as *const u64;
27+
let mut dst_ptr = dst.spare_capacity_mut().as_mut_ptr() as *mut u64;
28+
for _ in 0..u64_len {
29+
let value = unsafe { src_ptr.read_unaligned() };
30+
unsafe { dst_ptr.write_unaligned(op(value)) };
31+
src_ptr = unsafe { src_ptr.add(1) };
32+
dst_ptr = unsafe { dst_ptr.add(1) };
33+
}
34+
35+
if remainder > 0 {
36+
let mut remainder_u64 = 0u64;
37+
let src_bytes = src_ptr as *const u8;
38+
let dst_bytes = dst_ptr as *mut u8;
39+
for i in 0..remainder {
40+
let byte = unsafe { src_bytes.add(i).read() };
41+
remainder_u64 |= (byte as u64) << (i * 8);
42+
}
43+
let remainder_u64 = op(remainder_u64);
44+
for i in 0..remainder {
45+
let byte = ((remainder_u64 >> (i * 8)) & 0xFF) as u8;
46+
unsafe { dst_bytes.add(i).write(byte) };
47+
}
48+
}
49+
50+
// SAFETY: we wrote exactly src.len() bytes into the spare capacity.
51+
unsafe { dst.set_len(src.len()) };
52+
BitBuffer::new_with_offset(dst.freeze(), len, offset)
53+
}
54+
}
1455
}
1556

1657
#[inline]
@@ -95,7 +136,7 @@ mod tests {
95136
#[test]
96137
fn test_bitwise_unary_not() {
97138
let buffer = BitBuffer::new(buffer![0b10101010u8], 4);
98-
let result = bitwise_unary_op(&buffer, |x| !x);
139+
let result = bitwise_unary_op(buffer, |x| !x);
99140
assert_eq!(result, bitbuffer![true, false, true, false]);
100141
}
101142

vortex-mask/src/mask_mut.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,10 @@ impl Mask {
498498
Mask::AllFalse(len) => MaskMut::new_false(len),
499499
Mask::Values(values) => {
500500
let bit_buffer_mut = match Arc::try_unwrap(values) {
501-
Ok(mask_values) => {
502-
let bit_buffer = mask_values.into_buffer();
503-
bit_buffer.into_mut()
504-
}
501+
Ok(mask_values) => mask_values
502+
.into_buffer()
503+
.try_into_mut()
504+
.unwrap_or_else(|bb| BitBufferMut::copy_from(&bb)),
505505
Err(arc_mask_values) => {
506506
let bit_buffer = arc_mask_values.bit_buffer();
507507
BitBufferMut::copy_from(bit_buffer)

0 commit comments

Comments
 (0)