Skip to content

Commit 20fd841

Browse files
committed
Add arithmetic buffer compute
Signed-off-by: Nicholas Gates <[email protected]>
2 parents f8a7b41 + 60ce4b5 commit 20fd841

File tree

14 files changed

+800
-176
lines changed

14 files changed

+800
-176
lines changed

vortex-array/src/arrays/primitive/vtable/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{EncodingId, EncodingRef, vtable};
88
mod array;
99
mod canonical;
1010
mod operations;
11-
mod pipeline;
11+
mod operator;
1212
mod serde;
1313
mod validity;
1414
mod visitor;
@@ -26,8 +26,8 @@ impl VTable for PrimitiveVTable {
2626
type VisitorVTable = Self;
2727
type ComputeVTable = NotSupported;
2828
type EncodeVTable = NotSupported;
29-
type OperatorVTable = Self;
3029
type SerdeVTable = Self;
30+
type OperatorVTable = Self;
3131

3232
fn id(_encoding: &Self::Encoding) -> EncodingId {
3333
EncodingId::new_ref("vortex.primitive")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_compute::filter::Filter;
5+
use vortex_dtype::match_each_native_ptype;
6+
use vortex_error::VortexResult;
7+
use vortex_vector::PVector;
8+
9+
use crate::ArrayRef;
10+
use crate::arrays::{PrimitiveArray, PrimitiveVTable};
11+
use crate::execution::{BatchKernelRef, BindCtx, kernel};
12+
use crate::vtable::{OperatorVTable, ValidityHelper};
13+
14+
impl OperatorVTable<PrimitiveVTable> for PrimitiveVTable {
15+
fn bind(
16+
array: &PrimitiveArray,
17+
selection: Option<&ArrayRef>,
18+
ctx: &mut dyn BindCtx,
19+
) -> VortexResult<BatchKernelRef> {
20+
let mask = ctx.bind_selection(array.len(), selection)?;
21+
let validity = ctx.bind_validity(array.validity(), array.len(), selection)?;
22+
23+
match_each_native_ptype!(array.ptype(), |T| {
24+
let elements = array.buffer::<T>();
25+
Ok(kernel(move || {
26+
let mask = mask.execute()?;
27+
let validity = validity.execute()?;
28+
29+
// Note that validity already has the mask applied so we only need to apply it to
30+
// the elements.
31+
let elements = elements.filter(&mask);
32+
33+
Ok(PVector::new(elements, validity).into())
34+
}))
35+
})
36+
}
37+
}

vortex-array/src/arrays/primitive/vtable/pipeline.rs

Lines changed: 0 additions & 85 deletions
This file was deleted.

vortex-buffer/src/bit/buf_mut.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ use crate::{BitBuffer, BufferMut, ByteBufferMut, buffer_mut};
3131
#[derive(Debug, Clone, Eq)]
3232
pub struct BitBufferMut {
3333
buffer: ByteBufferMut,
34+
/// Represents the offset of the bit buffer into the first byte.
35+
///
36+
/// This is always less than 8 (for when the bit buffer is not aligned to a byte).
3437
offset: usize,
3538
len: usize,
3639
}
@@ -162,6 +165,13 @@ impl BitBufferMut {
162165
self.buffer.reserve(additional_bytes);
163166
}
164167

168+
/// Clears the bit buffer (but keeps any allocated memory).
169+
pub fn clear(&mut self) {
170+
// Since there are no items we need to drop, we simply set the length to 0.
171+
self.len = 0;
172+
self.offset = 0;
173+
}
174+
165175
/// Set the bit at `index` to the given boolean value.
166176
///
167177
/// This operation is checked so if `index` exceeds the buffer length, this will panic.

vortex-mask/src/mask_mut.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::ops::Sub;
55
use std::sync::Arc;
66

77
use vortex_buffer::BitBufferMut;
8+
use vortex_error::vortex_panic;
89

910
use crate::Mask;
1011

@@ -56,6 +57,29 @@ impl MaskMut {
5657
})
5758
}
5859

60+
/// Returns the boolean value at a given index.
61+
///
62+
/// # Panics
63+
///
64+
/// Panics if the index is out of bounds.
65+
pub fn value(&self, index: usize) -> bool {
66+
match &self.0 {
67+
Inner::Empty { .. } => {
68+
vortex_panic!("index out of bounds: the length is 0 but the index is {index}")
69+
}
70+
Inner::Constant { value, len, .. } => {
71+
assert!(
72+
index < *len,
73+
"index out of bounds: the length is {} but the index is {index}",
74+
*len
75+
);
76+
77+
*value
78+
}
79+
Inner::Builder(bit_buffer) => bit_buffer.value(index),
80+
}
81+
}
82+
5983
/// Reserve capacity for at least `additional` more values to be appended.
6084
pub fn reserve(&mut self, additional: usize) {
6185
match &mut self.0 {
@@ -71,6 +95,39 @@ impl MaskMut {
7195
}
7296
}
7397

98+
/// Clears the mask.
99+
///
100+
/// Note that this method has no effect on the allocated capacity of the mask.
101+
pub fn clear(&mut self) {
102+
match &mut self.0 {
103+
Inner::Empty { .. } => {}
104+
Inner::Constant { capacity, .. } => {
105+
self.0 = Inner::Empty {
106+
capacity: *capacity,
107+
}
108+
}
109+
Inner::Builder(bit_buffer) => bit_buffer.clear(),
110+
};
111+
}
112+
113+
/// Shortens the mask, keeping the first `len` bits.
114+
///
115+
/// If `len` is greater or equal to the vector’s current length, this has no effect.
116+
///
117+
/// Note that this method has no effect on the allocated capacity of the mask.
118+
pub fn truncate(&mut self, len: usize) {
119+
let truncated_len = len;
120+
if truncated_len > self.len() {
121+
return;
122+
}
123+
124+
match &mut self.0 {
125+
Inner::Empty { .. } => {}
126+
Inner::Constant { len, .. } => *len = truncated_len.min(*len),
127+
Inner::Builder(bit_buffer) => bit_buffer.truncate(truncated_len),
128+
};
129+
}
130+
74131
/// Append n values to the mask.
75132
pub fn append_n(&mut self, new_value: bool, n: usize) {
76133
match &mut self.0 {
Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4-
//! [`FromIterator`] and related implementations for [`BoolVectorMut`].
4+
//! Iterator implementations for [`BoolVectorMut`].
55
66
use vortex_buffer::BitBufferMut;
77
use vortex_mask::MaskMut;
88

9-
use crate::BoolVectorMut;
9+
use crate::{BoolVectorMut, VectorMutOps};
1010

1111
impl FromIterator<Option<bool>> for BoolVectorMut {
1212
/// Creates a new [`BoolVectorMut`] from an iterator of `Option<bool>` values.
@@ -78,3 +78,63 @@ impl FromIterator<bool> for BoolVectorMut {
7878
}
7979
}
8080
}
81+
82+
/// Iterator over a [`BoolVectorMut`] that yields [`Option<bool>`] values.
83+
///
84+
/// This iterator is created by calling [`IntoIterator::into_iter`] on a [`BoolVectorMut`].
85+
///
86+
/// It consumes the mutable vector and iterates over the elements, yielding `None` for null values
87+
/// and `Some(value)` for valid values.
88+
pub struct BoolVectorMutIterator {
89+
/// The vector being iterated over.
90+
vector: BoolVectorMut,
91+
/// The current index into the vector.
92+
index: usize,
93+
}
94+
95+
impl Iterator for BoolVectorMutIterator {
96+
type Item = Option<bool>;
97+
98+
fn next(&mut self) -> Option<Self::Item> {
99+
(self.index < self.vector.len()).then(|| {
100+
let value = self
101+
.vector
102+
.validity
103+
.value(self.index)
104+
.then(|| self.vector.bits.value(self.index));
105+
self.index += 1;
106+
value
107+
})
108+
}
109+
110+
fn size_hint(&self) -> (usize, Option<usize>) {
111+
let remaining = self.vector.len() - self.index;
112+
(remaining, Some(remaining))
113+
}
114+
}
115+
116+
impl IntoIterator for BoolVectorMut {
117+
type Item = Option<bool>;
118+
type IntoIter = BoolVectorMutIterator;
119+
120+
/// Converts the mutable vector into an iterator over `Option<bool>` values.
121+
///
122+
/// This method consumes the `BoolVectorMut` and returns an iterator that yields `None` for
123+
/// null values and `Some(value)` for valid values.
124+
///
125+
/// # Examples
126+
///
127+
/// ```
128+
/// use vortex_vector::BoolVectorMut;
129+
///
130+
/// let vec = BoolVectorMut::from_iter([Some(true), None, Some(false), Some(true)]);
131+
/// let collected: Vec<_> = vec.into_iter().collect();
132+
/// assert_eq!(collected, vec![Some(true), None, Some(false), Some(true)]);
133+
/// ```
134+
fn into_iter(self) -> Self::IntoIter {
135+
BoolVectorMutIterator {
136+
vector: self,
137+
index: 0,
138+
}
139+
}
140+
}

vortex-vector/src/bool/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub use vector::BoolVector;
99
mod vector_mut;
1010
pub use vector_mut::BoolVectorMut;
1111

12-
mod from_iter;
12+
mod iter;
1313

1414
use crate::{Vector, VectorMut};
1515

vortex-vector/src/bool/vector_mut.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,41 @@ mod tests {
224224
let frozen = vec1.freeze();
225225
assert_eq!(frozen.validity().true_count(), 3);
226226
}
227+
228+
#[test]
229+
fn test_into_iter_roundtrip() {
230+
// Test that from_iter followed by into_iter preserves the data.
231+
let original_data = vec![
232+
Some(true),
233+
None,
234+
Some(false),
235+
Some(true),
236+
None,
237+
Some(false),
238+
None,
239+
Some(true),
240+
];
241+
242+
// Create vector from iterator.
243+
let vec = BoolVectorMut::from_iter(original_data.clone());
244+
245+
// Convert back to iterator and collect.
246+
let roundtrip: Vec<_> = vec.into_iter().collect();
247+
248+
// Should be identical.
249+
assert_eq!(roundtrip, original_data);
250+
251+
// Also test with all valid values.
252+
let all_valid = vec![true, false, true, false, true];
253+
let vec = BoolVectorMut::from_iter(all_valid.clone());
254+
let roundtrip: Vec<_> = vec.into_iter().collect();
255+
let expected: Vec<_> = all_valid.into_iter().map(Some).collect();
256+
assert_eq!(roundtrip, expected);
257+
258+
// Test with empty.
259+
let empty: Vec<Option<bool>> = vec![];
260+
let vec = BoolVectorMut::from_iter(empty.clone());
261+
let roundtrip: Vec<_> = vec.into_iter().collect();
262+
assert_eq!(roundtrip, empty);
263+
}
227264
}

0 commit comments

Comments
 (0)