Skip to content

Commit 3935ac9

Browse files
committed
Polish fixed-size binary implementation
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent af744cd commit 3935ac9

9 files changed

Lines changed: 630 additions & 500 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use itertools::Itertools;
5+
6+
use crate::arrays::FixedSizeBinaryArray;
7+
use crate::arrays::fixed_size_binary::FixedSizeBinaryArrayExt;
8+
9+
pub(super) fn check_fixed_size_binary_constant(array: &FixedSizeBinaryArray) -> bool {
10+
let byte_width = array.byte_width() as usize;
11+
if byte_width == 0 {
12+
return true;
13+
}
14+
15+
array
16+
.buffer_handle()
17+
.to_host_sync()
18+
.as_slice()
19+
.chunks_exact(byte_width)
20+
.all_equal()
21+
}

vortex-array/src/aggregate_fn/fns/is_constant/mod.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
mod bool;
55
mod decimal;
66
mod extension;
7+
mod fixed_size_binary;
78
mod fixed_size_list;
89
mod list;
910
pub mod primitive;
1011
mod struct_;
1112
mod varbin;
1213

13-
use itertools::Itertools;
1414
use vortex_error::VortexExpect;
1515
use vortex_error::VortexResult;
1616
use vortex_error::vortex_bail;
@@ -19,6 +19,7 @@ use vortex_session::registry::CachedId;
1919
use self::bool::check_bool_constant;
2020
use self::decimal::check_decimal_constant;
2121
use self::extension::check_extension_constant;
22+
use self::fixed_size_binary::check_fixed_size_binary_constant;
2223
use self::fixed_size_list::check_fixed_size_list_constant;
2324
use self::list::check_listview_constant;
2425
use self::primitive::check_primitive_constant;
@@ -36,7 +37,6 @@ use crate::aggregate_fn::DynAccumulator;
3637
use crate::aggregate_fn::EmptyOptions;
3738
use crate::arrays::Constant;
3839
use crate::arrays::Null;
39-
use crate::arrays::fixed_size_binary::FixedSizeBinaryArrayExt;
4040
use crate::builtins::ArrayBuiltins;
4141
use crate::dtype::DType;
4242
use crate::dtype::FieldNames;
@@ -399,15 +399,7 @@ impl AggregateFnVTable for IsConstant {
399399
let batch_is_constant = match c {
400400
Canonical::Primitive(a) => check_primitive_constant(a),
401401
Canonical::Decimal(a) => check_decimal_constant(a),
402-
Canonical::FixedSizeBinary(a) => {
403-
let byte_width = a.byte_width() as usize;
404-
let values = a.buffer_handle().to_host_sync();
405-
values
406-
.as_slice()
407-
.chunks_exact(byte_width.max(1))
408-
.map(|value| if byte_width == 0 { &[][..] } else { value })
409-
.all_equal()
410-
}
402+
Canonical::FixedSizeBinary(a) => check_fixed_size_binary_constant(a),
411403
Canonical::Bool(b) => check_bool_constant(b),
412404
Canonical::VarBinView(v) => check_varbinview_constant(v),
413405
Canonical::Struct(s) => check_struct_constant(s, ctx)?,
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_error::VortexResult;
5+
6+
use super::IsSortedIteratorExt;
7+
use crate::ExecutionCtx;
8+
use crate::arrays::FixedSizeBinaryArray;
9+
use crate::arrays::fixed_size_binary::FixedSizeBinaryArrayExt;
10+
11+
pub(super) fn check_fixed_size_binary_sorted(
12+
array: &FixedSizeBinaryArray,
13+
strict: bool,
14+
ctx: &mut ExecutionCtx,
15+
) -> VortexResult<bool> {
16+
let byte_width = array.byte_width() as usize;
17+
let values = array.buffer_handle().to_host_sync();
18+
let validity = array
19+
.as_ref()
20+
.validity()?
21+
.execute_mask(array.len(), ctx)?
22+
.to_bit_buffer();
23+
let iter = validity.iter().enumerate().map(|(index, is_valid)| {
24+
is_valid.then(|| {
25+
let start = index * byte_width;
26+
&values[start..start + byte_width]
27+
})
28+
});
29+
30+
Ok(if strict {
31+
iter.is_strict_sorted()
32+
} else {
33+
iter.is_sorted()
34+
})
35+
}

vortex-array/src/aggregate_fn/fns/is_sorted/mod.rs

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
mod bool;
55
mod decimal;
66
mod extension;
7+
mod fixed_size_binary;
78
mod primitive;
89
mod varbin;
910

@@ -13,12 +14,12 @@ use std::fmt::Formatter;
1314

1415
use vortex_error::VortexExpect;
1516
use vortex_error::VortexResult;
16-
use vortex_mask::Mask;
1717
use vortex_session::registry::CachedId;
1818

1919
use self::bool::check_bool_sorted;
2020
use self::decimal::check_decimal_sorted;
2121
use self::extension::check_extension_sorted;
22+
use self::fixed_size_binary::check_fixed_size_binary_sorted;
2223
use self::primitive::check_primitive_sorted;
2324
use self::varbin::check_varbinview_sorted;
2425
use crate::ArrayRef;
@@ -42,44 +43,6 @@ use crate::expr::stats::Stat;
4243
use crate::expr::stats::StatsProviderExt;
4344
use crate::scalar::Scalar;
4445

45-
fn check_fixed_size_binary_sorted(
46-
array: &crate::arrays::FixedSizeBinaryArray,
47-
strict: bool,
48-
ctx: &mut ExecutionCtx,
49-
) -> VortexResult<bool> {
50-
let DType::FixedSizeBinary(byte_width, _) = array.dtype() else {
51-
unreachable!()
52-
};
53-
let byte_width = *byte_width as usize;
54-
let values = array.buffer_handle().to_host_sync();
55-
let validity = array.as_ref().validity()?.execute_mask(array.len(), ctx)?;
56-
let mut valid = match &validity {
57-
Mask::AllTrue(len) => {
58-
Box::new(std::iter::repeat_n(true, *len)) as Box<dyn Iterator<Item = bool>>
59-
}
60-
Mask::AllFalse(len) => Box::new(std::iter::repeat_n(false, *len)),
61-
Mask::Values(values) => Box::new(values.bit_buffer().iter()),
62-
};
63-
let mut previous: Option<Option<&[u8]>> = None;
64-
for index in 0..array.len() {
65-
let current = valid.next().unwrap_or(false).then(|| {
66-
let start = index * byte_width;
67-
&values[start..start + byte_width]
68-
});
69-
if let Some(previous) = previous
70-
&& if strict {
71-
previous >= current
72-
} else {
73-
previous > current
74-
}
75-
{
76-
return Ok(false);
77-
}
78-
previous = Some(current);
79-
}
80-
Ok(true)
81-
}
82-
8346
/// Options for the `is_sorted` aggregate function.
8447
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
8548
pub struct IsSortedOptions {
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::ops::Not;
5+
6+
use vortex_buffer::ByteBuffer;
7+
use vortex_error::VortexExpect;
8+
use vortex_error::VortexResult;
9+
10+
use super::FixedSizeBinary;
11+
use super::FixedSizeBinaryArray;
12+
use super::FixedSizeBinaryArrayExt;
13+
use crate::ArrayRef;
14+
use crate::ExecutionCtx;
15+
use crate::IntoArray;
16+
use crate::array::ArrayView;
17+
use crate::array::OperationsVTable;
18+
use crate::array::ValidityVTable;
19+
use crate::arrays::BoolArray;
20+
use crate::arrays::Masked;
21+
use crate::arrays::fixed_width::FixedWidthArray;
22+
use crate::arrays::fixed_width::vtable as fixed_width;
23+
use crate::arrays::slice::SliceReduce;
24+
use crate::dtype::DType;
25+
use crate::optimizer::rules::ArrayParentReduceRule;
26+
use crate::scalar::Scalar;
27+
use crate::scalar_fn::fns::cast::CastKernel;
28+
use crate::scalar_fn::fns::cast::CastReduce;
29+
use crate::scalar_fn::fns::fill_null::FillNullKernel;
30+
use crate::scalar_fn::fns::mask::MaskReduce;
31+
use crate::validity::Validity;
32+
33+
#[derive(Default, Debug)]
34+
pub(super) struct FixedSizeBinaryMaskedValidityRule;
35+
36+
impl ArrayParentReduceRule<FixedSizeBinary> for FixedSizeBinaryMaskedValidityRule {
37+
type Parent = Masked;
38+
39+
fn reduce_parent(
40+
&self,
41+
array: ArrayView<'_, FixedSizeBinary>,
42+
parent: ArrayView<'_, Masked>,
43+
_child_idx: usize,
44+
) -> VortexResult<Option<ArrayRef>> {
45+
let validity = array.validity()?.and(parent.validity()?)?;
46+
Ok(Some(
47+
FixedSizeBinaryArray::try_new_handle(
48+
array.buffer_handle().clone(),
49+
array.byte_width(),
50+
array.len(),
51+
validity,
52+
)?
53+
.into_array(),
54+
))
55+
}
56+
}
57+
58+
impl OperationsVTable<FixedSizeBinary> for FixedSizeBinary {
59+
fn scalar_at(
60+
array: ArrayView<'_, FixedSizeBinary>,
61+
index: usize,
62+
_ctx: &mut ExecutionCtx,
63+
) -> VortexResult<Scalar> {
64+
let byte_width = array.byte_width() as usize;
65+
let values = array.buffer_handle().to_host_sync();
66+
let start = index * byte_width;
67+
Ok(Scalar::fixed_size_binary(
68+
values.slice(start..start + byte_width),
69+
array.dtype().nullability(),
70+
))
71+
}
72+
}
73+
74+
impl ValidityVTable<FixedSizeBinary> for FixedSizeBinary {
75+
fn validity(array: ArrayView<'_, FixedSizeBinary>) -> VortexResult<Validity> {
76+
fixed_width::validity(array)
77+
}
78+
}
79+
80+
impl CastReduce for FixedSizeBinary {
81+
fn cast(
82+
array: ArrayView<'_, FixedSizeBinary>,
83+
dtype: &DType,
84+
) -> VortexResult<Option<ArrayRef>> {
85+
let DType::FixedSizeBinary(byte_width, nullability) = dtype else {
86+
return Ok(None);
87+
};
88+
if *byte_width != array.byte_width() {
89+
return Ok(None);
90+
}
91+
let Some(validity) = array
92+
.validity()?
93+
.trivially_cast_nullability(*nullability, array.len())?
94+
else {
95+
return Ok(None);
96+
};
97+
Ok(Some(
98+
FixedSizeBinaryArray::try_new_handle(
99+
array.buffer_handle().clone(),
100+
*byte_width,
101+
array.len(),
102+
validity,
103+
)?
104+
.into_array(),
105+
))
106+
}
107+
}
108+
109+
impl CastKernel for FixedSizeBinary {
110+
fn cast(
111+
array: ArrayView<'_, FixedSizeBinary>,
112+
dtype: &DType,
113+
ctx: &mut ExecutionCtx,
114+
) -> VortexResult<Option<ArrayRef>> {
115+
let DType::FixedSizeBinary(byte_width, nullability) = dtype else {
116+
return Ok(None);
117+
};
118+
if *byte_width != array.byte_width() {
119+
return Ok(None);
120+
}
121+
let validity = array
122+
.validity()?
123+
.cast_nullability(*nullability, array.len(), ctx)?;
124+
Ok(Some(
125+
FixedSizeBinaryArray::try_new_handle(
126+
array.buffer_handle().clone(),
127+
*byte_width,
128+
array.len(),
129+
validity,
130+
)?
131+
.into_array(),
132+
))
133+
}
134+
}
135+
136+
impl FillNullKernel for FixedSizeBinary {
137+
fn fill_null(
138+
array: ArrayView<'_, FixedSizeBinary>,
139+
fill_value: &Scalar,
140+
ctx: &mut ExecutionCtx,
141+
) -> VortexResult<Option<ArrayRef>> {
142+
let result_validity = Validity::from(fill_value.dtype().nullability());
143+
let mut values = array.buffer_handle().to_host_sync().into_mut();
144+
let fill = fill_value
145+
.as_binary()
146+
.value()
147+
.vortex_expect("top-level fill_null ensure non-null fill value");
148+
let is_invalid = match array.validity()? {
149+
Validity::Array(is_valid) => {
150+
is_valid.execute::<BoolArray>(ctx)?.into_bit_buffer().not()
151+
}
152+
_ => unreachable!("checked in entry point"),
153+
};
154+
let byte_width = array.byte_width() as usize;
155+
is_invalid.for_each_set_index(|invalid_index| {
156+
let start = invalid_index * byte_width;
157+
values[start..start + byte_width].copy_from_slice(fill.as_slice());
158+
});
159+
Ok(Some(
160+
FixedSizeBinaryArray::new(
161+
values.freeze(),
162+
array.byte_width(),
163+
array.len(),
164+
result_validity,
165+
)
166+
.into_array(),
167+
))
168+
}
169+
}
170+
171+
impl MaskReduce for FixedSizeBinary {
172+
fn mask(
173+
array: ArrayView<'_, FixedSizeBinary>,
174+
mask: &ArrayRef,
175+
) -> VortexResult<Option<ArrayRef>> {
176+
Ok(Some(
177+
FixedSizeBinaryArray::try_new_handle(
178+
array.buffer_handle().clone(),
179+
array.byte_width(),
180+
array.len(),
181+
array.validity()?.and(Validity::Array(mask.clone()))?,
182+
)?
183+
.into_array(),
184+
))
185+
}
186+
}
187+
188+
impl SliceReduce for FixedSizeBinary {
189+
fn slice(
190+
array: ArrayView<'_, FixedSizeBinary>,
191+
range: std::ops::Range<usize>,
192+
) -> VortexResult<Option<ArrayRef>> {
193+
let byte_width = array.byte_width();
194+
let width = byte_width as usize;
195+
let values = array
196+
.buffer_handle()
197+
.slice(range.start * width..range.end * width);
198+
let len = range.len();
199+
let validity = array.validity()?.slice(range)?;
200+
Ok(Some(
201+
FixedSizeBinaryArray::try_new_handle(values, byte_width, len, validity)?.into_array(),
202+
))
203+
}
204+
}
205+
206+
impl FixedWidthArray for FixedSizeBinary {
207+
fn byte_width(array: ArrayView<'_, Self>) -> usize {
208+
array.byte_width() as usize
209+
}
210+
211+
fn values(array: ArrayView<'_, Self>) -> ByteBuffer {
212+
array.buffer_handle().to_host_sync()
213+
}
214+
215+
fn with_values(
216+
array: ArrayView<'_, Self>,
217+
values: ByteBuffer,
218+
len: usize,
219+
validity: Validity,
220+
) -> VortexResult<FixedSizeBinaryArray> {
221+
FixedSizeBinaryArray::try_new(values, array.byte_width(), len, validity)
222+
}
223+
}

0 commit comments

Comments
 (0)