vortex_array/arrays/piecewise_sequence/
mod.rs1use itertools::Itertools;
12use num_traits::AsPrimitive;
13use vortex_buffer::BufferMut;
14use vortex_error::VortexExpect;
15use vortex_error::VortexResult;
16use vortex_error::vortex_bail;
17use vortex_error::vortex_ensure;
18use vortex_error::vortex_err;
19
20use crate::ArrayRef;
21use crate::Columnar;
22use crate::array::ArrayView;
23use crate::arrays::ConstantArray;
24use crate::arrays::PrimitiveArray;
25use crate::arrays::piecewise_sequence::array::PiecewiseSequenceArraySlotsExt;
26use crate::dtype::UnsignedPType;
27use crate::executor::ExecutionCtx;
28use crate::scalar::PValue;
29
30pub mod array;
31mod vtable;
32
33#[cfg(test)]
34mod tests;
35
36pub use array::PiecewiseSequenceArrayExt;
37pub use vtable::*;
38
39pub(crate) fn check_index_arrays(
40 starts: &ArrayRef,
41 lengths: &ArrayRef,
42 multipliers: &ArrayRef,
43) -> VortexResult<()> {
44 check_index_array("starts", starts)?;
45 check_index_array("lengths", lengths)?;
46 check_index_array("multipliers", multipliers)?;
47 vortex_ensure!(
48 starts.len() == lengths.len(),
49 "PiecewiseSequenceArray starts length {} does not match lengths length {}",
50 starts.len(),
51 lengths.len()
52 );
53 vortex_ensure!(
54 starts.len() == multipliers.len(),
55 "PiecewiseSequenceArray starts length {} does not match multipliers length {}",
56 starts.len(),
57 multipliers.len()
58 );
59 Ok(())
60}
61
62pub(crate) fn execute_index_arrays(
63 array: ArrayView<'_, PiecewiseSequence>,
64 ctx: &mut ExecutionCtx,
65) -> VortexResult<(PrimitiveArray, PrimitiveArray, PrimitiveArray)> {
66 let starts = array.starts().clone().execute::<PrimitiveArray>(ctx)?;
67 let lengths = array.lengths().clone().execute::<PrimitiveArray>(ctx)?;
68 let multipliers = array.multipliers().clone().execute::<PrimitiveArray>(ctx)?;
69 Ok((starts, lengths, multipliers))
70}
71
72pub(crate) fn maybe_contiguous_slices(
73 array: ArrayView<'_, PiecewiseSequence>,
74 ctx: &mut ExecutionCtx,
75) -> VortexResult<Option<(PrimitiveArray, Columnar)>> {
76 if !is_constant_one(array.multipliers()) {
77 return Ok(None);
78 }
79
80 let starts = array.starts().clone().execute::<PrimitiveArray>(ctx)?;
81 let lengths = array.lengths().clone().execute::<Columnar>(ctx)?;
82 Ok(Some((starts, lengths)))
83}
84
85pub(crate) fn is_constant_one(multipliers: &ArrayRef) -> bool {
86 let Some(scalar) = multipliers.as_constant() else {
87 return false;
88 };
89 matches!(
90 scalar.as_primitive_opt().and_then(|scalar| scalar.pvalue()),
91 Some(PValue::U8(1) | PValue::U16(1) | PValue::U32(1) | PValue::U64(1))
92 )
93}
94
95pub(crate) fn constant_unsigned_usize(array: &ConstantArray) -> usize {
96 let pvalue = array
97 .scalar()
98 .as_primitive_opt()
99 .and_then(|scalar| scalar.pvalue())
100 .vortex_expect("validated PiecewiseSequence length constants are primitive");
101
102 match pvalue {
103 PValue::U8(value) => value as usize,
104 PValue::U16(value) => value as usize,
105 PValue::U32(value) => value as usize,
106 PValue::U64(value) => value.as_(),
107 _ => unreachable!("validated PiecewiseSequence length constants are unsigned"),
108 }
109}
110
111fn check_index_array(name: &str, array: &ArrayRef) -> VortexResult<()> {
112 vortex_ensure!(
113 array.dtype().is_unsigned_int(),
114 "PiecewiseSequenceArray {name} must have unsigned integer dtype, got {}",
115 array.dtype()
116 );
117 vortex_ensure!(
118 !array.dtype().is_nullable(),
119 "PiecewiseSequenceArray {name} must be non-nullable, got {}",
120 array.dtype()
121 );
122 Ok(())
123}
124
125pub(crate) fn materialize_ranges<S, L, M>(
126 starts: &PrimitiveArray,
127 lengths: &PrimitiveArray,
128 multipliers: &PrimitiveArray,
129 output_len: usize,
130) -> VortexResult<BufferMut<u64>>
131where
132 S: UnsignedPType,
133 L: UnsignedPType,
134 M: UnsignedPType,
135{
136 let starts = starts.as_slice::<S>();
137 let lengths = lengths.as_slice::<L>();
138 let multipliers = multipliers.as_slice::<M>();
139 let mut values = BufferMut::with_capacity(output_len);
140 let mut computed_len = 0usize;
141
142 for ((&start, &length), &multiplier) in starts.iter().zip_eq(lengths).zip_eq(multipliers) {
143 let start: usize = start.as_();
144 let length: usize = length.as_();
145 let multiplier: usize = multiplier.as_();
146 if length != 0 {
147 let last_offset = length - 1;
148 let last_delta = last_offset
149 .checked_mul(multiplier)
150 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray range overflows usize"))?;
151 start
152 .checked_add(last_delta)
153 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray range overflows usize"))?;
154 }
155 computed_len = computed_len
156 .checked_add(length)
157 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
158
159 values.extend((0..length).map(|offset| (start + offset * multiplier) as u64));
160 }
161
162 if computed_len != output_len {
163 vortex_bail!(
164 "PiecewiseSequenceArray expanded length {computed_len} does not match declared length {output_len}"
165 );
166 }
167 Ok(values)
168}