1use crate::coalesce::{InProgressArray, check_filter_len};
6use crate::filter::{FilterIndices, FilterPredicate, FilterSelection, FilterSlices};
7use arrow_array::cast::AsArray;
8use arrow_array::{Array, ArrayRef, ArrowPrimitiveType, PrimitiveArray};
9use arrow_buffer::{NullBuffer, NullBufferBuilder, ScalarBuffer};
10use arrow_schema::{ArrowError, DataType};
11use std::fmt::Debug;
12use std::sync::Arc;
13
14#[derive(Debug)]
16pub(crate) struct InProgressPrimitiveArray<T: ArrowPrimitiveType> {
17 data_type: DataType,
19 source: Option<ArrayRef>,
21 batch_size: usize,
23 nulls: NullBufferBuilder,
25 current: Vec<T::Native>,
27}
28
29impl<T: ArrowPrimitiveType> InProgressPrimitiveArray<T> {
30 pub(crate) fn new(batch_size: usize, data_type: DataType) -> Self {
32 Self {
33 data_type,
34 batch_size,
35 source: None,
36 nulls: NullBufferBuilder::new(batch_size),
37 current: vec![],
38 }
39 }
40
41 fn ensure_capacity(&mut self) {
46 if self.current.capacity() == 0 {
47 self.current.reserve(self.batch_size);
48 }
49 }
50
51 unsafe fn append_values_by_indices(
56 current: &mut Vec<T::Native>,
57 values: &[T::Native],
58 indices: FilterIndices<'_>,
59 selected_count: usize,
60 ) {
61 current.reserve(selected_count);
62 let current_len = current.len();
63 let mut written = 0;
64
65 unsafe {
69 let mut out = current
70 .spare_capacity_mut()
71 .as_mut_ptr()
72 .cast::<T::Native>();
73
74 indices.for_each(|idx| {
75 out.write(*values.get_unchecked(idx));
76 out = out.add(1);
77 written += 1;
78 });
79
80 current.set_len(current_len + written);
81 }
82
83 debug_assert_eq!(written, selected_count);
84 }
85
86 unsafe fn append_values_by_slices(
92 current: &mut Vec<T::Native>,
93 values: &[T::Native],
94 slices: FilterSlices<'_>,
95 selected_count: usize,
96 ) {
97 current.reserve(selected_count);
98 let current_len = current.len();
99 let mut written = 0;
100
101 unsafe {
105 let mut out = current
106 .spare_capacity_mut()
107 .as_mut_ptr()
108 .cast::<T::Native>();
109
110 slices.for_each(|(start, end)| {
111 let len = end - start;
112 std::ptr::copy_nonoverlapping(values.as_ptr().add(start), out, len);
113 out = out.add(len);
114 written += len;
115 });
116
117 current.set_len(current_len + written);
118 }
119
120 debug_assert_eq!(written, selected_count);
121 }
122}
123
124#[inline]
125fn primitive_source<T: ArrowPrimitiveType>(
126 source: Option<&ArrayRef>,
127) -> Result<&PrimitiveArray<T>, ArrowError> {
128 Ok(source
129 .ok_or_else(|| {
130 ArrowError::InvalidArgumentError(
131 "Internal Error: InProgressPrimitiveArray: source not set".to_string(),
132 )
133 })?
134 .as_primitive::<T>())
135}
136
137fn append_filtered_nulls(
138 nulls: &mut NullBufferBuilder,
139 source_nulls: Option<&NullBuffer>,
140 filter: &FilterPredicate,
141) {
142 if let Some(filtered_nulls) = filter.filter_nulls(source_nulls) {
143 nulls.append_buffer(&filtered_nulls);
144 } else {
145 nulls.append_n_non_nulls(filter.count());
146 }
147}
148
149impl<T: ArrowPrimitiveType + Debug> InProgressArray for InProgressPrimitiveArray<T> {
150 fn set_source(&mut self, source: Option<ArrayRef>) {
151 self.source = source;
152 }
153
154 fn copy_rows(&mut self, offset: usize, len: usize) -> Result<(), ArrowError> {
155 self.ensure_capacity();
156
157 let s = primitive_source::<T>(self.source.as_ref())?;
158
159 if let Some(nulls) = s.nulls().as_ref() {
161 let nulls = nulls.slice(offset, len);
162 self.nulls.append_buffer(&nulls);
163 } else {
164 self.nulls.append_n_non_nulls(len);
165 }
166
167 let values = s.values();
169 self.current
170 .extend_from_slice(&values[offset..offset + len]);
171
172 Ok(())
173 }
174
175 fn copy_rows_by_filter(&mut self, filter: &FilterPredicate) -> Result<(), ArrowError> {
176 let source_len = primitive_source::<T>(self.source.as_ref())?.len();
177 check_filter_len(filter, source_len)?;
178
179 match filter.selection() {
180 FilterSelection::Indices(indices) => {
181 self.ensure_capacity();
182 let s = primitive_source::<T>(self.source.as_ref())?;
183
184 append_filtered_nulls(&mut self.nulls, s.nulls(), filter);
185 unsafe {
188 Self::append_values_by_indices(
189 &mut self.current,
190 s.values(),
191 indices,
192 filter.count(),
193 )
194 };
195 Ok(())
196 }
197 FilterSelection::Slices(slices) => {
198 self.ensure_capacity();
199 let s = primitive_source::<T>(self.source.as_ref())?;
200
201 append_filtered_nulls(&mut self.nulls, s.nulls(), filter);
202 unsafe {
205 Self::append_values_by_slices(
206 &mut self.current,
207 s.values(),
208 slices,
209 filter.count(),
210 )
211 };
212 Ok(())
213 }
214 selection => self.copy_rows_by_selection(selection),
216 }
217 }
218
219 fn finish(&mut self) -> Result<ArrayRef, ArrowError> {
220 let values = std::mem::take(&mut self.current);
222 let nulls = self.nulls.finish();
223 self.nulls = NullBufferBuilder::new(self.batch_size);
224
225 let array = PrimitiveArray::<T>::try_new(ScalarBuffer::from(values), nulls)?
226 .with_data_type(self.data_type.clone());
228 Ok(Arc::new(array))
229 }
230
231 fn size(&self) -> usize {
232 self.source
233 .as_ref()
234 .map_or(0, |source| source.get_array_memory_size())
235 + self.current.capacity() * std::mem::size_of::<T::Native>()
236 + self.nulls.allocated_size()
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use crate::filter::FilterBuilder;
244 use arrow_array::types::Int32Type;
245 use arrow_array::{BooleanArray, Int32Array};
246
247 #[test]
248 fn test_copy_rows_by_filter_index_iterator() {
249 let source =
250 Int32Array::from_iter((0..21).map(|idx| if idx % 5 == 0 { None } else { Some(idx) }));
251 let filter = BooleanArray::from_iter(
252 (0..21).map(|idx| Some(matches!(idx, 0 | 1 | 2 | 3 | 5 | 8 | 13))),
253 );
254 let predicate = FilterBuilder::new(&filter).build();
255 let FilterSelection::Indices(indices) = predicate.selection() else {
256 panic!("expected index iterator selection");
257 };
258 let mut selected_indices = Vec::new();
259 indices.for_each(|idx| selected_indices.push(idx));
260 assert_eq!(selected_indices, vec![0, 1, 2, 3, 5, 8, 13]);
261
262 let mut in_progress = InProgressPrimitiveArray::<Int32Type>::new(7, DataType::Int32);
263 in_progress.set_source(Some(Arc::new(source)));
264 in_progress.copy_rows_by_filter(&predicate).unwrap();
265
266 let result = in_progress.finish().unwrap();
267 let result = result.as_primitive::<Int32Type>();
268 let expected = Int32Array::from(vec![
269 None,
270 Some(1),
271 Some(2),
272 Some(3),
273 None,
274 Some(8),
275 Some(13),
276 ]);
277 assert_eq!(result, &expected);
278 }
279
280 #[test]
281 fn test_copy_rows_by_filter_slice_iterator() {
282 let source =
283 Int32Array::from_iter((0..16).map(|idx| if idx % 5 == 0 { None } else { Some(idx) }));
284 let filter = BooleanArray::from_iter((0..16).map(|idx| Some(!matches!(idx, 3 | 9))));
285 let predicate = FilterBuilder::new(&filter).build();
286 let FilterSelection::Slices(slices) = predicate.selection() else {
287 panic!("expected slice iterator selection");
288 };
289 let mut selected_slices = Vec::new();
290 slices.for_each(|slice| selected_slices.push(slice));
291 assert_eq!(selected_slices, vec![(0, 3), (4, 9), (10, 16)]);
292
293 let mut in_progress = InProgressPrimitiveArray::<Int32Type>::new(14, DataType::Int32);
294 in_progress.set_source(Some(Arc::new(source)));
295 in_progress.copy_rows_by_filter(&predicate).unwrap();
296
297 let result = in_progress.finish().unwrap();
298 let result = result.as_primitive::<Int32Type>();
299 let expected = Int32Array::from(vec![
300 None,
301 Some(1),
302 Some(2),
303 Some(4),
304 None,
305 Some(6),
306 Some(7),
307 Some(8),
308 None,
309 Some(11),
310 Some(12),
311 Some(13),
312 Some(14),
313 None,
314 ]);
315 assert_eq!(result, &expected);
316 }
317
318 #[test]
319 fn test_copy_rows_by_filter_rejects_all_filter_longer_than_source() {
320 let source: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
321 let mut in_progress = InProgressPrimitiveArray::<Int32Type>::new(4, DataType::Int32);
322 in_progress.set_source(Some(source));
323
324 let filter = BooleanArray::from(vec![true; 4]);
325 let predicate = FilterBuilder::new(&filter).build();
326 assert!(matches!(predicate.selection(), FilterSelection::All { .. }));
327
328 let err = in_progress.copy_rows_by_filter(&predicate).unwrap_err();
329 assert!(matches!(
330 err,
331 ArrowError::InvalidArgumentError(message)
332 if message == "Filter predicate of length 4 is larger than source array of length 3"
333 ));
334 }
335
336 #[test]
337 fn test_size_empty() {
338 let in_progress = InProgressPrimitiveArray::<Int32Type>::new(64, DataType::Int32);
340 assert_eq!(in_progress.size(), 0);
341 }
342
343 #[test]
344 fn test_size_counts_source() {
345 let mut in_progress = InProgressPrimitiveArray::<Int32Type>::new(64, DataType::Int32);
346 let source: ArrayRef = Arc::new(Int32Array::from_iter_values(0..100));
347 in_progress.set_source(Some(Arc::clone(&source)));
348 assert_eq!(in_progress.size(), source.get_array_memory_size());
350 }
351
352 #[test]
353 fn test_size_counts_values_buffer_and_resets_on_finish() {
354 const BATCH_SIZE: usize = 64;
355 let mut in_progress =
356 InProgressPrimitiveArray::<Int32Type>::new(BATCH_SIZE, DataType::Int32);
357 let source: ArrayRef = Arc::new(Int32Array::from_iter_values(0..100));
360 let source_size = source.get_array_memory_size();
361 in_progress.set_source(Some(Arc::clone(&source)));
362
363 in_progress.copy_rows(0, 50).unwrap();
364 assert!(
365 in_progress.size() >= source_size + 50 * size_of::<i32>(),
366 "values buffer under-counted: {} < {} + {} * {}",
367 in_progress.size(),
368 source_size,
369 50,
370 size_of::<i32>(),
371 );
372
373 in_progress.finish().unwrap();
376 assert_eq!(in_progress.size(), source_size);
377 }
378
379 #[test]
380 fn test_size_counts_null_buffer() {
381 const BATCH_SIZE: usize = 64;
382
383 let in_progress_bytes = |source: ArrayRef| {
384 let mut in_progress =
385 InProgressPrimitiveArray::<Int32Type>::new(BATCH_SIZE, DataType::Int32);
386 let source_len = source.len();
387 in_progress.set_source(Some(source));
388 in_progress.copy_rows(0, source_len / 2).unwrap();
389 in_progress.size()
390 };
391
392 let all_valid = in_progress_bytes(Arc::new(Int32Array::from_iter_values(0..100)));
394 let with_nulls = in_progress_bytes(Arc::new(Int32Array::from_iter(
396 (0..100).map(|i| (i % 2 == 0).then_some(i)),
397 )));
398
399 assert!(
400 with_nulls > all_valid,
401 "null buffer must be included in size(): with_nulls={with_nulls} all_valid={all_valid}"
402 );
403 }
404}