vortex_array/arrays/chunked/
ops.rs1use vortex_error::{VortexResult, vortex_bail};
2use vortex_scalar::Scalar;
3
4use crate::arrays::ChunkedVTable;
5use crate::arrays::chunked::ChunkedArray;
6use crate::vtable::OperationsVTable;
7use crate::{Array, ArrayRef, IntoArray};
8
9impl OperationsVTable<ChunkedVTable> for ChunkedVTable {
10 fn slice(array: &ChunkedArray, start: usize, stop: usize) -> VortexResult<ArrayRef> {
11 let (offset_chunk, offset_in_first_chunk) = array.find_chunk_idx(start);
12 let (length_chunk, length_in_last_chunk) = array.find_chunk_idx(stop);
13
14 if array.is_empty() && (start != 0 || stop != 0) {
15 vortex_bail!(ComputeError: "Empty chunked array can't be sliced from {start} to {stop}");
16 } else if array.is_empty() {
17 return Ok(ChunkedArray::new_unchecked(vec![], array.dtype().clone()).into_array());
18 }
19
20 if length_chunk == offset_chunk {
21 let chunk = array.chunk(offset_chunk)?;
22 return chunk.slice(offset_in_first_chunk, length_in_last_chunk);
23 }
24
25 let mut chunks = (offset_chunk..length_chunk + 1)
26 .map(|i| array.chunk(i).cloned())
27 .collect::<VortexResult<Vec<_>>>()?;
28 if let Some(c) = chunks.first_mut() {
29 *c = c.slice(offset_in_first_chunk, c.len())?;
30 }
31
32 if length_in_last_chunk == 0 {
33 chunks.pop();
34 } else if let Some(c) = chunks.last_mut() {
35 *c = c.slice(0, length_in_last_chunk)?;
36 }
37
38 Ok(ChunkedArray::new_unchecked(chunks, array.dtype().clone()).into_array())
39 }
40
41 fn scalar_at(array: &ChunkedArray, index: usize) -> VortexResult<Scalar> {
42 let (chunk_index, chunk_offset) = array.find_chunk_idx(index);
43 array.chunk(chunk_index)?.scalar_at(chunk_offset)
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use vortex_buffer::Buffer;
50 use vortex_dtype::{DType, NativePType, Nullability, PType};
51
52 use crate::IntoArray;
53 use crate::array::Array;
54 use crate::arrays::{ChunkedArray, ChunkedVTable, PrimitiveArray};
55 use crate::canonical::ToCanonical;
56
57 fn chunked_array() -> ChunkedArray {
58 ChunkedArray::try_new(
59 vec![
60 PrimitiveArray::from_iter([1u64, 2, 3]).into_array(),
61 PrimitiveArray::from_iter([4u64, 5, 6]).into_array(),
62 PrimitiveArray::from_iter([7u64, 8, 9]).into_array(),
63 ],
64 DType::Primitive(PType::U64, Nullability::NonNullable),
65 )
66 .unwrap()
67 }
68
69 fn assert_equal_slices<T: NativePType>(arr: &dyn Array, slice: &[T]) {
70 let mut values = Vec::with_capacity(arr.len());
71 if let Some(arr) = arr.as_opt::<ChunkedVTable>() {
72 arr.chunks()
73 .iter()
74 .map(|a| a.to_primitive().unwrap())
75 .for_each(|a| values.extend_from_slice(a.as_slice::<T>()));
76 } else {
77 values.extend_from_slice(arr.to_primitive().unwrap().as_slice::<T>());
78 }
79 assert_eq!(values, slice);
80 }
81
82 #[test]
83 fn slice_middle() {
84 assert_equal_slices(&chunked_array().slice(2, 5).unwrap(), &[3u64, 4, 5])
85 }
86
87 #[test]
88 fn slice_begin() {
89 assert_equal_slices(&chunked_array().slice(1, 3).unwrap(), &[2u64, 3]);
90 }
91
92 #[test]
93 fn slice_aligned() {
94 assert_equal_slices(&chunked_array().slice(3, 6).unwrap(), &[4u64, 5, 6]);
95 }
96
97 #[test]
98 fn slice_many_aligned() {
99 assert_equal_slices(
100 &chunked_array().slice(0, 6).unwrap(),
101 &[1u64, 2, 3, 4, 5, 6],
102 );
103 }
104
105 #[test]
106 fn slice_end() {
107 assert_equal_slices(&chunked_array().slice(7, 8).unwrap(), &[8u64]);
108 }
109
110 #[test]
111 fn slice_exactly_end() {
112 assert_equal_slices(&chunked_array().slice(6, 9).unwrap(), &[7u64, 8, 9]);
113 }
114
115 #[test]
116 fn slice_empty() {
117 let chunked = ChunkedArray::try_new(vec![], PType::U32.into()).unwrap();
118 let sliced = chunked.slice(0, 0).unwrap();
119
120 assert!(sliced.is_empty());
121 }
122
123 #[test]
124 fn scalar_at_empty_children_both_sides() {
125 let array = ChunkedArray::try_new(
126 vec![
127 Buffer::<u64>::empty().into_array(),
128 Buffer::<u64>::empty().into_array(),
129 PrimitiveArray::from_iter([1u64, 2]).into_array(),
130 Buffer::<u64>::empty().into_array(),
131 Buffer::<u64>::empty().into_array(),
132 ],
133 DType::Primitive(PType::U64, Nullability::NonNullable),
134 )
135 .unwrap();
136 assert_eq!(array.scalar_at(0).unwrap(), 1u64.into());
137 assert_eq!(array.scalar_at(1).unwrap(), 2u64.into());
138 }
139
140 #[test]
141 fn scalar_at_empty_children_trailing() {
142 let array = ChunkedArray::try_new(
143 vec![
144 PrimitiveArray::from_iter([1u64, 2]).into_array(),
145 Buffer::<u64>::empty().into_array(),
146 Buffer::<u64>::empty().into_array(),
147 PrimitiveArray::from_iter([3u64, 4]).into_array(),
148 ],
149 DType::Primitive(PType::U64, Nullability::NonNullable),
150 )
151 .unwrap();
152 assert_eq!(array.scalar_at(0).unwrap(), 1u64.into());
153 assert_eq!(array.scalar_at(1).unwrap(), 2u64.into());
154 assert_eq!(array.scalar_at(2).unwrap(), 3u64.into());
155 assert_eq!(array.scalar_at(3).unwrap(), 4u64.into());
156 }
157
158 #[test]
159 fn scalar_at_empty_children_leading() {
160 let array = ChunkedArray::try_new(
161 vec![
162 Buffer::<u64>::empty().into_array(),
163 Buffer::<u64>::empty().into_array(),
164 PrimitiveArray::from_iter([1u64, 2]).into_array(),
165 PrimitiveArray::from_iter([3u64, 4]).into_array(),
166 ],
167 DType::Primitive(PType::U64, Nullability::NonNullable),
168 )
169 .unwrap();
170 assert_eq!(array.scalar_at(0).unwrap(), 1u64.into());
171 assert_eq!(array.scalar_at(1).unwrap(), 2u64.into());
172 assert_eq!(array.scalar_at(2).unwrap(), 3u64.into());
173 assert_eq!(array.scalar_at(3).unwrap(), 4u64.into());
174 }
175}