1mod compute;
5mod serde;
6
7use std::sync::Arc;
8
9use itertools::Itertools;
10use num_traits::{AsPrimitive, PrimInt};
11use vortex_dtype::{DType, NativePType, match_each_native_ptype};
12use vortex_error::{VortexResult, VortexUnwrap, vortex_bail};
13use vortex_scalar::Scalar;
14
15use crate::arrays::PrimitiveVTable;
16#[cfg(feature = "test-harness")]
17use crate::builders::{ArrayBuilder, ListBuilder};
18use crate::compute::sub_scalar;
19use crate::stats::{ArrayStats, StatsSetRef};
20use crate::validity::Validity;
21use crate::vtable::{
22 ArrayVTable, CanonicalVTable, NotSupported, OperationsVTable, VTable, ValidityHelper,
23 ValidityVTableFromValidityHelper,
24};
25use crate::{Array, ArrayRef, Canonical, EncodingId, EncodingRef, IntoArray, vtable};
26
27vtable!(List);
28
29impl VTable for ListVTable {
30 type Array = ListArray;
31 type Encoding = ListEncoding;
32
33 type ArrayVTable = Self;
34 type CanonicalVTable = Self;
35 type OperationsVTable = Self;
36 type ValidityVTable = ValidityVTableFromValidityHelper;
37 type VisitorVTable = Self;
38 type ComputeVTable = NotSupported;
39 type EncodeVTable = NotSupported;
40 type SerdeVTable = Self;
41
42 fn id(_encoding: &Self::Encoding) -> EncodingId {
43 EncodingId::new_ref("vortex.list")
44 }
45
46 fn encoding(_array: &Self::Array) -> EncodingRef {
47 EncodingRef::new_ref(ListEncoding.as_ref())
48 }
49}
50
51#[derive(Clone, Debug)]
103pub struct ListArray {
104 dtype: DType,
105 elements: ArrayRef,
106 offsets: ArrayRef,
107 validity: Validity,
108 stats_set: ArrayStats,
109}
110
111#[derive(Clone, Debug)]
112pub struct ListEncoding;
113
114pub trait OffsetPType: NativePType + PrimInt + AsPrimitive<usize> + Into<Scalar> {}
115
116impl<T> OffsetPType for T where T: NativePType + PrimInt + AsPrimitive<usize> + Into<Scalar> {}
117
118impl ListArray {
127 pub fn try_new(
128 elements: ArrayRef,
129 offsets: ArrayRef,
130 validity: Validity,
131 ) -> VortexResult<Self> {
132 let nullability = validity.nullability();
133
134 if !offsets.dtype().is_int() || offsets.dtype().is_nullable() {
135 vortex_bail!(
136 "Expected offsets to be an non-nullable integer type, got {:?}",
137 offsets.dtype()
138 );
139 }
140
141 if offsets.is_empty() {
142 vortex_bail!("Offsets must have at least one element, [0] for an empty list");
143 }
144
145 Ok(Self {
146 dtype: DType::List(Arc::new(elements.dtype().clone()), nullability),
147 elements,
148 offsets,
149 validity,
150 stats_set: Default::default(),
151 })
152 }
153
154 pub fn offset_at(&self, index: usize) -> usize {
158 assert!(
159 index <= self.len(),
160 "Index {index} out of bounds 0..={}",
161 self.len()
162 );
163
164 self.offsets()
165 .as_opt::<PrimitiveVTable>()
166 .map(|p| {
167 Ok(match_each_native_ptype!(p.ptype(), |P| {
168 p.as_slice::<P>()[index].as_()
169 }))
170 })
171 .unwrap_or_else(|| {
172 self.offsets()
173 .scalar_at(index)
174 .and_then(|s| usize::try_from(&s))
175 })
176 .vortex_unwrap()
177 }
178
179 pub fn elements_at(&self, index: usize) -> VortexResult<ArrayRef> {
181 let start = self.offset_at(index);
182 let end = self.offset_at(index + 1);
183 self.elements().slice(start, end)
184 }
185
186 pub fn sliced_elements(&self) -> VortexResult<ArrayRef> {
188 let start = self.offset_at(0);
189 let end = self.offset_at(self.len());
190 self.elements().slice(start, end)
191 }
192
193 pub fn offsets(&self) -> &ArrayRef {
195 &self.offsets
196 }
197
198 pub fn elements(&self) -> &ArrayRef {
200 &self.elements
201 }
202
203 pub fn reset_offsets(&self) -> VortexResult<Self> {
205 let elements = self.sliced_elements()?;
206 let offsets = self.offsets();
207 let first_offset = offsets.scalar_at(0)?;
208 let adjusted_offsets = sub_scalar(offsets, first_offset)?;
209
210 Self::try_new(elements, adjusted_offsets, self.validity.clone())
211 }
212}
213
214impl ArrayVTable<ListVTable> for ListVTable {
215 fn len(array: &ListArray) -> usize {
216 array.offsets.len().saturating_sub(1)
217 }
218
219 fn dtype(array: &ListArray) -> &DType {
220 &array.dtype
221 }
222
223 fn stats(array: &ListArray) -> StatsSetRef<'_> {
224 array.stats_set.to_ref(array.as_ref())
225 }
226}
227
228impl OperationsVTable<ListVTable> for ListVTable {
229 fn slice(array: &ListArray, start: usize, stop: usize) -> VortexResult<ArrayRef> {
230 Ok(ListArray::try_new(
231 array.elements().clone(),
232 array.offsets().slice(start, stop + 1)?,
233 array.validity().slice(start, stop)?,
234 )?
235 .into_array())
236 }
237
238 fn scalar_at(array: &ListArray, index: usize) -> VortexResult<Scalar> {
239 let elem = array.elements_at(index)?;
240 let scalars: Vec<Scalar> = (0..elem.len()).map(|i| elem.scalar_at(i)).try_collect()?;
241
242 Ok(Scalar::list(
243 Arc::new(elem.dtype().clone()),
244 scalars,
245 array.dtype().nullability(),
246 ))
247 }
248}
249
250impl CanonicalVTable<ListVTable> for ListVTable {
251 fn canonicalize(array: &ListArray) -> VortexResult<Canonical> {
252 Ok(Canonical::List(array.clone()))
253 }
254}
255
256impl ValidityHelper for ListArray {
257 fn validity(&self) -> &Validity {
258 &self.validity
259 }
260}
261
262#[cfg(feature = "test-harness")]
263impl ListArray {
264 pub fn from_iter_slow<O: OffsetPType, I: IntoIterator>(
268 iter: I,
269 dtype: Arc<DType>,
270 ) -> VortexResult<ArrayRef>
271 where
272 I::Item: IntoIterator,
273 <I::Item as IntoIterator>::Item: Into<Scalar>,
274 {
275 let iter = iter.into_iter();
276 let mut builder = ListBuilder::<O>::with_capacity(
277 dtype.clone(),
278 vortex_dtype::Nullability::NonNullable,
279 iter.size_hint().0,
280 );
281
282 for v in iter {
283 let elem = Scalar::list(
284 dtype.clone(),
285 v.into_iter().map(|x| x.into()).collect_vec(),
286 dtype.nullability(),
287 );
288 builder.append_value(elem.as_list())?
289 }
290 Ok(builder.finish())
291 }
292
293 pub fn from_iter_opt_slow<O: OffsetPType, I: IntoIterator<Item = Option<T>>, T>(
294 iter: I,
295 dtype: Arc<DType>,
296 ) -> VortexResult<ArrayRef>
297 where
298 T: IntoIterator,
299 T::Item: Into<Scalar>,
300 {
301 let iter = iter.into_iter();
302 let mut builder = ListBuilder::<O>::with_capacity(
303 dtype.clone(),
304 vortex_dtype::Nullability::Nullable,
305 iter.size_hint().0,
306 );
307
308 for v in iter {
309 if let Some(v) = v {
310 let elem = Scalar::list(
311 dtype.clone(),
312 v.into_iter().map(|x| x.into()).collect_vec(),
313 dtype.nullability(),
314 );
315 builder.append_value(elem.as_list())?
316 } else {
317 builder.append_null()
318 }
319 }
320 Ok(builder.finish())
321 }
322}
323
324#[cfg(test)]
325mod test {
326 use std::sync::Arc;
327
328 use arrow_buffer::BooleanBuffer;
329 use vortex_dtype::Nullability;
330 use vortex_dtype::PType::I32;
331 use vortex_error::VortexUnwrap;
332 use vortex_mask::Mask;
333 use vortex_scalar::Scalar;
334
335 use crate::arrays::list::ListArray;
336 use crate::arrays::{ListVTable, PrimitiveArray};
337 use crate::builders::{ArrayBuilder, ListBuilder};
338 use crate::compute::filter;
339 use crate::validity::Validity;
340 use crate::{Array, IntoArray};
341
342 #[test]
343 fn test_empty_list_array() {
344 let elements = PrimitiveArray::empty::<u32>(Nullability::NonNullable);
345 let offsets = PrimitiveArray::from_iter([0]);
346 let validity = Validity::AllValid;
347
348 let list =
349 ListArray::try_new(elements.into_array(), offsets.into_array(), validity).unwrap();
350
351 assert_eq!(0, list.len());
352 }
353
354 #[test]
355 fn test_simple_list_array() {
356 let elements = PrimitiveArray::from_iter([1i32, 2, 3, 4, 5]);
357 let offsets = PrimitiveArray::from_iter([0, 2, 4, 5]);
358 let validity = Validity::AllValid;
359
360 let list =
361 ListArray::try_new(elements.into_array(), offsets.into_array(), validity).unwrap();
362
363 assert_eq!(
364 Scalar::list(
365 Arc::new(I32.into()),
366 vec![1.into(), 2.into()],
367 Nullability::Nullable
368 ),
369 list.scalar_at(0).unwrap()
370 );
371 assert_eq!(
372 Scalar::list(
373 Arc::new(I32.into()),
374 vec![3.into(), 4.into()],
375 Nullability::Nullable
376 ),
377 list.scalar_at(1).unwrap()
378 );
379 assert_eq!(
380 Scalar::list(Arc::new(I32.into()), vec![5.into()], Nullability::Nullable),
381 list.scalar_at(2).unwrap()
382 );
383 }
384
385 #[test]
386 fn test_simple_list_array_from_iter() {
387 let elements = PrimitiveArray::from_iter([1i32, 2, 3]);
388 let offsets = PrimitiveArray::from_iter([0, 2, 3]);
389 let validity = Validity::NonNullable;
390
391 let list =
392 ListArray::try_new(elements.into_array(), offsets.into_array(), validity).unwrap();
393
394 let list_from_iter =
395 ListArray::from_iter_slow::<u32, _>(vec![vec![1i32, 2], vec![3]], Arc::new(I32.into()))
396 .unwrap();
397
398 assert_eq!(list.len(), list_from_iter.len());
399 assert_eq!(
400 list.scalar_at(0).unwrap(),
401 list_from_iter.scalar_at(0).unwrap()
402 );
403 assert_eq!(
404 list.scalar_at(1).unwrap(),
405 list_from_iter.scalar_at(1).unwrap()
406 );
407 }
408
409 #[test]
410 fn test_simple_list_filter() {
411 let elements = PrimitiveArray::from_option_iter([None, Some(2), Some(3), Some(4), Some(5)]);
412 let offsets = PrimitiveArray::from_iter([0, 2, 4, 5]);
413 let validity = Validity::AllValid;
414
415 let list = ListArray::try_new(elements.into_array(), offsets.into_array(), validity)
416 .unwrap()
417 .into_array();
418
419 let filtered = filter(
420 &list,
421 &Mask::from(BooleanBuffer::from(vec![false, true, true])),
422 );
423
424 assert!(filtered.is_ok())
425 }
426
427 #[test]
428 fn test_offset_to_0() {
429 let mut builder =
430 ListBuilder::<u32>::with_capacity(Arc::new(I32.into()), Nullability::NonNullable, 5);
431 builder
432 .append_value(
433 Scalar::list(
434 Arc::new(I32.into()),
435 vec![1.into(), 2.into(), 3.into()],
436 Nullability::NonNullable,
437 )
438 .as_list(),
439 )
440 .vortex_unwrap();
441 builder
442 .append_value(
443 Scalar::list(
444 Arc::new(I32.into()),
445 vec![4.into(), 5.into(), 6.into()],
446 Nullability::NonNullable,
447 )
448 .as_list(),
449 )
450 .vortex_unwrap();
451 builder
452 .append_value(
453 Scalar::list(
454 Arc::new(I32.into()),
455 vec![7.into(), 8.into(), 9.into()],
456 Nullability::NonNullable,
457 )
458 .as_list(),
459 )
460 .vortex_unwrap();
461 builder
462 .append_value(
463 Scalar::list(
464 Arc::new(I32.into()),
465 vec![10.into(), 11.into(), 12.into()],
466 Nullability::NonNullable,
467 )
468 .as_list(),
469 )
470 .vortex_unwrap();
471 builder
472 .append_value(
473 Scalar::list(
474 Arc::new(I32.into()),
475 vec![13.into(), 14.into(), 15.into()],
476 Nullability::NonNullable,
477 )
478 .as_list(),
479 )
480 .vortex_unwrap();
481 let list = builder.finish().slice(2, 4).vortex_unwrap();
482 let list = list.as_::<ListVTable>().reset_offsets().unwrap();
483 assert_eq!(list.len(), 2);
484 assert_eq!(list.offsets().len(), 3);
485 assert_eq!(list.elements().len(), 6);
486 assert_eq!(list.offsets().scalar_at(0).unwrap(), 0u32.into());
487 }
488}