vortex_array/arrays/fixed_width/take/
mod.rs1#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
5mod avx2;
6mod records;
7mod scalar;
8mod slices;
9#[cfg(test)]
10mod tests;
11
12#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
13use std::sync::LazyLock;
14
15use vortex_buffer::Buffer;
16use vortex_error::VortexResult;
17use vortex_error::vortex_bail;
18use vortex_mask::Mask;
19
20use self::records::take_byte_records;
21use self::scalar::take_values_scalar;
22use self::slices::take_slices;
23use self::slices::take_slices_constant_length;
24use super::FixedWidthArray;
25use super::with_values;
26use crate::ArrayRef;
27use crate::Columnar;
28use crate::ExecutionCtx;
29use crate::IntoArray;
30use crate::array::ArrayView;
31use crate::arrays::ConstantArray;
32use crate::arrays::PiecewiseSequence;
33use crate::arrays::PrimitiveArray;
34use crate::arrays::dict::TakeExecute;
35use crate::arrays::piecewise_sequence::constant_unsigned_usize;
36use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
37use crate::builtins::ArrayBuiltins;
38use crate::dtype::DType;
39use crate::dtype::UnsignedPType;
40use crate::dtype::half::f16;
41use crate::match_each_unsigned_integer_ptype;
42use crate::scalar::Scalar;
43
44#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
45static HAS_AVX2: LazyLock<bool> = LazyLock::new(|| is_x86_feature_detected!("avx2"));
46
47impl<V: FixedWidthArray> TakeExecute for V {
48 fn take(
49 array: ArrayView<'_, Self>,
50 indices: &ArrayRef,
51 ctx: &mut ExecutionCtx,
52 ) -> VortexResult<Option<ArrayRef>> {
53 take(array, indices, ctx)
54 }
55}
56
57pub(crate) unsafe trait FixedWidthTakeValue: Copy {}
64
65macro_rules! impl_fixed_width_take_value {
66 ($($ty:ty),+ $(,)?) => {
67 $(
68 unsafe impl FixedWidthTakeValue for $ty {}
70 )+
71 };
72}
73
74impl_fixed_width_take_value!(u8, u16, u32, u64, i8, i16, i32, i64, f16, f32, f64,);
75
76unsafe impl<const N: usize> FixedWidthTakeValue for [u8; N] {}
78
79pub(crate) fn take_values<T: FixedWidthTakeValue, I: UnsignedPType>(
80 values: &[T],
81 indices: &[I],
82) -> Buffer<T> {
83 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
84 if *HAS_AVX2 {
85 return unsafe { avx2::take_avx2(values, indices) };
89 }
90
91 take_values_scalar(values, indices)
92}
93
94pub(crate) fn take<V: FixedWidthArray>(
95 array: ArrayView<'_, V>,
96 indices: &ArrayRef,
97 ctx: &mut ExecutionCtx,
98) -> VortexResult<Option<ArrayRef>> {
99 if let Some(piecewise_indices) = indices.as_opt::<PiecewiseSequence>()
100 && let Some(taken) = take_contiguous_ranges(array, piecewise_indices, indices, ctx)?
101 {
102 return Ok(Some(taken));
103 }
104
105 let DType::Primitive(ptype, nullability) = indices.dtype() else {
106 vortex_bail!("Invalid indices dtype: {}", indices.dtype())
107 };
108 if !ptype.is_int() {
109 vortex_bail!("Invalid indices dtype: {}", indices.dtype())
110 }
111
112 let indices_validity = indices.validity()?;
113 let indices_nulls_zeroed = match indices_validity.execute_mask(indices.len(), ctx)? {
114 Mask::AllTrue(_) => indices.clone(),
115 Mask::AllFalse(_) => {
116 return Ok(Some(
117 ConstantArray::new(Scalar::null(array.dtype().as_nullable()), indices.len())
118 .into_array(),
119 ));
120 }
121 Mask::Values(_) => indices
122 .clone()
123 .fill_null(Scalar::from(0).cast(indices.dtype())?)?,
124 };
125
126 let indices = if ptype.is_unsigned_int() {
127 indices_nulls_zeroed.execute::<PrimitiveArray>(ctx)?
128 } else {
129 indices_nulls_zeroed
130 .cast(DType::Primitive(ptype.to_unsigned(), *nullability))?
131 .execute::<PrimitiveArray>(ctx)?
132 };
133 let validity = array
134 .validity()?
135 .take(&indices.clone().into_array())?
136 .and(indices_validity)?;
137
138 let source = V::values(array);
139 let values = match_each_unsigned_integer_ptype!(indices.ptype(), |I| {
140 take_byte_records(
141 &source,
142 V::byte_width(array),
143 array.len(),
144 indices.as_slice::<I>(),
145 )
146 })?;
147 Ok(Some(
148 with_values(array, values, indices.len(), validity)?.into_array(),
149 ))
150}
151
152fn take_contiguous_ranges<V: FixedWidthArray>(
153 array: ArrayView<'_, V>,
154 indices: ArrayView<'_, PiecewiseSequence>,
155 indices_ref: &ArrayRef,
156 ctx: &mut ExecutionCtx,
157) -> VortexResult<Option<ArrayRef>> {
158 let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
159 return Ok(None);
160 };
161
162 let values = V::values(array);
163 let byte_width = V::byte_width(array);
164 let output_len = indices_ref.len();
165 let taken = match lengths {
166 Columnar::Constant(lengths) => {
167 let length = constant_unsigned_usize(&lengths);
168 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
169 take_slices_constant_length(
170 &values,
171 byte_width,
172 array.len(),
173 starts.as_slice::<S>(),
174 length,
175 output_len,
176 )
177 })
178 }
179 Columnar::Canonical(lengths) => {
180 let lengths = lengths.into_primitive();
181 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
182 match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
183 take_slices(
184 &values,
185 byte_width,
186 array.len(),
187 starts.as_slice::<S>(),
188 lengths.as_slice::<L>(),
189 output_len,
190 )
191 })
192 })
193 }
194 }?;
195 let validity = array.validity()?.take(indices_ref)?;
196 Ok(Some(
197 with_values(array, taken, output_len, validity)?.into_array(),
198 ))
199}