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