1use std::ptr;
5
6use itertools::Itertools as _;
7use vortex_buffer::Buffer;
8use vortex_buffer::BufferMut;
9use vortex_error::VortexResult;
10use vortex_error::vortex_ensure;
11use vortex_error::vortex_err;
12
13use crate::ArrayRef;
14use crate::Columnar;
15use crate::IntoArray;
16use crate::array::ArrayView;
17use crate::arrays::Decimal;
18use crate::arrays::DecimalArray;
19use crate::arrays::PiecewiseSequence;
20use crate::arrays::PrimitiveArray;
21use crate::arrays::dict::TakeExecute;
22use crate::arrays::piecewise_sequence::constant_unsigned_usize;
23use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
24use crate::dtype::IntegerPType;
25use crate::dtype::NativeDecimalType;
26use crate::dtype::UnsignedPType;
27use crate::executor::ExecutionCtx;
28use crate::match_each_decimal_value_type;
29use crate::match_each_integer_ptype;
30use crate::match_each_unsigned_integer_ptype;
31use crate::validity::Validity;
32
33impl TakeExecute for Decimal {
34 fn take(
35 array: ArrayView<'_, Decimal>,
36 indices: &ArrayRef,
37 ctx: &mut ExecutionCtx,
38 ) -> VortexResult<Option<ArrayRef>> {
39 if let Some(piecewise_indices) = indices.as_opt::<PiecewiseSequence>()
40 && let Some(taken) = take_contiguous_ranges(array, piecewise_indices, indices, ctx)?
41 {
42 return Ok(Some(taken));
43 }
44
45 let indices = indices.clone().execute::<PrimitiveArray>(ctx)?;
46 let validity = array.validity()?.take(&indices.clone().into_array())?;
47
48 let decimal = match_each_decimal_value_type!(array.values_type(), |D| {
51 match_each_integer_ptype!(indices.ptype(), |I| {
52 let buffer =
53 take_to_buffer::<I, D>(indices.as_slice::<I>(), array.buffer::<D>().as_slice());
54 unsafe { DecimalArray::new_unchecked(buffer, array.decimal_dtype(), validity) }
57 })
58 });
59
60 Ok(Some(decimal.into_array()))
61 }
62}
63
64fn take_contiguous_ranges(
65 array: ArrayView<'_, Decimal>,
66 indices: ArrayView<'_, PiecewiseSequence>,
67 indices_ref: &ArrayRef,
68 ctx: &mut ExecutionCtx,
69) -> VortexResult<Option<ArrayRef>> {
70 let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
71 return Ok(None);
72 };
73 let validity = array.validity()?.take(indices_ref)?;
74 let output_len = indices_ref.len();
75 let taken = match lengths {
76 Columnar::Constant(lengths) => {
77 let length = constant_unsigned_usize(&lengths);
78 take_slices_constant_length(array, &starts, length, validity, output_len)?
79 }
80 Columnar::Canonical(lengths) => {
81 let lengths = lengths.into_primitive();
82 take_slices(array, &starts, &lengths, validity, output_len)?
83 }
84 };
85 Ok(Some(taken))
86}
87
88fn take_slices_constant_length(
89 array: ArrayView<'_, Decimal>,
90 starts: &PrimitiveArray,
91 length: usize,
92 validity: Validity,
93 output_len: usize,
94) -> VortexResult<ArrayRef> {
95 match_each_decimal_value_type!(array.values_type(), |D| {
96 take_slices_constant_length_typed::<D>(array, starts, length, validity, output_len)
97 })
98}
99
100fn take_slices_constant_length_typed<D>(
101 array: ArrayView<'_, Decimal>,
102 starts: &PrimitiveArray,
103 length: usize,
104 validity: Validity,
105 output_len: usize,
106) -> VortexResult<ArrayRef>
107where
108 D: NativeDecimalType,
109{
110 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
111 let values = take_slices_constant_length_to_buffer::<S, D>(
112 starts.as_slice::<S>(),
113 length,
114 array.buffer::<D>().as_slice(),
115 output_len,
116 )?;
117
118 Ok(
120 unsafe { DecimalArray::new_unchecked(values, array.decimal_dtype(), validity) }
121 .into_array(),
122 )
123 })
124}
125
126fn take_slices(
127 array: ArrayView<'_, Decimal>,
128 starts: &PrimitiveArray,
129 lengths: &PrimitiveArray,
130 validity: Validity,
131 output_len: usize,
132) -> VortexResult<ArrayRef> {
133 match_each_decimal_value_type!(array.values_type(), |D| {
134 take_slices_typed::<D>(array, starts, lengths, validity, output_len)
135 })
136}
137
138fn take_slices_typed<D>(
139 array: ArrayView<'_, Decimal>,
140 starts: &PrimitiveArray,
141 lengths: &PrimitiveArray,
142 validity: Validity,
143 output_len: usize,
144) -> VortexResult<ArrayRef>
145where
146 D: NativeDecimalType,
147{
148 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
149 take_slices_start_typed::<D, S>(array, starts, lengths, validity, output_len)
150 })
151}
152
153fn take_slices_start_typed<D, S>(
154 array: ArrayView<'_, Decimal>,
155 starts: &PrimitiveArray,
156 lengths: &PrimitiveArray,
157 validity: Validity,
158 output_len: usize,
159) -> VortexResult<ArrayRef>
160where
161 D: NativeDecimalType,
162 S: UnsignedPType,
163{
164 match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
165 let values = take_slices_to_buffer::<S, L, D>(
166 starts.as_slice::<S>(),
167 lengths.as_slice::<L>(),
168 array.buffer::<D>().as_slice(),
169 output_len,
170 )?;
171
172 Ok(
174 unsafe { DecimalArray::new_unchecked(values, array.decimal_dtype(), validity) }
175 .into_array(),
176 )
177 })
178}
179
180fn take_to_buffer<I: IntegerPType, T: NativeDecimalType>(indices: &[I], values: &[T]) -> Buffer<T> {
181 indices.iter().map(|idx| values[idx.as_()]).collect()
182}
183
184fn take_slices_constant_length_to_buffer<S, T>(
185 starts: &[S],
186 length: usize,
187 values: &[T],
188 output_len: usize,
189) -> VortexResult<Buffer<T>>
190where
191 S: UnsignedPType,
192 T: NativeDecimalType,
193{
194 let computed_len = starts
195 .len()
196 .checked_mul(length)
197 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
198 vortex_ensure!(
199 computed_len == output_len,
200 "PiecewiseSequenceArray expanded length {computed_len} does not match declared length {output_len}"
201 );
202
203 let mut result = BufferMut::<T>::with_capacity(output_len);
204 let spare = &mut result.spare_capacity_mut()[..output_len];
205 let mut cursor = 0usize;
206 for &start in starts {
207 let start = start.as_();
208 let src = &values[start..][..length];
209 unsafe {
211 ptr::copy_nonoverlapping(
212 src.as_ptr(),
213 spare[cursor..][..src.len()].as_mut_ptr().cast::<T>(),
214 src.len(),
215 );
216 }
217 cursor += src.len();
218 }
219 unsafe { result.set_len(cursor) };
221 vortex_ensure!(
222 result.len() == output_len,
223 "PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
224 result.len()
225 );
226 Ok(result.freeze())
227}
228
229fn take_slices_to_buffer<S, L, T>(
230 starts: &[S],
231 lengths: &[L],
232 values: &[T],
233 output_len: usize,
234) -> VortexResult<Buffer<T>>
235where
236 S: UnsignedPType,
237 L: UnsignedPType,
238 T: NativeDecimalType,
239{
240 let mut result = BufferMut::<T>::with_capacity(output_len);
241 let spare = &mut result.spare_capacity_mut()[..output_len];
242 let mut cursor = 0usize;
243 for (&start, &length) in starts.iter().zip_eq(lengths) {
244 let start = start.as_();
245 let length = length.as_();
246 let src = &values[start..][..length];
247 unsafe {
249 ptr::copy_nonoverlapping(
250 src.as_ptr(),
251 spare[cursor..][..src.len()].as_mut_ptr().cast::<T>(),
252 src.len(),
253 );
254 }
255 cursor += src.len();
256 }
257 unsafe { result.set_len(cursor) };
259 vortex_ensure!(
260 result.len() == output_len,
261 "PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
262 result.len()
263 );
264 Ok(result.freeze())
265}
266
267#[cfg(test)]
268mod tests {
269 use rstest::rstest;
270 use vortex_buffer::Buffer;
271 use vortex_buffer::buffer;
272
273 use crate::IntoArray;
274 use crate::VortexSessionExecute;
275 use crate::array_session;
276 use crate::arrays::DecimalArray;
277 use crate::arrays::PrimitiveArray;
278 use crate::assert_arrays_eq;
279 use crate::compute::conformance::take::test_take_conformance;
280 use crate::dtype::DecimalDType;
281 use crate::validity::Validity;
282
283 #[test]
284 fn test_take() {
285 let mut ctx = array_session().create_execution_ctx();
286 let ddtype = DecimalDType::new(19, 1);
287 let array = DecimalArray::new(
288 buffer![10i128, 11i128, 12i128, 13i128],
289 ddtype,
290 Validity::NonNullable,
291 );
292
293 let indices = buffer![0, 2, 3].into_array();
294 let taken = array.take(indices).unwrap();
295
296 let expected = DecimalArray::from_iter([10i128, 12, 13], ddtype);
297 assert_arrays_eq!(expected, taken, &mut ctx);
298 }
299
300 #[test]
301 fn test_take_null_indices() {
302 let mut ctx = array_session().create_execution_ctx();
303 let ddtype = DecimalDType::new(19, 1);
304 let array = DecimalArray::new(
305 buffer![i128::MAX, 11i128, 12i128, 13i128],
306 ddtype,
307 Validity::NonNullable,
308 );
309
310 let indices = PrimitiveArray::from_option_iter([None, Some(2), Some(3)]).into_array();
311 let taken = array.take(indices).unwrap();
312
313 let expected = DecimalArray::from_option_iter([None, Some(12i128), Some(13)], ddtype);
314 assert_arrays_eq!(expected, taken, &mut ctx);
315 }
316
317 #[rstest]
318 #[case(DecimalArray::new(
319 buffer![100i128, 200i128, 300i128, 400i128, 500i128],
320 DecimalDType::new(19, 2),
321 Validity::NonNullable,
322 ))]
323 #[case(DecimalArray::new(
324 buffer![10i64, 20i64, 30i64, 40i64, 50i64],
325 DecimalDType::new(10, 1),
326 Validity::NonNullable,
327 ))]
328 #[case(DecimalArray::new(
329 buffer![1i32, 2i32, 3i32, 4i32, 5i32],
330 DecimalDType::new(5, 0),
331 Validity::NonNullable,
332 ))]
333 #[case(DecimalArray::new(
334 buffer![1000i128, 2000i128, 3000i128, 4000i128, 5000i128],
335 DecimalDType::new(19, 3),
336 Validity::from_iter([true, false, true, true, false]),
337 ))]
338 #[case(DecimalArray::new(
339 buffer![42i128],
340 DecimalDType::new(19, 0),
341 Validity::NonNullable,
342 ))]
343 #[case({
344 let values: Vec<i128> = (0..100).map(|i| i * 1000).collect();
345 DecimalArray::new(
346 Buffer::from_iter(values),
347 DecimalDType::new(19, 4),
348 Validity::NonNullable,
349 )
350 })]
351 fn test_take_decimal_conformance(#[case] array: DecimalArray) {
352 test_take_conformance(
353 &array.into_array(),
354 &mut array_session().create_execution_ctx(),
355 );
356 }
357}