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