1use super::InProgressArray;
6use crate::concat::concat;
7use crate::filter::{FilterPredicate, FilterSelection};
8use crate::interleave::interleave;
9use arrow_array::{Array, ArrayRef, new_empty_array};
10use arrow_schema::{ArrowError, DataType};
11
12const MAX_PENDING_SOURCES: usize = 64;
14const MAX_PENDING_SOURCE_BYTES: usize = 64 * 1024 * 1024;
15
16#[derive(Debug)]
17pub(crate) struct InProgressListArray {
18 source: Option<ArrayRef>,
20 data_type: DataType,
22 buffered_arrays: Vec<ArrayRef>,
24 sparse_sources: Vec<ArrayRef>,
26 sparse_indices: Vec<(usize, usize)>,
28 sparse_source_bytes: usize,
30 source_data_referenced_in_buffers: bool,
32 total_size_of_non_shared_buffers: usize,
34}
35
36impl InProgressListArray {
37 pub(crate) fn new(data_type: DataType) -> Self {
38 Self {
39 source: None,
40 data_type,
41 buffered_arrays: Vec::new(),
42 sparse_sources: Vec::new(),
43 sparse_indices: Vec::new(),
44 sparse_source_bytes: 0,
45 source_data_referenced_in_buffers: false,
46 total_size_of_non_shared_buffers: 0,
47 }
48 }
49
50 fn sparse_limit_reached(&self) -> bool {
51 self.sparse_sources.len() >= MAX_PENDING_SOURCES
52 || self.sparse_source_bytes >= MAX_PENDING_SOURCE_BYTES
53 }
54
55 fn flush_sparse(&mut self) -> Result<(), ArrowError> {
56 if self.sparse_indices.is_empty() {
57 self.sparse_sources.clear();
58 self.sparse_source_bytes = 0;
59 return Ok(());
60 }
61
62 let sources = self
63 .sparse_sources
64 .iter()
65 .map(|array| array.as_ref())
66 .collect::<Vec<_>>();
67 let array = interleave(&sources, &self.sparse_indices)?;
68 self.total_size_of_non_shared_buffers += array.get_array_memory_size();
69 self.buffered_arrays.push(array);
70 self.sparse_sources.clear();
71 self.sparse_indices.clear();
72 self.sparse_source_bytes = 0;
73 Ok(())
74 }
75}
76
77impl InProgressArray for InProgressListArray {
78 fn set_source(&mut self, source: Option<ArrayRef>) {
79 if let Some(old_source) = self.source.take()
80 && self.source_data_referenced_in_buffers
81 {
82 self.total_size_of_non_shared_buffers += old_source.get_array_memory_size();
83 }
84 self.source_data_referenced_in_buffers = false;
85 self.source = source;
86 }
87
88 fn copy_rows(&mut self, offset: usize, len: usize) -> Result<(), ArrowError> {
89 self.flush_sparse()?;
90 let source = self.source.as_ref().ok_or_else(|| {
91 ArrowError::InvalidArgumentError("InProgressListArray source not set".into())
92 })?;
93 self.source_data_referenced_in_buffers = true;
94 self.buffered_arrays.push(source.slice(offset, len));
95 Ok(())
96 }
97
98 fn copy_rows_by_filter_from(
99 &mut self,
100 source: ArrayRef,
101 filter: &FilterPredicate,
102 ) -> Result<(), ArrowError> {
103 let source_size = source.get_array_memory_size();
104 if !self.sparse_sources.is_empty()
105 && (self.sparse_limit_reached()
106 || self.sparse_source_bytes.saturating_add(source_size) > MAX_PENDING_SOURCE_BYTES)
107 {
108 self.flush_sparse()?;
109 }
110
111 let source_index = self.sparse_sources.len();
112 match filter.selection() {
113 FilterSelection::None => return Ok(()),
114 FilterSelection::All { len } => self
115 .sparse_indices
116 .extend((0..len).map(|index| (source_index, index))),
117 FilterSelection::Slices(slices) => slices.for_each(|(start, end)| {
118 self.sparse_indices
119 .extend((start..end).map(|index| (source_index, index)));
120 }),
121 FilterSelection::Indices(indices) => indices.for_each(|index| {
122 self.sparse_indices.push((source_index, index));
123 }),
124 }
125 self.sparse_sources.push(source);
126 self.sparse_source_bytes += source_size;
127 if self.sparse_limit_reached() {
128 self.flush_sparse()?;
129 }
130 Ok(())
131 }
132
133 fn finish(&mut self) -> Result<ArrayRef, ArrowError> {
134 self.flush_sparse()?;
135 let result = match self.buffered_arrays.len() {
136 0 => new_empty_array(&self.data_type),
137 1 => self.buffered_arrays.pop().unwrap(),
138 _ => concat(
139 &self
140 .buffered_arrays
141 .iter()
142 .map(|array| array.as_ref())
143 .collect::<Vec<_>>(),
144 )?,
145 };
146 self.buffered_arrays.clear();
147 self.total_size_of_non_shared_buffers = 0;
148 self.source_data_referenced_in_buffers = false;
149 Ok(result)
150 }
151
152 fn size(&self) -> usize {
153 self.total_size_of_non_shared_buffers
154 + self.buffered_arrays.capacity() * size_of::<ArrayRef>()
155 + self.sparse_sources.capacity() * size_of::<ArrayRef>()
156 + self.sparse_indices.capacity() * size_of::<(usize, usize)>()
157 + self
158 .source
159 .as_ref()
160 .map_or(0, |source| source.get_array_memory_size())
161 + self.sparse_source_bytes
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::filter::FilterBuilder;
169 use arrow_array::{BooleanArray, Int32Array, ListArray, StructArray, UInt32Array};
170 use arrow_buffer::{NullBuffer, OffsetBuffer};
171 use arrow_schema::Field;
172 use std::sync::Arc;
173
174 fn list_array() -> ArrayRef {
175 let field = Arc::new(Field::new("item", DataType::Int32, true));
176 let offsets = OffsetBuffer::from_lengths([2, 1, 0, 2, 0]);
177 let values = Arc::new(Int32Array::from(vec![
178 Some(1),
179 None,
180 Some(2),
181 Some(3),
182 Some(4),
183 ]));
184 let nulls = NullBuffer::from(vec![true, true, false, true, true]);
185 Arc::new(ListArray::new(field, offsets, values, Some(nulls)))
186 }
187
188 #[test]
189 fn sparse_copy_preserves_nulls_and_empty_lists() {
190 let source = list_array();
191 let filter = BooleanArray::from(vec![true, false, true, true, true]);
192 let predicate = FilterBuilder::new(&filter).optimize().build();
193 let expected = crate::filter::filter(source.as_ref(), &filter).unwrap();
194 let mut in_progress = InProgressListArray::new(source.data_type().clone());
195 in_progress
196 .copy_rows_by_filter_from(source, &predicate)
197 .unwrap();
198 assert_eq!(in_progress.sparse_sources.len(), 1);
199 assert!(in_progress.buffered_arrays.is_empty());
200 assert_eq!(in_progress.finish().unwrap().as_ref(), expected.as_ref());
201 }
202
203 #[test]
204 fn sparse_copy_nested_children() {
205 let child = StructArray::from(vec![(
206 Arc::new(Field::new("id", DataType::Int32, false)),
207 Arc::new(Int32Array::from_iter_values(0..6)) as ArrayRef,
208 )]);
209 let field = Arc::new(Field::new("item", child.data_type().clone(), true));
210 let source = Arc::new(ListArray::new(
211 field,
212 OffsetBuffer::from_lengths([2, 1, 3]),
213 Arc::new(child),
214 None,
215 )) as ArrayRef;
216 let filter = BooleanArray::from(vec![true, false, true]);
217 let predicate = FilterBuilder::new(&filter).optimize().build();
218 let expected = crate::take::take(
219 source.as_ref(),
220 &UInt32Array::from_iter_values([0, 2]),
221 None,
222 )
223 .unwrap();
224 let mut in_progress = InProgressListArray::new(source.data_type().clone());
225 in_progress
226 .copy_rows_by_filter_from(source, &predicate)
227 .unwrap();
228 let output = in_progress.finish().unwrap();
229 assert_eq!(output.as_ref(), expected.as_ref());
230 }
231
232 #[test]
233 fn preserves_order_when_sparse_and_contiguous_inputs_mix() {
234 let first = list_array();
235 let second = list_array();
236 let filter = BooleanArray::from(vec![true, false, false, true, false]);
237 let predicate = FilterBuilder::new(&filter).optimize().build();
238 let filtered = crate::filter::filter(first.as_ref(), &filter).unwrap();
239 let second_slice = second.slice(1, 2);
240 let expected = concat(&[filtered.as_ref(), second_slice.as_ref()]).unwrap();
241 let mut in_progress = InProgressListArray::new(first.data_type().clone());
242 in_progress
243 .copy_rows_by_filter_from(first, &predicate)
244 .unwrap();
245 in_progress.set_source(Some(second));
246 in_progress.copy_rows(1, 2).unwrap();
247
248 assert_eq!(in_progress.finish().unwrap().as_ref(), expected.as_ref());
249 }
250
251 #[test]
252 fn bounds_retained_sparse_sources() {
253 let source = list_array();
254 let filter = BooleanArray::from(vec![true, false, false, false, false]);
255 let predicate = FilterBuilder::new(&filter).optimize().build();
256 let mut in_progress = InProgressListArray::new(source.data_type().clone());
257
258 for _ in 0..MAX_PENDING_SOURCES {
259 in_progress
260 .copy_rows_by_filter_from(Arc::clone(&source), &predicate)
261 .unwrap();
262 }
263
264 assert!(in_progress.sparse_sources.is_empty());
265 assert_eq!(in_progress.sparse_source_bytes, 0);
266 assert_eq!(in_progress.buffered_arrays.len(), 1);
267 assert_eq!(in_progress.finish().unwrap().len(), MAX_PENDING_SOURCES);
268 }
269}