1use super::{RowSelection, RowSelector};
28use crate::file::page_index::offset_index::PageLocation;
29use arrow_buffer::BooleanBuffer;
30use arrow_buffer::bit_chunk_iterator::BitChunks;
31use std::ops::Range;
32
33pub(super) fn scan_ranges_from_mask(
35 mask: &BooleanBuffer,
36 page_locations: &[PageLocation],
37) -> Vec<Range<u64>> {
38 let mut ranges = Vec::new();
39 let mut row_start = 0;
40 for (idx, page) in page_locations.iter().enumerate() {
41 if row_start >= mask.len() {
42 break;
43 }
44 let row_end = page_locations
45 .get(idx + 1)
46 .map_or(mask.len(), |next| next.first_row_index as usize)
47 .min(mask.len());
48
49 let chunks = BitChunks::new(
50 mask.values(),
51 mask.offset() + row_start,
52 row_end - row_start,
53 );
54 if chunks.iter_padded().any(|word| word != 0) {
55 let start = page.offset as u64;
56 ranges.push(start..start + page.compressed_page_size as u64);
57 }
58 row_start = row_end;
59 }
60 ranges
61}
62
63#[inline]
65pub(super) fn scan_ranges_from_selectors<I>(
66 selectors: I,
67 page_locations: &[PageLocation],
68) -> Vec<Range<u64>>
69where
70 I: IntoIterator<Item = RowSelector>,
71{
72 let mut ranges: Vec<Range<u64>> = vec![];
73 let mut row_offset = 0;
74
75 let mut pages = page_locations.iter().peekable();
76 let mut selectors = selectors.into_iter();
77 let mut current_selector = selectors.next();
78 let mut current_page = pages.next();
79
80 let mut current_page_included = false;
81
82 while let Some((selector, page)) = current_selector.as_mut().zip(current_page) {
83 if !(selector.skip || current_page_included) {
84 let start = page.offset as u64;
85 let end = start + page.compressed_page_size as u64;
86 ranges.push(start..end);
87 current_page_included = true;
88 }
89
90 if let Some(next_page) = pages.peek() {
91 if row_offset + selector.row_count > next_page.first_row_index as usize {
92 let remaining_in_page = next_page.first_row_index as usize - row_offset;
93 selector.row_count -= remaining_in_page;
94 row_offset += remaining_in_page;
95 current_page = pages.next();
96 current_page_included = false;
97 } else {
98 if row_offset + selector.row_count == next_page.first_row_index as usize {
99 current_page = pages.next();
100 current_page_included = false;
101 }
102 row_offset += selector.row_count;
103 current_selector = selectors.next();
104 }
105 } else {
106 if !(selector.skip || current_page_included) {
107 let start = page.offset as u64;
108 let end = start + page.compressed_page_size as u64;
109 ranges.push(start..end);
110 }
111 current_selector = selectors.next()
112 }
113 }
114
115 ranges
116}
117
118#[inline]
121pub(super) fn expand_to_batch_boundaries_from_selectors<I>(
122 selectors: I,
123 batch_size: usize,
124 total_rows: usize,
125) -> RowSelection
126where
127 I: IntoIterator<Item = RowSelector>,
128{
129 let mut expanded_ranges = Vec::new();
130 let mut row_offset = 0;
131
132 for selector in selectors {
133 if !selector.skip {
134 let start = row_offset;
135 let end = row_offset + selector.row_count;
136
137 let expanded_start = (start / batch_size) * batch_size;
139 let expanded_end = end.div_ceil(batch_size) * batch_size;
141 let expanded_end = expanded_end.min(total_rows);
142
143 expanded_ranges.push(expanded_start..expanded_end);
144 }
145 row_offset += selector.row_count;
146 }
147
148 expanded_ranges.sort_by_key(|range| range.start);
150
151 let mut merged_ranges: Vec<Range<usize>> = Vec::new();
153 for range in expanded_ranges {
154 if let Some(last) = merged_ranges.last_mut() {
155 if range.start <= last.end {
156 last.end = last.end.max(range.end);
158 } else {
159 merged_ranges.push(range);
161 }
162 } else {
163 merged_ranges.push(range);
165 }
166 }
167
168 RowSelection::from_consecutive_ranges(merged_ranges.into_iter(), total_rows)
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn page_locations(starts: impl IntoIterator<Item = usize>) -> Vec<PageLocation> {
176 starts
177 .into_iter()
178 .enumerate()
179 .map(|(idx, row)| PageLocation {
180 offset: (idx * 23 + 5) as i64,
181 compressed_page_size: (idx % 13 + 1) as i32,
182 first_row_index: row as i64,
183 })
184 .collect()
185 }
186
187 fn assert_mask_scan_ranges(
188 mask: &BooleanBuffer,
189 offset: usize,
190 pages: &[PageLocation],
191 total_rows: usize,
192 ) {
193 let selectors = RowSelection::from_consecutive_ranges(
195 mask.iter()
196 .enumerate()
197 .filter_map(|(row, selected)| selected.then_some(row..row + 1)),
198 mask.len(),
199 );
200 let padded = BooleanBuffer::from_iter(
202 std::iter::repeat_n(true, offset)
203 .chain(mask.iter())
204 .chain(std::iter::repeat_n(true, 64)),
205 );
206 let selection = RowSelection::from_boolean_buffer(padded.slice(offset, mask.len()));
207 assert_eq!(
208 selection.scan_ranges(pages),
209 selectors.scan_ranges(pages),
210 "mask={mask:?}, offset={offset}, pages={pages:?}"
211 );
212 assert_eq!(
213 selection.row_ranges_for_selected_pages(pages, total_rows),
214 selectors.row_ranges_for_selected_pages(pages, total_rows)
215 );
216 assert!(selection.scan_ranges(&[]).is_empty());
217 }
218
219 #[test]
220 fn test_mask_scan_ranges_matches_selectors() {
221 use rand::{RngExt, SeedableRng, rngs::StdRng};
222
223 for len in [0, 1, 7, 8, 63, 64, 65, 127, 128, 129, 511, 2049] {
224 for page_rows in [1, 7, 31, 63, 64, 65, 127, 257] {
225 let total_rows = len + 37;
227 let pages = page_locations((0..total_rows).step_by(page_rows));
228 for pattern in 0..7 {
229 let mut rng = StdRng::seed_from_u64(42);
230 let mask = BooleanBuffer::from_iter((0..len).map(|row| match pattern {
231 0 => false,
232 1 => true,
233 2 => row % 2 == 0,
234 3 => row % page_rows == page_rows - 1,
235 4 => rng.random_bool(0.01),
236 5 => rng.random_bool(0.5),
237 _ => rng.random_bool(0.99),
238 }));
239 for offset in [0, 1, 3, 7, 31, 63, 64, 65] {
240 assert_mask_scan_ranges(&mask, offset, &pages, total_rows);
241 }
242 }
243 }
244 }
245 }
246
247 #[test]
248 fn test_mask_scan_ranges_single_bits_and_padding() {
249 let pages = page_locations([0, 1, 130, 257, 386, 512, 549]);
250 for len in [130, 257, 511, 512] {
251 let selected_rows = std::iter::once(None).chain(
252 [
253 0, 1, 7, 8, 55, 56, 63, 64, 65, 127, 128, 129, 130, 255, 256, 257, 510, 511,
254 ]
255 .into_iter()
256 .filter(|&row| row < len)
257 .map(Some),
258 );
259 for selected_row in selected_rows {
260 let mask = BooleanBuffer::from_iter((0..len).map(|row| selected_row == Some(row)));
261 for offset in [0, 1, 7, 8, 31, 63, 64, 65] {
262 assert_mask_scan_ranges(&mask, offset, &pages, 600);
263 }
264 }
265 }
266 }
267
268 #[test]
269 fn test_scan_ranges() {
270 let index = vec![
271 PageLocation {
272 offset: 0,
273 compressed_page_size: 10,
274 first_row_index: 0,
275 },
276 PageLocation {
277 offset: 10,
278 compressed_page_size: 10,
279 first_row_index: 10,
280 },
281 PageLocation {
282 offset: 20,
283 compressed_page_size: 10,
284 first_row_index: 20,
285 },
286 PageLocation {
287 offset: 30,
288 compressed_page_size: 10,
289 first_row_index: 30,
290 },
291 PageLocation {
292 offset: 40,
293 compressed_page_size: 10,
294 first_row_index: 40,
295 },
296 PageLocation {
297 offset: 50,
298 compressed_page_size: 10,
299 first_row_index: 50,
300 },
301 PageLocation {
302 offset: 60,
303 compressed_page_size: 10,
304 first_row_index: 60,
305 },
306 ];
307
308 let selection = RowSelection::from(vec![
309 RowSelector::skip(10),
311 RowSelector::select(3),
313 RowSelector::skip(3),
314 RowSelector::select(4),
315 RowSelector::skip(5),
317 RowSelector::select(5),
318 RowSelector::skip(12),
320 RowSelector::select(12),
322 RowSelector::skip(12),
324 ]);
325
326 let ranges = selection.scan_ranges(&index);
327
328 assert_eq!(ranges, vec![10..20, 20..30, 40..50, 50..60]);
330 assert_eq!(
331 selection.row_ranges_for_selected_pages(&index, 70),
332 vec![10..20, 20..30, 40..50, 50..60]
333 );
334
335 let selection = RowSelection::from(vec![
336 RowSelector::skip(10),
338 RowSelector::select(3),
340 RowSelector::skip(3),
341 RowSelector::select(4),
342 RowSelector::skip(5),
344 RowSelector::select(5),
345 RowSelector::skip(12),
347 RowSelector::select(12),
349 RowSelector::skip(1),
350 RowSelector::select(8),
352 ]);
353
354 let ranges = selection.scan_ranges(&index);
355
356 assert_eq!(ranges, vec![10..20, 20..30, 40..50, 50..60, 60..70]);
358
359 let selection = RowSelection::from(vec![
360 RowSelector::skip(10),
362 RowSelector::select(3),
364 RowSelector::skip(3),
365 RowSelector::select(4),
366 RowSelector::skip(5),
368 RowSelector::select(5),
369 RowSelector::skip(12),
371 RowSelector::select(12),
373 RowSelector::skip(1),
374 RowSelector::skip(8),
376 RowSelector::select(4),
378 ]);
379
380 let ranges = selection.scan_ranges(&index);
381
382 assert_eq!(ranges, vec![10..20, 20..30, 40..50, 50..60, 60..70]);
384
385 let selection = RowSelection::from(vec![
386 RowSelector::skip(10),
388 RowSelector::select(3),
390 RowSelector::skip(3),
391 RowSelector::select(4),
392 RowSelector::skip(5),
394 RowSelector::select(6),
395 RowSelector::skip(50),
397 ]);
398
399 let ranges = selection.scan_ranges(&index);
400
401 assert_eq!(ranges, vec![10..20, 20..30, 30..40]);
403 }
404}