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