vortex_array/arrays/varbinview/compute/
take.rs1use std::iter;
5use std::sync::Arc;
6
7use itertools::Itertools as _;
8use num_traits::AsPrimitive;
9use vortex_buffer::Buffer;
10use vortex_buffer::BufferMut;
11use vortex_error::VortexResult;
12use vortex_error::vortex_ensure;
13use vortex_error::vortex_err;
14use vortex_mask::AllOr;
15use vortex_mask::Mask;
16
17use crate::ArrayRef;
18use crate::Columnar;
19use crate::IntoArray;
20use crate::array::ArrayView;
21use crate::arrays::PiecewiseSequence;
22use crate::arrays::PrimitiveArray;
23use crate::arrays::VarBinView;
24use crate::arrays::VarBinViewArray;
25use crate::arrays::dict::TakeExecute;
26use crate::arrays::piecewise_sequence::constant_unsigned_usize;
27use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
28use crate::arrays::varbinview::BinaryView;
29use crate::buffer::BufferHandle;
30use crate::dtype::UnsignedPType;
31use crate::executor::ExecutionCtx;
32use crate::match_each_integer_ptype;
33use crate::match_each_unsigned_integer_ptype;
34
35impl TakeExecute for VarBinView {
36 fn take(
38 array: ArrayView<'_, VarBinView>,
39 indices: &ArrayRef,
40 ctx: &mut ExecutionCtx,
41 ) -> VortexResult<Option<ArrayRef>> {
42 if let Some(piecewise_indices) = indices.as_opt::<PiecewiseSequence>()
43 && let Some(taken) = take_contiguous_ranges(array, piecewise_indices, indices, ctx)?
44 {
45 return Ok(Some(taken));
46 }
47
48 let validity = array.validity()?.take(indices)?;
49 let indices = indices.clone().execute::<PrimitiveArray>(ctx)?;
50
51 let indices_mask = indices
52 .as_ref()
53 .validity()?
54 .execute_mask(indices.as_ref().len(), ctx)?;
55 let views_buffer = match_each_integer_ptype!(indices.ptype(), |I| {
56 take_views(array.views(), indices.as_slice::<I>(), &indices_mask)
57 });
58
59 unsafe {
61 Ok(Some(
62 VarBinViewArray::new_handle_unchecked(
63 BufferHandle::new_host(views_buffer.into_byte_buffer()),
64 Arc::clone(array.data_buffers()),
65 array
66 .dtype()
67 .union_nullability(indices.dtype().nullability()),
68 validity,
69 )
70 .into_array(),
71 ))
72 }
73 }
74}
75
76fn take_contiguous_ranges(
77 array: ArrayView<'_, VarBinView>,
78 indices: ArrayView<'_, PiecewiseSequence>,
79 indices_ref: &ArrayRef,
80 ctx: &mut ExecutionCtx,
81) -> VortexResult<Option<ArrayRef>> {
82 let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
83 return Ok(None);
84 };
85 let source = array.views();
86 let output_len = indices_ref.len();
87 let views = match lengths {
88 Columnar::Constant(lengths) => {
89 let length = constant_unsigned_usize(&lengths);
90 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
91 gather_view_slices_constant_length(
92 source,
93 starts.as_slice::<S>(),
94 length,
95 output_len,
96 )?
97 })
98 }
99 Columnar::Canonical(lengths) => {
100 let lengths = lengths.into_primitive();
101 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
102 match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
103 gather_view_slices(
104 source,
105 starts.as_slice::<S>(),
106 lengths.as_slice::<L>(),
107 output_len,
108 )?
109 })
110 })
111 }
112 };
113 let validity = array.validity()?.take(indices_ref)?;
114
115 unsafe {
118 Ok(Some(
119 VarBinViewArray::new_handle_unchecked(
120 BufferHandle::new_host(views.into_byte_buffer()),
121 Arc::clone(array.data_buffers()),
122 array.dtype().clone(),
123 validity,
124 )
125 .into_array(),
126 ))
127 }
128}
129
130fn take_views<I: AsPrimitive<usize>>(
131 views_ref: &[BinaryView],
132 indices: &[I],
133 mask: &Mask,
134) -> Buffer<BinaryView> {
135 match mask.bit_buffer() {
139 AllOr::All => {
140 Buffer::<BinaryView>::from_trusted_len_iter(indices.iter().map(|i| views_ref[i.as_()]))
141 }
142 AllOr::None => Buffer::<BinaryView>::from_trusted_len_iter(iter::repeat_n(
143 BinaryView::default(),
144 indices.len(),
145 )),
146 AllOr::Some(buffer) => Buffer::<BinaryView>::from_trusted_len_iter(
147 buffer.iter().zip(indices.iter()).map(|(valid, idx)| {
148 if valid {
149 views_ref[idx.as_()]
150 } else {
151 BinaryView::default()
152 }
153 }),
154 ),
155 }
156}
157
158fn gather_view_slices_constant_length<S>(
159 source: &[BinaryView],
160 starts: &[S],
161 length: usize,
162 output_len: usize,
163) -> VortexResult<Buffer<BinaryView>>
164where
165 S: UnsignedPType,
166{
167 let computed_len = starts
168 .len()
169 .checked_mul(length)
170 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
171 vortex_ensure!(
172 computed_len == output_len,
173 "PiecewiseSequenceArray expanded length {computed_len} does not match declared length {output_len}"
174 );
175
176 let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
177 let spare = &mut views.spare_capacity_mut()[..output_len];
178 let mut cursor = 0usize;
179 for &start in starts {
180 let start = start.as_();
181 let src = &source[start..][..length];
182 spare[cursor..][..src.len()].write_copy_of_slice(src);
183 cursor += src.len();
184 }
185 unsafe { views.set_len(cursor) };
187 vortex_ensure!(
188 views.len() == output_len,
189 "PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
190 views.len()
191 );
192 Ok(views.freeze())
193}
194
195fn gather_view_slices<S, L>(
196 source: &[BinaryView],
197 starts: &[S],
198 lengths: &[L],
199 output_len: usize,
200) -> VortexResult<Buffer<BinaryView>>
201where
202 S: UnsignedPType,
203 L: UnsignedPType,
204{
205 let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
206 let spare = &mut views.spare_capacity_mut()[..output_len];
207 let mut cursor = 0usize;
208 for (&start, &length) in starts.iter().zip_eq(lengths) {
209 let start = start.as_();
210 let length = length.as_();
211 let src = &source[start..][..length];
212 spare[cursor..][..src.len()].write_copy_of_slice(src);
213 cursor += src.len();
214 }
215 unsafe { views.set_len(cursor) };
217 vortex_ensure!(
218 views.len() == output_len,
219 "PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
220 views.len()
221 );
222 Ok(views.freeze())
223}
224
225#[cfg(test)]
226mod tests {
227 use rstest::rstest;
228 use vortex_buffer::BitBuffer;
229 use vortex_buffer::buffer;
230 use vortex_error::VortexResult;
231
232 use crate::IntoArray;
233 use crate::VortexSessionExecute;
234 use crate::array_session;
235 use crate::arrays::VarBinViewArray;
236 use crate::arrays::varbinview::compute::take::PrimitiveArray;
237 use crate::compute::conformance::take::test_take_conformance;
238 use crate::dtype::DType;
239 use crate::dtype::Nullability::NonNullable;
240 use crate::validity::Validity;
241
242 #[test]
243 fn take_nullable() -> VortexResult<()> {
244 let arr = VarBinViewArray::from_iter_nullable_str([
245 Some("one"),
246 None,
247 Some("three"),
248 Some("four"),
249 None,
250 Some("six"),
251 ]);
252
253 let taken = arr.take(buffer![0, 3].into_array())?;
254
255 assert!(taken.dtype().is_nullable());
256 let mut ctx = array_session().create_execution_ctx();
257 let taken = taken.execute::<VarBinViewArray>(&mut ctx)?;
258 let mask = taken.validity()?.execute_mask(taken.len(), &mut ctx)?;
259 let result = (0..taken.len())
260 .map(|i| {
261 mask.value(i)
262 .then(|| unsafe { String::from_utf8_unchecked(taken.bytes_at(i).to_vec()) })
263 })
264 .collect::<Vec<_>>();
265 assert_eq!(result, [Some("one".to_string()), Some("four".to_string())]);
266 Ok(())
267 }
268
269 #[test]
270 fn take_nullable_indices() -> VortexResult<()> {
271 let arr = VarBinViewArray::from_iter(["one", "two"].map(Some), DType::Utf8(NonNullable));
272
273 let indices = PrimitiveArray::new(
274 buffer![1u64, 999],
276 Validity::from(BitBuffer::from(vec![true, false])),
277 );
278
279 let taken = arr.take(indices.into_array())?;
280
281 assert!(taken.dtype().is_nullable());
282 let mut ctx = array_session().create_execution_ctx();
283 let taken = taken.execute::<VarBinViewArray>(&mut ctx)?;
284 let mask = taken.validity()?.execute_mask(taken.len(), &mut ctx)?;
285 let result = (0..taken.len())
286 .map(|i| {
287 mask.value(i)
288 .then(|| unsafe { String::from_utf8_unchecked(taken.bytes_at(i).to_vec()) })
289 })
290 .collect::<Vec<_>>();
291 assert_eq!(result, [Some("two".to_string()), None]);
292 Ok(())
293 }
294
295 #[rstest]
296 #[case(VarBinViewArray::from_iter(
297 ["hello", "world", "test", "data", "array"].map(Some),
298 DType::Utf8(NonNullable),
299 ))]
300 #[case(VarBinViewArray::from_iter_nullable_str([
301 Some("hello"),
302 None,
303 Some("test"),
304 Some("data"),
305 None,
306 ]))]
307 #[case(VarBinViewArray::from_iter(
308 [b"hello".as_slice(), b"world", b"test", b"data", b"array"].map(Some),
309 DType::Binary(NonNullable),
310 ))]
311 #[case(VarBinViewArray::from_iter(["single"].map(Some), DType::Utf8(NonNullable)))]
312 fn test_take_varbinview_conformance(#[case] array: VarBinViewArray) {
313 test_take_conformance(
314 &array.into_array(),
315 &mut array_session().create_execution_ctx(),
316 );
317 }
318}