Skip to content

Commit 1de7544

Browse files
committed
move fn to BitBuffer
Signed-off-by: Alexander Droste <[email protected]>
1 parent d3a1932 commit 1de7544

File tree

4 files changed

+156
-107
lines changed

4 files changed

+156
-107
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vortex-buffer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ workspace = true
4242
[dev-dependencies]
4343
arrow-buffer = { workspace = true }
4444
divan = { workspace = true }
45+
rstest = { workspace = true }
4546

4647
[[bench]]
4748
name = "vortex_buffer"

vortex-buffer/src/bit/buf.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,108 @@ impl BitBuffer {
406406
pub fn bitand_not(&self, rhs: &BitBuffer) -> BitBuffer {
407407
bitwise_binary_op(self, rhs, |a, b| a & !b)
408408
}
409+
410+
/// Iterate through bits in a buffer.
411+
///
412+
/// # Arguments
413+
///
414+
/// * `range` - Bit range to iterate through
415+
/// * `f` - Callback function taking (bit_index, is_set)
416+
///
417+
/// # Panics
418+
///
419+
/// Panics if the range is outside valid bounds of the buffer.
420+
#[inline]
421+
pub fn iter_bits<F>(&self, range: std::ops::Range<usize>, mut f: F)
422+
where
423+
F: FnMut(usize, bool),
424+
{
425+
let start = range.start;
426+
let end = range.end;
427+
428+
assert!(start <= end);
429+
assert!(end <= self.len);
430+
431+
let buffer_ptr = self.buffer.as_ptr();
432+
let offset = self.offset;
433+
434+
let full_bytes = (end - start) / 8;
435+
let remaining_bits = (end - start) % 8;
436+
437+
for byte_idx in 0..full_bytes {
438+
let bit_offset = offset + start + byte_idx * 8;
439+
let byte_offset = bit_offset / 8;
440+
let byte = unsafe { *buffer_ptr.add(byte_offset) };
441+
442+
for bit_idx in 0..8 {
443+
let is_set = (byte & (1 << bit_idx)) != 0;
444+
f(start + byte_idx * 8 + bit_idx, is_set);
445+
}
446+
}
447+
448+
if remaining_bits > 0 {
449+
let bit_idx_start = start + full_bytes * 8;
450+
let bit_offset = offset + bit_idx_start;
451+
let byte_offset = bit_offset / 8;
452+
let byte = unsafe { *buffer_ptr.add(byte_offset) };
453+
454+
for i in 0..remaining_bits {
455+
let is_set = (byte & (1 << i)) != 0;
456+
f(bit_idx_start + i, is_set);
457+
}
458+
}
459+
}
460+
461+
/// Iterate through bits in a buffer in reverse.
462+
///
463+
/// # Arguments
464+
///
465+
/// * `range` - Bit range to iterate through in reverse (start inclusive, end exclusive)
466+
/// * `f` - Callback function taking (bit_index, is_set)
467+
///
468+
/// # Panics
469+
///
470+
/// Panics if the range is outside valid bounds of the buffer.
471+
#[inline]
472+
pub fn iter_bits_reverse<F>(&self, range: std::ops::Range<usize>, mut f: F)
473+
where
474+
F: FnMut(usize, bool),
475+
{
476+
let start = range.start;
477+
let end = range.end;
478+
479+
assert!(start <= end);
480+
assert!(end <= self.len);
481+
482+
let buffer_ptr = self.buffer.as_ptr();
483+
let offset = self.offset;
484+
485+
let full_bytes = (end - start) / 8;
486+
let remaining_bits = (end - start) % 8;
487+
488+
if remaining_bits > 0 {
489+
let bit_idx_start = start + full_bytes * 8;
490+
let bit_offset = offset + bit_idx_start;
491+
let byte_offset = bit_offset / 8;
492+
let byte = unsafe { *buffer_ptr.add(byte_offset) };
493+
494+
for bit_idx in (0..remaining_bits).rev() {
495+
let is_set = (byte & (1 << bit_idx)) != 0;
496+
f(bit_idx_start + bit_idx, is_set);
497+
}
498+
}
499+
500+
for byte_idx in (0..full_bytes).rev() {
501+
let bit_offset = offset + start + byte_idx * 8;
502+
let byte_offset = bit_offset / 8;
503+
let byte = unsafe { *buffer_ptr.add(byte_offset) };
504+
505+
for bit_idx in (0..8).rev() {
506+
let is_set = (byte & (1 << bit_idx)) != 0;
507+
f(start + byte_idx * 8 + bit_idx, is_set);
508+
}
509+
}
510+
}
409511
}
410512

411513
impl<'a> IntoIterator for &'a BitBuffer {
@@ -419,6 +521,8 @@ impl<'a> IntoIterator for &'a BitBuffer {
419521

420522
#[cfg(test)]
421523
mod tests {
524+
use rstest::rstest;
525+
422526
use crate::bit::BitBuffer;
423527
use crate::{ByteBuffer, buffer};
424528

@@ -488,4 +592,52 @@ mod tests {
488592
"Buffer slices with different bits should not be equal (`PartialEq` needs `iter_padded()`)"
489593
);
490594
}
595+
596+
#[rstest]
597+
#[case(5)]
598+
#[case(8)]
599+
#[case(10)]
600+
#[case(13)]
601+
#[case(16)]
602+
#[case(23)]
603+
#[case(100)]
604+
fn test_iter_bits(#[case] len: usize) {
605+
let buf = BitBuffer::collect_bool(len, |i| i % 2 == 0);
606+
607+
let mut collected = Vec::new();
608+
buf.iter_bits(0..len, |idx, is_set| {
609+
collected.push((idx, is_set));
610+
});
611+
612+
assert_eq!(collected.len(), len);
613+
614+
for (idx, is_set) in collected {
615+
assert_eq!(is_set, idx % 2 == 0);
616+
}
617+
}
618+
619+
#[rstest]
620+
#[case(5)]
621+
#[case(8)]
622+
#[case(10)]
623+
#[case(13)]
624+
#[case(16)]
625+
#[case(23)]
626+
#[case(100)]
627+
fn test_iter_bits_reverse(#[case] len: usize) {
628+
let buf = BitBuffer::collect_bool(len, |i| i % 2 == 0);
629+
630+
let mut collected = Vec::new();
631+
buf.iter_bits_reverse(0..len, |idx, is_set| {
632+
collected.push((idx, is_set));
633+
});
634+
635+
assert_eq!(collected.len(), len);
636+
637+
for (i, (idx, is_set)) in collected.iter().enumerate() {
638+
let expected_idx = len - 1 - i;
639+
assert_eq!(*idx, expected_idx);
640+
assert_eq!(*is_set, expected_idx % 2 == 0);
641+
}
642+
}
491643
}

vortex-compute/src/expand/buffer.rs

Lines changed: 2 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4-
use std::ops::Range;
54
use vortex_buffer::{Buffer, BufferMut};
65
use vortex_mask::{Mask, MaskValues};
76

@@ -100,7 +99,7 @@ fn expand_inplace<T: Copy>(buf_mut: &mut BufferMut<T>, mask_values: &MaskValues)
10099
let bit_buffer = mask_values.bit_buffer();
101100

102101
// Iterate backwards through the mask to avoid overwriting unprocessed elements.
103-
iter_bits_reverse(bit_buffer, 0..mask_len, |idx, is_valid| {
102+
bit_buffer.iter_bits_reverse(0..mask_len, |idx, is_valid| {
104103
if is_valid {
105104
element_idx -= 1;
106105
unsafe { *buf_slice.get_unchecked_mut(idx) = buf_slice[element_idx] };
@@ -134,7 +133,7 @@ fn expand_copy<T: Copy>(src: &[T], mask_values: &MaskValues) -> Buffer<T> {
134133

135134
let bit_buffer = mask_values.bit_buffer();
136135

137-
iter_bits(bit_buffer, 0..mask_len, |mask_idx, is_valid| {
136+
bit_buffer.iter_bits(0..mask_len, |mask_idx, is_valid| {
138137
if is_valid {
139138
unsafe {
140139
target_slice
@@ -157,110 +156,6 @@ fn expand_copy<T: Copy>(src: &[T], mask_values: &MaskValues) -> Buffer<T> {
157156
target_buf.freeze()
158157
}
159158

160-
/// Iterate through bits in a buffer.
161-
///
162-
/// # Arguments
163-
///
164-
/// * `bit_buffer` - The bit buffer to iterate through
165-
/// * `range` - Bit range to iterate through
166-
/// * `f` - Callback function taking (bit_index, is_set)
167-
///
168-
/// # Safety
169-
///
170-
/// The caller must ensure that the range is within valid bounds of the bit buffer.
171-
#[inline]
172-
fn iter_bits<F>(bit_buffer: &vortex_buffer::BitBuffer, range: Range<usize>, mut f: F)
173-
where
174-
F: FnMut(usize, bool),
175-
{
176-
let start = range.start;
177-
let end = range.end;
178-
179-
assert!(start <= end);
180-
assert!(end <= bit_buffer.len());
181-
182-
let buffer_ptr = bit_buffer.inner().as_ptr();
183-
let offset = bit_buffer.offset();
184-
185-
let full_bytes = (end - start) / 8;
186-
let remaining_bits = (end - start) % 8;
187-
188-
for byte_idx in 0..full_bytes {
189-
let bit_offset = offset + start + byte_idx * 8;
190-
let byte_offset = bit_offset / 8;
191-
let byte = unsafe { *buffer_ptr.add(byte_offset) };
192-
193-
for bit_idx in 0..8 {
194-
let is_set = (byte & (1 << bit_idx)) != 0;
195-
f(start + byte_idx * 8 + bit_idx, is_set);
196-
}
197-
}
198-
199-
if remaining_bits > 0 {
200-
let bit_idx_start = start + full_bytes * 8;
201-
let bit_offset = offset + bit_idx_start;
202-
let byte_offset = bit_offset / 8;
203-
let byte = unsafe { *buffer_ptr.add(byte_offset) };
204-
205-
for i in 0..remaining_bits {
206-
let is_set = (byte & (1 << i)) != 0;
207-
f(bit_idx_start + i, is_set);
208-
}
209-
}
210-
}
211-
212-
/// Iterate through bits in a buffer in reverse.
213-
///
214-
/// # Arguments
215-
///
216-
/// * `bit_buffer` - The bit buffer to iterate through
217-
/// * `range` - Bit range to iterate through in reverse (start inclusive, end exclusive)
218-
/// * `f` - Callback function taking (bit_index, is_set)
219-
///
220-
/// # Safety
221-
///
222-
/// The caller must ensure that the range is within valid bounds of the bit buffer.
223-
#[inline]
224-
fn iter_bits_reverse<F>(bit_buffer: &vortex_buffer::BitBuffer, range: Range<usize>, mut f: F)
225-
where
226-
F: FnMut(usize, bool),
227-
{
228-
let start = range.start;
229-
let end = range.end;
230-
231-
assert!(start <= end);
232-
assert!(end <= bit_buffer.len());
233-
234-
let buffer_ptr = bit_buffer.inner().as_ptr();
235-
let offset = bit_buffer.offset();
236-
237-
let full_bytes = (end - start) / 8;
238-
let remaining_bits = (end - start) % 8;
239-
240-
if remaining_bits > 0 {
241-
let bit_idx_start = start + full_bytes * 8;
242-
let bit_offset = offset + bit_idx_start;
243-
let byte_offset = bit_offset / 8;
244-
let byte = unsafe { *buffer_ptr.add(byte_offset) };
245-
246-
for bit_idx in (0..remaining_bits).rev() {
247-
let is_set = (byte & (1 << bit_idx)) != 0;
248-
f(bit_idx_start + bit_idx, is_set);
249-
}
250-
}
251-
252-
for byte_idx in (0..full_bytes).rev() {
253-
let bit_offset = offset + start + byte_idx * 8;
254-
let byte_offset = bit_offset / 8;
255-
let byte = unsafe { *buffer_ptr.add(byte_offset) };
256-
257-
for bit_idx in (0..8).rev() {
258-
let is_set = (byte & (1 << bit_idx)) != 0;
259-
f(start + byte_idx * 8 + bit_idx, is_set);
260-
}
261-
}
262-
}
263-
264159
#[cfg(test)]
265160
mod tests {
266161
use vortex_buffer::{buffer, buffer_mut};

0 commit comments

Comments
 (0)