Skip to main content

lance_table/
rowids.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3//! Indices for mapping row ids to their corresponding addresses.
4//!
5//! Each fragment in a table has a [RowIdSequence] that contains the row ids
6//! in the order they appear in the fragment. The [RowIdIndex] aggregates these
7//! sequences and maps row ids to their corresponding addresses across the
8//! whole dataset.
9//!
10//! [RowIdSequence]s are serialized individually and stored in the fragment
11//! metadata. Use [read_row_ids] and [write_row_ids] to read and write these
12//! sequences. The on-disk format is designed to align well with the in-memory
13//! representation, to avoid unnecessary deserialization.
14use std::ops::{Range, RangeInclusive};
15// TODO: replace this with Arrow BooleanBuffer.
16
17// These are all internal data structures, and are private.
18mod bitmap;
19mod encoded_array;
20mod index;
21pub mod segment;
22mod serde;
23pub mod version;
24
25use lance_core::deepsize::DeepSizeOf;
26// These are the public API.
27pub use index::FragmentRowIdIndex;
28pub use index::RowIdIndex;
29use lance_core::{Error, Result};
30use lance_io::ReadBatchParams;
31use lance_select::{RowAddrMask, RowAddrTreeMap, RowSetOps};
32pub use serde::{read_row_ids, write_row_ids};
33
34use crate::utils::LanceIteratorExtension;
35use segment::U64Segment;
36use tracing::instrument;
37
38/// A sequence of row ids.
39///
40/// Row ids are u64s that:
41///
42/// 1. Are **unique** within a table (except for tombstones)
43/// 2. Are *often* but not always sorted and/or contiguous.
44///
45/// This sequence of row ids is optimized to be compact when the row ids are
46/// contiguous and sorted. However, it does not require that the row ids are
47/// contiguous or sorted.
48///
49/// We can make optimizations that assume uniqueness.
50#[derive(Debug, Clone, DeepSizeOf, PartialEq, Eq, Default)]
51pub struct RowIdSequence(Vec<U64Segment>);
52
53impl std::fmt::Display for RowIdSequence {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        let mut iter = self.iter();
56        let mut first_10 = Vec::new();
57        let mut last_10 = Vec::new();
58        for row_id in iter.by_ref() {
59            first_10.push(row_id);
60            if first_10.len() > 10 {
61                break;
62            }
63        }
64
65        while let Some(row_id) = iter.next_back() {
66            last_10.push(row_id);
67            if last_10.len() > 10 {
68                break;
69            }
70        }
71        last_10.reverse();
72
73        let theres_more = iter.next().is_some();
74
75        write!(f, "[")?;
76        for row_id in first_10 {
77            write!(f, "{}", row_id)?;
78        }
79        if theres_more {
80            write!(f, ", ...")?;
81        }
82        for row_id in last_10 {
83            write!(f, ", {}", row_id)?;
84        }
85        write!(f, "]")
86    }
87}
88
89impl From<Range<u64>> for RowIdSequence {
90    fn from(range: Range<u64>) -> Self {
91        Self(vec![U64Segment::Range(range)])
92    }
93}
94
95impl From<&[u64]> for RowIdSequence {
96    fn from(row_ids: &[u64]) -> Self {
97        Self(vec![U64Segment::from_slice(row_ids)])
98    }
99}
100
101/// Return some value that appears more than once in `row_ids`, if any.
102///
103/// The already-sorted case is the common one for row id sequences, and is
104/// checked in a single pass without allocating.
105fn find_duplicate(row_ids: &[u64]) -> Option<u64> {
106    if row_ids.windows(2).all(|pair| pair[0] < pair[1]) {
107        return None;
108    }
109    let mut sorted = row_ids.to_vec();
110    sorted.sort_unstable();
111    sorted
112        .windows(2)
113        .find(|pair| pair[0] == pair[1])
114        .map(|pair| pair[0])
115}
116
117impl RowIdSequence {
118    pub fn new() -> Self {
119        Self::default()
120    }
121
122    /// Build a sequence from row ids, rejecting duplicates within the sequence.
123    ///
124    /// The segment encodings represent a sorted run as a range plus its holes,
125    /// so a repeated value would be silently encoded as a shorter sequence with
126    /// a spurious hole. Callers assembling a sequence from untrusted input
127    /// should use this instead of the infallible `From` conversions, which
128    /// assume uniqueness.
129    ///
130    /// Row ids must also be unique across the dataset. That is not checked
131    /// here, and commit does not re-check it either.
132    pub fn try_from_iter(row_ids: impl IntoIterator<Item = u64>) -> Result<Self> {
133        let row_ids: Vec<u64> = row_ids.into_iter().collect();
134        if row_ids.is_empty() {
135            return Ok(Self::new());
136        }
137        if let Some(duplicate) = find_duplicate(&row_ids) {
138            return Err(Error::invalid_input(format!(
139                "Row ids must be unique, but row id {} appears more than once in the sequence of {} row ids",
140                duplicate,
141                row_ids.len()
142            )));
143        }
144        Ok(Self(vec![U64Segment::from_iter(row_ids)]))
145    }
146
147    pub fn iter(&self) -> impl DoubleEndedIterator<Item = u64> + '_ {
148        self.0.iter().flat_map(|segment| segment.iter())
149    }
150
151    pub fn len(&self) -> u64 {
152        self.0.iter().map(|segment| segment.len() as u64).sum()
153    }
154
155    pub fn is_empty(&self) -> bool {
156        self.0.is_empty()
157    }
158
159    /// Returns the bounding range `[min, max]` across all row IDs in this sequence,
160    /// or `None` if the sequence contains no values.
161    ///
162    /// This is a conservative bounding box: a value falling within the returned range
163    /// is not guaranteed to exist in the sequence (segments may be sparse), but any
164    /// value that *does* exist is guaranteed to fall within the range.  This makes
165    /// the result suitable as a cheap pre-filter before a full scan.
166    pub fn row_id_range(&self) -> Option<RangeInclusive<u64>> {
167        let min = self
168            .0
169            .iter()
170            .filter_map(|s| s.range())
171            .map(|r| *r.start())
172            .min()?;
173        let max = self
174            .0
175            .iter()
176            .filter_map(|s| s.range())
177            .map(|r| *r.end())
178            .max()?;
179        Some(min..=max)
180    }
181
182    /// Combines this row id sequence with another row id sequence.
183    pub fn extend(&mut self, other: Self) {
184        // If the last element of this sequence and the first element of next
185        // sequence are ranges, we might be able to combine them into a single
186        // range.
187        if let (Some(U64Segment::Range(range1)), Some(U64Segment::Range(range2))) =
188            (self.0.last(), other.0.first())
189            && range1.end == range2.start
190        {
191            let new_range = U64Segment::Range(range1.start..range2.end);
192            self.0.pop();
193            self.0.push(new_range);
194            self.0.extend(other.0.into_iter().skip(1));
195            return;
196        }
197        // TODO: add other optimizations, such as combining two RangeWithHoles.
198        self.0.extend(other.0);
199    }
200
201    /// Remove a set of row ids from the sequence.
202    pub fn delete(&mut self, row_ids: impl IntoIterator<Item = u64>) {
203        // Order the row ids by position in which they appear in the sequence.
204        let (row_ids, offsets) = self.find_ids(row_ids);
205
206        let capacity = self.0.capacity();
207        let old_segments = std::mem::replace(&mut self.0, Vec::with_capacity(capacity));
208        let mut remaining_segments = old_segments.as_slice();
209
210        for (segment_idx, range) in offsets {
211            let segments_handled = old_segments.len() - remaining_segments.len();
212            let segments_to_add = segment_idx - segments_handled;
213            self.0
214                .extend_from_slice(&remaining_segments[..segments_to_add]);
215            remaining_segments = &remaining_segments[segments_to_add..];
216
217            let segment;
218            (segment, remaining_segments) = remaining_segments.split_first().unwrap();
219
220            let segment_ids = &row_ids[range];
221            self.0.push(segment.delete(segment_ids));
222        }
223
224        // Add the remaining segments.
225        self.0.extend_from_slice(remaining_segments);
226    }
227
228    /// Delete row ids by position.
229    pub fn mask(&mut self, positions: impl IntoIterator<Item = u32>) -> Result<()> {
230        let mut local_positions = Vec::new();
231        let mut positions_iter = positions.into_iter();
232        let mut curr_position = positions_iter.next();
233        let mut offset = 0;
234        let mut cutoff = 0;
235
236        for segment in &mut self.0 {
237            // Make vector of local positions
238            cutoff += segment.len() as u32;
239            while let Some(position) = curr_position {
240                if position >= cutoff {
241                    break;
242                }
243                local_positions.push(position - offset);
244                curr_position = positions_iter.next();
245            }
246
247            if !local_positions.is_empty() {
248                segment.mask(&local_positions);
249                local_positions.clear();
250            }
251            offset = cutoff;
252        }
253
254        self.0.retain(|segment| !segment.is_empty());
255
256        Ok(())
257    }
258
259    /// Find the row ids in the sequence.
260    ///
261    /// Returns the row ids sorted by their appearance in the sequence.
262    /// Also returns the segment index and the range where that segment's
263    /// row id matches are found in the returned row id vector.
264    fn find_ids(
265        &self,
266        row_ids: impl IntoIterator<Item = u64>,
267    ) -> (Vec<u64>, Vec<(usize, Range<usize>)>) {
268        // Often, the row ids will already be provided in the order they appear.
269        // So the optimal way to search will be to cycle through rather than
270        // restarting the search from the beginning each time.
271        let mut segment_iter = self.0.iter().enumerate().cycle();
272
273        let mut segment_matches = vec![Vec::new(); self.0.len()];
274
275        row_ids.into_iter().for_each(|row_id| {
276            let mut i = 0;
277            // If we've cycled through all segments, we know the row id is not in the sequence.
278            while i < self.0.len() {
279                let (segment_idx, segment) = segment_iter.next().unwrap();
280                if segment.range().is_some_and(|range| range.contains(&row_id))
281                    && let Some(offset) = segment.position(row_id)
282                {
283                    segment_matches.get_mut(segment_idx).unwrap().push(offset);
284                    // The row id was not found it the segment. It might be in a later segment.
285                }
286                i += 1;
287            }
288        });
289        for matches in &mut segment_matches {
290            matches.sort_unstable();
291        }
292
293        let mut offset = 0;
294        let segment_ranges = segment_matches
295            .iter()
296            .enumerate()
297            .filter(|(_, matches)| !matches.is_empty())
298            .map(|(segment_idx, matches)| {
299                let range = offset..offset + matches.len();
300                offset += matches.len();
301                (segment_idx, range)
302            })
303            .collect();
304        let row_ids = segment_matches
305            .into_iter()
306            .enumerate()
307            .flat_map(|(segment_idx, offset)| {
308                offset
309                    .into_iter()
310                    .map(move |offset| self.0[segment_idx].get(offset).unwrap())
311            })
312            .collect();
313
314        (row_ids, segment_ranges)
315    }
316
317    pub fn slice(&self, offset: usize, len: usize) -> RowIdSeqSlice<'_> {
318        if len == 0 {
319            return RowIdSeqSlice {
320                segments: &[],
321                offset_start: 0,
322                offset_last: 0,
323            };
324        }
325
326        // Find the starting position
327        let mut offset_start = offset;
328        let mut segment_offset = 0;
329        for segment in &self.0 {
330            let segment_len = segment.len();
331            if offset_start < segment_len {
332                break;
333            }
334            offset_start -= segment_len;
335            segment_offset += 1;
336        }
337
338        // Find the ending position
339        let mut offset_last = offset_start + len;
340        let mut segment_offset_last = segment_offset;
341        for segment in &self.0[segment_offset..] {
342            let segment_len = segment.len();
343            if offset_last <= segment_len {
344                break;
345            }
346            offset_last -= segment_len;
347            segment_offset_last += 1;
348        }
349
350        RowIdSeqSlice {
351            segments: &self.0[segment_offset..=segment_offset_last],
352            offset_start,
353            offset_last,
354        }
355    }
356
357    /// Get the row id at the given index.
358    ///
359    /// If the index is out of bounds, this will return None.
360    /// The segments backing the sequence, in offset order.
361    pub fn segments(&self) -> &[U64Segment] {
362        &self.0
363    }
364
365    pub fn get(&self, index: usize) -> Option<u64> {
366        let mut offset = 0;
367        for segment in &self.0 {
368            let segment_len = segment.len();
369            if index < offset + segment_len {
370                return segment.get(index - offset);
371            }
372            offset += segment_len;
373        }
374        None
375    }
376
377    /// Get row ids from the sequence based on the provided _sorted_ offsets
378    ///
379    /// Any out of bounds offsets will be ignored
380    ///
381    /// # Panics
382    ///
383    /// If the input selection is not sorted, this function will panic
384    pub fn select<'a>(
385        &'a self,
386        selection: impl Iterator<Item = usize> + 'a,
387    ) -> impl Iterator<Item = u64> + 'a {
388        let mut seg_iter = self.0.iter();
389        let mut cur_seg = seg_iter.next();
390        let mut rows_passed = 0;
391        let mut cur_seg_len = cur_seg.map(|seg| seg.len()).unwrap_or(0);
392        let mut cursor = cur_seg.map(|seg| seg.cursor());
393        let mut last_index = 0;
394        selection.filter_map(move |index| {
395            if index < last_index {
396                panic!("Selection is not sorted");
397            }
398            last_index = index;
399
400            cur_seg?;
401
402            while (index - rows_passed) >= cur_seg_len {
403                rows_passed += cur_seg_len;
404                cur_seg = seg_iter.next();
405                cur_seg_len = cur_seg?.len();
406                cursor = cur_seg.map(|seg| seg.cursor());
407            }
408
409            let value = cursor.as_mut().unwrap().get(index - rows_passed);
410            debug_assert!(
411                value.is_some(),
412                "segment reported {cur_seg_len} rows but has no value at {}",
413                index - rows_passed
414            );
415            value
416        })
417    }
418
419    /// Given a mask of row ids, calculate the offset ranges of the row ids that are present
420    /// in the sequence.
421    ///
422    /// For example, given a mask that selects all even ids and a sequence that is
423    /// [80..85, 86..90, 14]
424    ///
425    /// this will return [0, 2, 4, 5, 7, 9]
426    /// because the range expands to
427    ///
428    /// [80, 81, 82, 83, 84, 86, 87, 88, 89, 14] with offsets
429    /// [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]
430    ///
431    /// This function is useful when determining which row offsets to read from a fragment given
432    /// a mask.
433    #[instrument(level = "debug", skip_all)]
434    pub fn mask_to_offset_ranges(&self, mask: &RowAddrMask) -> Vec<Range<u64>> {
435        let mut offset = 0;
436        let mut ranges = Vec::new();
437        for segment in &self.0 {
438            match segment {
439                U64Segment::Range(range) => {
440                    let mut ids = RowAddrTreeMap::from(range.clone());
441                    ids.mask(mask);
442                    // Range-aware path: walk the bitmap's runs directly via
443                    // iter_runs so the per-row cost collapses to per-run cost.
444                    let mut cur: Option<Range<u64>> = None;
445                    for (fragment, run) in ids.iter_runs() {
446                        let frag = u64::from(fragment);
447                        let run_start = (frag << 32) | u64::from(*run.start());
448                        let run_end_excl = (frag << 32) | (u64::from(*run.end()) + 1);
449                        let start = run_start - range.start + offset;
450                        let end = run_end_excl - range.start + offset;
451                        match cur.as_mut() {
452                            Some(c) if c.end == start => c.end = end,
453                            Some(c) => {
454                                ranges.push(std::mem::replace(c, start..end));
455                            }
456                            None => cur = Some(start..end),
457                        }
458                    }
459                    if let Some(c) = cur {
460                        ranges.push(c);
461                    }
462                    offset += range.end - range.start;
463                }
464                U64Segment::RangeWithHoles { range, holes } => {
465                    let offset_start = offset;
466                    let mut ids = RowAddrTreeMap::from(range.clone());
467                    offset += range.end - range.start;
468                    for hole in holes.iter() {
469                        if ids.remove(hole) {
470                            offset -= 1;
471                        }
472                    }
473                    ids.mask(mask);
474
475                    // Sadly we can't just subtract the offset because of the holes
476                    let mut sorted_holes = holes.clone().into_iter().collect::<Vec<_>>();
477                    sorted_holes.sort_unstable();
478                    let mut next_holes_iter = sorted_holes.into_iter().peekable();
479                    let mut holes_passed = 0;
480                    ranges.extend(GroupingIterator::new(ids.into_addr_iter().map(|addr| {
481                        while let Some(next_hole) = next_holes_iter.peek() {
482                            if *next_hole < addr {
483                                next_holes_iter.next();
484                                holes_passed += 1;
485                            } else {
486                                break;
487                            }
488                        }
489                        addr - range.start + offset_start - holes_passed
490                    })));
491                }
492                U64Segment::RangeWithBitmap { range, bitmap } => {
493                    let mut ids = RowAddrTreeMap::from(range.clone());
494                    let offset_start = offset;
495                    offset += range.end - range.start;
496                    for (i, val) in range.clone().enumerate() {
497                        if !bitmap.get(i) && ids.remove(val) {
498                            offset -= 1;
499                        }
500                    }
501                    ids.mask(mask);
502                    let mut bitmap_iter = bitmap.iter();
503                    let mut bitmap_iter_pos = 0;
504                    let mut holes_passed = 0;
505                    ranges.extend(GroupingIterator::new(ids.into_addr_iter().map(|addr| {
506                        let position_in_range = addr - range.start;
507                        while bitmap_iter_pos < position_in_range {
508                            if !bitmap_iter.next().unwrap() {
509                                holes_passed += 1;
510                            }
511                            bitmap_iter_pos += 1;
512                        }
513                        offset_start + position_in_range - holes_passed
514                    })));
515                }
516                U64Segment::SortedArray(array) | U64Segment::Array(array) => {
517                    // TODO: Could probably optimize the sorted array case to be O(N) instead of O(N log N)
518                    ranges.extend(GroupingIterator::new(array.iter().enumerate().filter_map(
519                        |(off, id)| {
520                            if mask.selected(id) {
521                                Some(off as u64 + offset)
522                            } else {
523                                None
524                            }
525                        },
526                    )));
527                    offset += array.len() as u64;
528                }
529            }
530        }
531        ranges
532    }
533}
534
535/// An iterator that groups row ids into ranges
536///
537/// For example, given an input iterator of [1, 2, 3, 5, 6, 7, 10, 11, 12]
538/// this will return an iterator of [(1..4), (5..8), (10..13)]
539struct GroupingIterator<I: Iterator<Item = u64>> {
540    iter: I,
541    cur_range: Option<Range<u64>>,
542}
543
544impl<I: Iterator<Item = u64>> GroupingIterator<I> {
545    fn new(iter: I) -> Self {
546        Self {
547            iter,
548            cur_range: None,
549        }
550    }
551}
552
553impl<I: Iterator<Item = u64>> Iterator for GroupingIterator<I> {
554    type Item = Range<u64>;
555
556    fn next(&mut self) -> Option<Self::Item> {
557        for id in self.iter.by_ref() {
558            if let Some(range) = self.cur_range.as_mut() {
559                if range.end == id {
560                    range.end = id + 1;
561                } else {
562                    let ret = Some(range.clone());
563                    self.cur_range = Some(id..id + 1);
564                    return ret;
565                }
566            } else {
567                self.cur_range = Some(id..id + 1);
568            }
569        }
570        self.cur_range.take()
571    }
572}
573
574impl From<&RowIdSequence> for RowAddrTreeMap {
575    fn from(row_ids: &RowIdSequence) -> Self {
576        let mut tree_map = Self::new();
577        for segment in &row_ids.0 {
578            let mut seg = Self::new();
579            match segment {
580                U64Segment::Range(range) => {
581                    seg.insert_range(range.clone());
582                }
583                U64Segment::RangeWithBitmap { range, bitmap } => {
584                    seg.insert_range(range.clone());
585                    for (i, val) in range.clone().enumerate() {
586                        if !bitmap.get(i) {
587                            seg.remove(val);
588                        }
589                    }
590                }
591                U64Segment::RangeWithHoles { range, holes } => {
592                    seg.insert_range(range.clone());
593                    for hole in holes.iter() {
594                        seg.remove(hole);
595                    }
596                }
597                U64Segment::SortedArray(array) | U64Segment::Array(array) => {
598                    for val in array.iter() {
599                        seg.insert(val);
600                    }
601                }
602            }
603            tree_map |= seg;
604        }
605        tree_map
606    }
607}
608
609#[derive(Debug)]
610pub struct RowIdSeqSlice<'a> {
611    /// Current slice of the segments we cover
612    segments: &'a [U64Segment],
613    /// Offset into the first segment to start iterating from
614    offset_start: usize,
615    /// Offset into the last segment to stop iterating at
616    offset_last: usize,
617}
618
619impl RowIdSeqSlice<'_> {
620    pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
621        let mut known_size = self.segments.iter().map(|segment| segment.len()).sum();
622        known_size -= self.offset_start;
623        known_size -= self.segments.last().map(|s| s.len()).unwrap_or_default() - self.offset_last;
624
625        let end = self.segments.len().saturating_sub(1);
626        self.segments
627            .iter()
628            .enumerate()
629            .flat_map(move |(i, segment)| {
630                match i {
631                    0 if self.segments.len() == 1 => {
632                        let len = self.offset_last - self.offset_start;
633                        // TODO: Optimize this so we don't have to use skip
634                        // (take is probably fine though.)
635                        Box::new(segment.iter().skip(self.offset_start).take(len))
636                            as Box<dyn Iterator<Item = u64>>
637                    }
638                    0 => Box::new(segment.iter().skip(self.offset_start)),
639                    i if i == end => Box::new(segment.iter().take(self.offset_last)),
640                    _ => Box::new(segment.iter()),
641                }
642            })
643            .exact_size(known_size)
644    }
645}
646
647/// Re-chunk a sequences of row ids into chunks of a given size.
648///
649/// The sequences may less than chunk sizes, because the sequences only
650/// contains the row ids that we want to keep, they come from the updates records.
651/// But the chunk sizes are based on the fragment physical rows(may contain inserted records).
652/// So if the sequences are smaller than the chunk sizes, we need to
653/// assign the incremental row ids in the further step. This behavior is controlled by the
654/// `allow_incomplete` parameter.
655///
656/// # Errors
657///
658/// If `allow_incomplete` is false, will return an error if the sum of the chunk sizes
659/// is not equal to the total number of row ids in the sequences.
660pub fn rechunk_sequences(
661    sequences: impl IntoIterator<Item = RowIdSequence>,
662    chunk_sizes: impl IntoIterator<Item = u64>,
663    allow_incomplete: bool,
664) -> Result<Vec<RowIdSequence>> {
665    // TODO: return an iterator. (with a good size hint?)
666    let chunk_sizes_vec: Vec<u64> = chunk_sizes.into_iter().collect();
667    let total_chunks = chunk_sizes_vec.len();
668    let mut chunked_sequences = Vec::with_capacity(total_chunks);
669    let mut segment_iter = sequences
670        .into_iter()
671        .flat_map(|sequence| sequence.0.into_iter())
672        .peekable();
673
674    let too_few_segments_error = |chunk_index: usize, expected_chunk_size: u64, remaining: u64| {
675        Error::invalid_input(format!(
676            "Got too few segments for chunk {}. Expected chunk size: {}, remaining needed: {}",
677            chunk_index, expected_chunk_size, remaining
678        ))
679    };
680
681    let too_many_segments_error = |processed_chunks: usize, total_chunk_sizes: usize| {
682        Error::invalid_input(format!(
683            "Got too many segments for the provided chunk lengths. Processed {} chunks out of {} expected",
684            processed_chunks, total_chunk_sizes
685        ))
686    };
687
688    let mut segment_offset = 0_u64;
689
690    for (chunk_index, chunk_size) in chunk_sizes_vec.iter().enumerate() {
691        let chunk_size = *chunk_size;
692        let mut sequence = RowIdSequence(Vec::new());
693        let mut remaining = chunk_size;
694
695        while remaining > 0 {
696            let remaining_in_segment = segment_iter
697                .peek()
698                .map_or(0, |segment| segment.len() as u64 - segment_offset);
699
700            // Step 1: Handle segment remaining to be empty(also empty seg) - skip and continue
701            if remaining_in_segment == 0 {
702                if segment_iter.next().is_some() {
703                    segment_offset = 0;
704                    continue;
705                } else {
706                    // No more segments available
707                    if allow_incomplete {
708                        break;
709                    } else {
710                        return Err(too_few_segments_error(chunk_index, chunk_size, remaining));
711                    }
712                }
713            }
714
715            // Step 2: Handle still remaining segment based on size comparison
716            match remaining_in_segment.cmp(&remaining) {
717                std::cmp::Ordering::Greater => {
718                    // Segment is larger than remaining space - slice it
719                    let segment = segment_iter
720                        .peek()
721                        .ok_or_else(|| too_few_segments_error(chunk_index, chunk_size, remaining))?
722                        .slice(segment_offset as usize, remaining as usize);
723                    sequence.extend(RowIdSequence(vec![segment]));
724                    segment_offset += remaining;
725                    remaining = 0;
726                }
727                std::cmp::Ordering::Equal | std::cmp::Ordering::Less => {
728                    // UNIFIED HANDLING: Both equal and less cases subtract from remaining
729                    // Equal case: remaining -= remaining_in_segment (remaining becomes 0)
730                    // Less case: remaining -= remaining_in_segment (remaining becomes positive)
731                    let segment = segment_iter
732                        .next()
733                        .ok_or_else(|| too_few_segments_error(chunk_index, chunk_size, remaining))?
734                        .slice(segment_offset as usize, remaining_in_segment as usize);
735                    sequence.extend(RowIdSequence(vec![segment]));
736                    segment_offset = 0;
737                    remaining -= remaining_in_segment;
738                }
739            }
740        }
741
742        chunked_sequences.push(sequence);
743    }
744
745    if segment_iter.peek().is_some() {
746        return Err(too_many_segments_error(
747            chunked_sequences.len(),
748            total_chunks,
749        ));
750    }
751
752    Ok(chunked_sequences)
753}
754
755/// Selects the row ids from a sequence based on the provided offsets.
756pub fn select_row_ids<'a>(
757    sequence: &'a RowIdSequence,
758    offsets: &'a ReadBatchParams,
759) -> Result<Vec<u64>> {
760    let out_of_bounds_err = |offset: u32| {
761        Error::invalid_input(format!(
762            "Index out of bounds: {} for sequence of length {}",
763            offset,
764            sequence.len()
765        ))
766    };
767
768    match offsets {
769        ReadBatchParams::Indices(indices) => {
770            let indices = indices.values();
771            if indices.windows(2).all(|pair| pair[0] <= pair[1]) {
772                // `select` drops out-of-bounds indices instead of erroring.
773                if let Some(&last) = indices.last()
774                    && last as u64 >= sequence.len()
775                {
776                    return Err(out_of_bounds_err(last));
777                }
778                return Ok(sequence
779                    .select(indices.iter().map(|&index| index as usize))
780                    .collect());
781            }
782            indices
783                .iter()
784                .map(|index| {
785                    sequence
786                        .get(*index as usize)
787                        .ok_or_else(|| out_of_bounds_err(*index))
788                })
789                .collect()
790        }
791        ReadBatchParams::Range(range) => {
792            if range.end > sequence.len() as usize {
793                return Err(out_of_bounds_err(range.end as u32));
794            }
795            let sequence = sequence.slice(range.start, range.end - range.start);
796            Ok(sequence.iter().collect())
797        }
798        ReadBatchParams::Ranges(ranges) => {
799            let num_rows = ranges
800                .iter()
801                .map(|r| (r.end - r.start) as usize)
802                .sum::<usize>();
803            let mut result = Vec::with_capacity(num_rows);
804            for range in ranges.as_ref() {
805                if range.end > sequence.len() {
806                    return Err(out_of_bounds_err(range.end as u32));
807                }
808                let sequence =
809                    sequence.slice(range.start as usize, (range.end - range.start) as usize);
810                result.extend(sequence.iter());
811            }
812            Ok(result)
813        }
814
815        ReadBatchParams::RangeFull => Ok(sequence.iter().collect()),
816        ReadBatchParams::RangeTo(to) => {
817            if to.end > sequence.len() as usize {
818                return Err(out_of_bounds_err(to.end as u32));
819            }
820            let len = to.end;
821            let sequence = sequence.slice(0, len);
822            Ok(sequence.iter().collect())
823        }
824        ReadBatchParams::RangeFrom(from) => {
825            let sequence = sequence.slice(from.start, sequence.len() as usize - from.start);
826            Ok(sequence.iter().collect())
827        }
828    }
829}
830
831#[cfg(test)]
832mod test {
833    use super::*;
834
835    use pretty_assertions::assert_eq;
836    use test::bitmap::Bitmap;
837
838    #[test]
839    fn test_row_id_sequence_from_range() {
840        let sequence = RowIdSequence::from(0..10);
841        assert_eq!(sequence.len(), 10);
842        assert_eq!(sequence.is_empty(), false);
843
844        let iter = sequence.iter();
845        assert_eq!(iter.collect::<Vec<_>>(), (0..10).collect::<Vec<_>>());
846    }
847
848    #[rstest::rstest]
849    #[case::sorted_contiguous(vec![0, 1, 2, 3])]
850    #[case::sorted_with_gaps(vec![0, 2, 4])]
851    #[case::sparse(vec![0, 1_000_000])]
852    #[case::unsorted(vec![12, 11, 10])]
853    fn test_row_id_sequence_try_from_iter(#[case] row_ids: Vec<u64>) {
854        let sequence = RowIdSequence::try_from_iter(row_ids.clone()).unwrap();
855        assert_eq!(sequence.len(), row_ids.len() as u64);
856        assert_eq!(sequence.iter().collect::<Vec<_>>(), row_ids);
857    }
858
859    #[test]
860    fn test_row_id_sequence_try_from_iter_contiguous_is_a_range() {
861        let sequence = RowIdSequence::try_from_iter(0..10).unwrap();
862        assert_eq!(sequence.0, vec![U64Segment::Range(0..10)]);
863    }
864
865    #[test]
866    fn test_row_id_sequence_try_from_iter_empty() {
867        let sequence = RowIdSequence::try_from_iter(std::iter::empty()).unwrap();
868        assert_eq!(sequence.len(), 0);
869        assert!(sequence.is_empty());
870    }
871
872    #[rstest::rstest]
873    #[case::adjacent(vec![1, 1, 2])]
874    #[case::separated(vec![1, 2, 3, 1])]
875    #[case::unsorted(vec![5, 3, 5])]
876    fn test_row_id_sequence_try_from_iter_rejects_duplicates(#[case] row_ids: Vec<u64>) {
877        // Without validation these encode to a shorter sequence with a spurious
878        // hole rather than failing, so assert the error rather than the output.
879        let error = RowIdSequence::try_from_iter(row_ids).unwrap_err();
880        assert!(
881            matches!(error, Error::InvalidInput { .. }),
882            "expected InvalidInput, got {:?}",
883            error
884        );
885        assert!(
886            error.to_string().contains("must be unique"),
887            "unexpected message: {}",
888            error
889        );
890    }
891
892    #[test]
893    fn test_row_id_sequence_extend() {
894        let mut sequence = RowIdSequence::from(0..10);
895        sequence.extend(RowIdSequence::from(10..20));
896        assert_eq!(sequence.0, vec![U64Segment::Range(0..20)]);
897
898        let mut sequence = RowIdSequence::from(0..10);
899        sequence.extend(RowIdSequence::from(20..30));
900        assert_eq!(
901            sequence.0,
902            vec![U64Segment::Range(0..10), U64Segment::Range(20..30)]
903        );
904    }
905
906    #[test]
907    fn test_row_id_sequence_delete() {
908        let mut sequence = RowIdSequence::from(0..10);
909        sequence.delete(vec![1, 3, 5, 7, 9]);
910        let mut expected_bitmap = Bitmap::new_empty(9);
911        for i in [0, 2, 4, 6, 8] {
912            expected_bitmap.set(i as usize);
913        }
914        assert_eq!(
915            sequence.0,
916            vec![U64Segment::RangeWithBitmap {
917                range: 0..9,
918                bitmap: expected_bitmap
919            },]
920        );
921
922        let mut sequence = RowIdSequence::from(0..10);
923        sequence.extend(RowIdSequence::from(12..20));
924        sequence.delete(vec![0, 9, 10, 11, 12, 13]);
925        assert_eq!(
926            sequence.0,
927            vec![U64Segment::Range(1..9), U64Segment::Range(14..20),]
928        );
929
930        let mut sequence = RowIdSequence::from(0..10);
931        sequence.delete(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
932        assert_eq!(sequence.0, vec![U64Segment::Range(0..0)]);
933    }
934
935    #[test]
936    fn test_row_id_slice() {
937        // The type of sequence isn't that relevant to the implementation, so
938        // we can just have a single one with all the segment types.
939        let sequence = RowIdSequence(vec![
940            U64Segment::Range(30..35), // 5
941            U64Segment::RangeWithHoles {
942                // 8
943                range: 50..60,
944                holes: vec![53, 54].into(),
945            },
946            U64Segment::SortedArray(vec![7, 9].into()), // 2
947            U64Segment::RangeWithBitmap {
948                range: 0..5,
949                bitmap: [true, false, true, false, true].as_slice().into(),
950            },
951            U64Segment::Array(vec![35, 39].into()),
952            U64Segment::Range(40..50),
953        ]);
954
955        // All possible offsets and lengths
956        for offset in 0..sequence.len() as usize {
957            for len in 0..sequence.len() as usize {
958                if offset + len > sequence.len() as usize {
959                    continue;
960                }
961                let slice = sequence.slice(offset, len);
962
963                let actual = slice.iter().collect::<Vec<_>>();
964                let expected = sequence.iter().skip(offset).take(len).collect::<Vec<_>>();
965                assert_eq!(
966                    actual, expected,
967                    "Failed for offset {} and len {}",
968                    offset, len
969                );
970
971                let (claimed_size, claimed_max) = slice.iter().size_hint();
972                assert_eq!(claimed_max, Some(claimed_size)); // Exact size hint
973                assert_eq!(claimed_size, actual.len()); // Correct size hint
974            }
975        }
976    }
977
978    #[test]
979    fn test_row_id_slice_empty() {
980        let sequence = RowIdSequence::from(0..10);
981        let slice = sequence.slice(10, 0);
982        assert_eq!(slice.iter().collect::<Vec<_>>(), Vec::<u64>::new());
983    }
984
985    #[test]
986    fn test_row_id_sequence_rechunk() {
987        fn assert_rechunked(
988            input: Vec<RowIdSequence>,
989            chunk_sizes: Vec<u64>,
990            expected: Vec<RowIdSequence>,
991        ) {
992            let chunked = rechunk_sequences(input, chunk_sizes, false).unwrap();
993            assert_eq!(chunked, expected);
994        }
995
996        // Small pieces to larger ones
997        let many_segments = vec![
998            RowIdSequence(vec![U64Segment::Range(0..5), U64Segment::Range(35..40)]),
999            RowIdSequence::from(10..18),
1000            RowIdSequence::from(18..28),
1001            RowIdSequence::from(28..30),
1002        ];
1003        let fewer_segments = vec![
1004            RowIdSequence(vec![U64Segment::Range(0..5), U64Segment::Range(35..40)]),
1005            RowIdSequence::from(10..30),
1006        ];
1007        assert_rechunked(
1008            many_segments.clone(),
1009            fewer_segments.iter().map(|seq| seq.len()).collect(),
1010            fewer_segments.clone(),
1011        );
1012
1013        // Large pieces to smaller ones
1014        assert_rechunked(
1015            fewer_segments,
1016            many_segments.iter().map(|seq| seq.len()).collect(),
1017            many_segments.clone(),
1018        );
1019
1020        // Equal pieces
1021        assert_rechunked(
1022            many_segments.clone(),
1023            many_segments.iter().map(|seq| seq.len()).collect(),
1024            many_segments.clone(),
1025        );
1026
1027        // Too few segments -> error
1028        let result = rechunk_sequences(many_segments.clone(), vec![100], false);
1029        assert!(result.is_err());
1030
1031        // Too many segments -> error
1032        let result = rechunk_sequences(many_segments, vec![5], false);
1033        assert!(result.is_err());
1034    }
1035
1036    #[test]
1037    fn test_select_row_ids() {
1038        // All forms of offsets
1039        let offsets = [
1040            ReadBatchParams::Indices(vec![1, 3, 9, 5, 7, 6].into()),
1041            ReadBatchParams::Indices(vec![1, 3, 5, 6, 7, 9].into()),
1042            ReadBatchParams::Range(2..8),
1043            ReadBatchParams::RangeFull,
1044            ReadBatchParams::RangeTo(..5),
1045            ReadBatchParams::RangeFrom(5..),
1046            ReadBatchParams::Ranges(vec![2..3, 5..10].into()),
1047        ];
1048
1049        // Sequences with all segment types. These have at least 10 elements,
1050        // so they are valid for all the above offsets.
1051        let sequences = [
1052            RowIdSequence(vec![
1053                U64Segment::Range(0..5),
1054                U64Segment::RangeWithHoles {
1055                    range: 50..60,
1056                    holes: vec![53, 54].into(),
1057                },
1058                U64Segment::SortedArray(vec![7, 9].into()),
1059            ]),
1060            RowIdSequence(vec![
1061                U64Segment::RangeWithBitmap {
1062                    range: 0..5,
1063                    bitmap: [true, false, true, false, true].as_slice().into(),
1064                },
1065                U64Segment::Array(vec![30, 20, 10].into()),
1066                U64Segment::Range(40..50),
1067            ]),
1068        ];
1069
1070        for params in offsets {
1071            for sequence in &sequences {
1072                let row_ids = select_row_ids(sequence, &params).unwrap();
1073                let flat_sequence = sequence.iter().collect::<Vec<_>>();
1074
1075                // Transform params into bounded ones
1076                let selection: Vec<usize> = match &params {
1077                    ReadBatchParams::RangeFull => (0..flat_sequence.len()).collect(),
1078                    ReadBatchParams::RangeTo(to) => (0..to.end).collect(),
1079                    ReadBatchParams::RangeFrom(from) => (from.start..flat_sequence.len()).collect(),
1080                    ReadBatchParams::Range(range) => range.clone().collect(),
1081                    ReadBatchParams::Ranges(ranges) => ranges
1082                        .iter()
1083                        .flat_map(|r| r.start as usize..r.end as usize)
1084                        .collect(),
1085                    ReadBatchParams::Indices(indices) => {
1086                        indices.values().iter().map(|i| *i as usize).collect()
1087                    }
1088                };
1089
1090                let expected = selection
1091                    .into_iter()
1092                    .map(|i| flat_sequence[i])
1093                    .collect::<Vec<_>>();
1094                assert_eq!(
1095                    row_ids, expected,
1096                    "Failed for params {:?} on the sequence {:?}",
1097                    &params, sequence
1098                );
1099            }
1100        }
1101    }
1102
1103    #[test]
1104    fn test_select_row_ids_out_of_bounds() {
1105        let offsets = [
1106            ReadBatchParams::Indices(vec![1, 1000, 4].into()),
1107            ReadBatchParams::Indices(vec![1, 4, 1000].into()),
1108            ReadBatchParams::Range(2..1000),
1109            ReadBatchParams::RangeTo(..1000),
1110        ];
1111
1112        let sequence = RowIdSequence::from(0..10);
1113
1114        for params in offsets {
1115            let result = select_row_ids(&sequence, &params);
1116            assert!(result.is_err());
1117            assert!(matches!(result.unwrap_err(), Error::InvalidInput { .. }));
1118        }
1119    }
1120
1121    #[test]
1122    fn test_row_id_sequence_to_treemap() {
1123        let sequence = RowIdSequence(vec![
1124            U64Segment::Range(0..5),
1125            U64Segment::RangeWithHoles {
1126                range: 50..60,
1127                holes: vec![53, 54].into(),
1128            },
1129            U64Segment::SortedArray(vec![7, 9].into()),
1130            U64Segment::RangeWithBitmap {
1131                range: 10..15,
1132                bitmap: [true, false, true, false, true].as_slice().into(),
1133            },
1134            U64Segment::Array(vec![35, 39].into()),
1135            U64Segment::Range(40..50),
1136        ]);
1137
1138        let tree_map = RowAddrTreeMap::from(&sequence);
1139        let expected = vec![
1140            0, 1, 2, 3, 4, 7, 9, 10, 12, 14, 35, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
1141            51, 52, 55, 56, 57, 58, 59,
1142        ]
1143        .into_iter()
1144        .collect::<RowAddrTreeMap>();
1145        assert_eq!(tree_map, expected);
1146    }
1147
1148    #[test]
1149    fn test_row_id_sequence_to_treemap_overlapping_segments() {
1150        // Compaction can concatenate segments whose ranges overlap but whose
1151        // selected ids are disjoint (here: even ids, then odd ids over 0..6).
1152        // The tree map must contain every id the sequence yields.
1153        let sequence = RowIdSequence(vec![
1154            U64Segment::RangeWithBitmap {
1155                range: 0..6,
1156                bitmap: [true, false, true, false, true, false].as_slice().into(),
1157            },
1158            U64Segment::RangeWithBitmap {
1159                range: 0..6,
1160                bitmap: [false, true, false, true, false, true].as_slice().into(),
1161            },
1162        ]);
1163
1164        let expected = sequence.iter().collect::<RowAddrTreeMap>();
1165        assert_eq!(expected, (0..6).collect::<RowAddrTreeMap>());
1166        assert_eq!(RowAddrTreeMap::from(&sequence), expected);
1167    }
1168
1169    #[test]
1170    fn test_row_addr_mask() {
1171        // 0, 1, 2, 3, 4
1172        // 50, 51, 52, 55, 56, 57, 58, 59
1173        // 7, 9
1174        // 10, 12, 14
1175        // 35, 39
1176        let sequence = RowIdSequence(vec![
1177            U64Segment::Range(0..5),
1178            U64Segment::RangeWithHoles {
1179                range: 50..60,
1180                holes: vec![53, 54].into(),
1181            },
1182            U64Segment::SortedArray(vec![7, 9].into()),
1183            U64Segment::RangeWithBitmap {
1184                range: 10..15,
1185                bitmap: [true, false, true, false, true].as_slice().into(),
1186            },
1187            U64Segment::Array(vec![35, 39].into()),
1188        ]);
1189
1190        // Masking one in each segment
1191        let values_to_remove = [4, 55, 7, 12, 39];
1192        let positions_to_remove = sequence
1193            .iter()
1194            .enumerate()
1195            .filter_map(|(i, val)| {
1196                if values_to_remove.contains(&val) {
1197                    Some(i as u32)
1198                } else {
1199                    None
1200                }
1201            })
1202            .collect::<Vec<_>>();
1203        let mut sequence = sequence;
1204        sequence.mask(positions_to_remove).unwrap();
1205        let expected = RowIdSequence(vec![
1206            U64Segment::Range(0..4),
1207            U64Segment::RangeWithBitmap {
1208                range: 50..60,
1209                bitmap: [
1210                    true, true, true, false, false, false, true, true, true, true,
1211                ]
1212                .as_slice()
1213                .into(),
1214            },
1215            U64Segment::Range(9..10),
1216            U64Segment::RangeWithBitmap {
1217                range: 10..15,
1218                bitmap: [true, false, false, false, true].as_slice().into(),
1219            },
1220            U64Segment::Array(vec![35].into()),
1221        ]);
1222        assert_eq!(sequence, expected);
1223    }
1224
1225    #[test]
1226    fn test_row_addr_mask_everything() {
1227        let mut sequence = RowIdSequence(vec![
1228            U64Segment::Range(0..5),
1229            U64Segment::SortedArray(vec![7, 9].into()),
1230        ]);
1231        sequence.mask(0..sequence.len() as u32).unwrap();
1232        let expected = RowIdSequence(vec![]);
1233        assert_eq!(sequence, expected);
1234    }
1235
1236    #[test]
1237    fn test_selection() {
1238        let sequence = RowIdSequence(vec![
1239            U64Segment::Range(0..5),
1240            U64Segment::Range(10..15),
1241            U64Segment::Range(20..25),
1242        ]);
1243        let selection = sequence.select(vec![2, 4, 13, 14, 57].into_iter());
1244        assert_eq!(selection.collect::<Vec<_>>(), vec![2, 4, 23, 24]);
1245    }
1246
1247    #[test]
1248    fn test_selection_over_bitmap_segments() {
1249        let mut bitmap = Bitmap::new_full(40);
1250        for hole in [3, 4, 17, 39] {
1251            bitmap.clear(hole);
1252        }
1253        let sequence = RowIdSequence(vec![
1254            U64Segment::RangeWithBitmap {
1255                range: 100..140,
1256                bitmap,
1257            },
1258            U64Segment::Range(200..205),
1259        ]);
1260        let live: Vec<u64> = sequence.iter().collect();
1261        assert_eq!(live.len(), 41);
1262
1263        // Every index, one cursor pass.
1264        let all = sequence.select(0..live.len()).collect::<Vec<_>>();
1265        assert_eq!(all, live);
1266        // Sparse, repeated, and past-the-end indices agree with the full pass.
1267        let picks = vec![0, 2, 3, 3, 15, 16, 35, 36, 40, 99];
1268        let got = sequence.select(picks.iter().copied()).collect::<Vec<_>>();
1269        let want: Vec<u64> = picks.iter().filter_map(|&i| live.get(i).copied()).collect();
1270        assert_eq!(got, want);
1271    }
1272
1273    #[test]
1274    fn test_selection_over_a_large_bitmap_segment() {
1275        // A restart-per-index scan of this segment takes tens of seconds, so a
1276        // regression to that shows up as a test that no longer finishes quickly.
1277        const ROWS: usize = 1_000_000;
1278        let mut bitmap = Bitmap::new_full(ROWS);
1279        for hole in (0..ROWS).step_by(17) {
1280            bitmap.clear(hole);
1281        }
1282        let sequence = RowIdSequence(vec![
1283            U64Segment::Range(0..8),
1284            U64Segment::RangeWithBitmap {
1285                range: 1_000..(1_000 + ROWS as u64),
1286                bitmap,
1287            },
1288        ]);
1289        let live: Vec<u64> = sequence.iter().collect();
1290
1291        let all = sequence.select(0..live.len()).collect::<Vec<_>>();
1292        assert_eq!(all, live);
1293
1294        // Byte-boundary and tail indices, read through one cursor.
1295        let mut picks: Vec<usize> = [0, 7, 8, 9, 15, 16, 63, 64, 65]
1296            .into_iter()
1297            .chain((0..live.len()).step_by(9973))
1298            .chain([live.len() - 1, live.len()])
1299            .collect();
1300        picks.sort_unstable();
1301        let got = sequence.select(picks.iter().copied()).collect::<Vec<_>>();
1302        let want: Vec<u64> = picks.iter().filter_map(|&i| live.get(i).copied()).collect();
1303        assert_eq!(got, want);
1304    }
1305
1306    #[test]
1307    #[should_panic(expected = "Selection is not sorted")]
1308    fn test_selection_unsorted() {
1309        let sequence = RowIdSequence(vec![
1310            U64Segment::Range(0..5),
1311            U64Segment::Range(10..15),
1312            U64Segment::Range(20..25),
1313        ]);
1314        let _ = sequence
1315            .select(vec![2, 4, 3].into_iter())
1316            .collect::<Vec<_>>();
1317    }
1318
1319    #[test]
1320    fn test_mask_to_offset_ranges() {
1321        // Tests with a simple range segment
1322        let sequence = RowIdSequence(vec![U64Segment::Range(0..10)]);
1323        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[0, 2, 4, 6, 8]));
1324        let ranges = sequence.mask_to_offset_ranges(&mask);
1325        assert_eq!(ranges, vec![0..1, 2..3, 4..5, 6..7, 8..9]);
1326
1327        let sequence = RowIdSequence(vec![U64Segment::Range(40..60)]);
1328        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[54]));
1329        let ranges = sequence.mask_to_offset_ranges(&mask);
1330        assert_eq!(ranges, vec![14..15]);
1331
1332        let sequence = RowIdSequence(vec![U64Segment::Range(40..60)]);
1333        let mask = RowAddrMask::from_block(RowAddrTreeMap::from_iter(&[54]));
1334        let ranges = sequence.mask_to_offset_ranges(&mask);
1335        assert_eq!(ranges, vec![0..14, 15..20]);
1336
1337        // Test with a range segment with holes
1338        // 0, 1, 3, 4, 5, 7, 8, 9
1339        let sequence = RowIdSequence(vec![U64Segment::RangeWithHoles {
1340            range: 0..10,
1341            holes: vec![2, 6].into(),
1342        }]);
1343        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[0, 2, 4, 6, 8]));
1344        let ranges = sequence.mask_to_offset_ranges(&mask);
1345        assert_eq!(ranges, vec![0..1, 3..4, 6..7]);
1346
1347        let sequence = RowIdSequence(vec![U64Segment::RangeWithHoles {
1348            range: 40..60,
1349            holes: vec![47, 43].into(),
1350        }]);
1351        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[44]));
1352        let ranges = sequence.mask_to_offset_ranges(&mask);
1353        assert_eq!(ranges, vec![3..4]);
1354
1355        let sequence = RowIdSequence(vec![U64Segment::RangeWithHoles {
1356            range: 40..60,
1357            holes: vec![47, 43].into(),
1358        }]);
1359        let mask = RowAddrMask::from_block(RowAddrTreeMap::from_iter(&[44]));
1360        let ranges = sequence.mask_to_offset_ranges(&mask);
1361        assert_eq!(ranges, vec![0..3, 4..18]);
1362
1363        // Test with a range segment with bitmap
1364        // 0, 1, 4, 5, 6, 7
1365        let sequence = RowIdSequence(vec![U64Segment::RangeWithBitmap {
1366            range: 0..10,
1367            bitmap: [
1368                true, true, false, false, true, true, true, true, false, false,
1369            ]
1370            .as_slice()
1371            .into(),
1372        }]);
1373        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[0, 2, 4, 6, 8]));
1374        let ranges = sequence.mask_to_offset_ranges(&mask);
1375        assert_eq!(ranges, vec![0..1, 2..3, 4..5]);
1376
1377        let sequence = RowIdSequence(vec![U64Segment::RangeWithBitmap {
1378            range: 40..45,
1379            bitmap: [true, true, false, false, true].as_slice().into(),
1380        }]);
1381        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[44]));
1382        let ranges = sequence.mask_to_offset_ranges(&mask);
1383        assert_eq!(ranges, vec![2..3]);
1384
1385        let sequence = RowIdSequence(vec![U64Segment::RangeWithBitmap {
1386            range: 40..45,
1387            bitmap: [true, true, false, false, true].as_slice().into(),
1388        }]);
1389        let mask = RowAddrMask::from_block(RowAddrTreeMap::from_iter(&[44]));
1390        let ranges = sequence.mask_to_offset_ranges(&mask);
1391        assert_eq!(ranges, vec![0..2]);
1392
1393        // Test with a sorted array segment
1394        let sequence = RowIdSequence(vec![U64Segment::SortedArray(vec![0, 2, 4, 6, 8].into())]);
1395        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[0, 6, 8]));
1396        let ranges = sequence.mask_to_offset_ranges(&mask);
1397        assert_eq!(ranges, vec![0..1, 3..5]);
1398
1399        let sequence = RowIdSequence(vec![U64Segment::Array(vec![8, 2, 6, 0, 4].into())]);
1400        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[0, 6, 8]));
1401        let ranges = sequence.mask_to_offset_ranges(&mask);
1402        assert_eq!(ranges, vec![0..1, 2..4]);
1403
1404        // Test with multiple segments
1405        // 0, 1, 2, 3, 4, 100, 101, 102, 104, 44, 46, 78
1406        // *, -, *, -, -, ***, ---, ---, ***, --, **, --
1407        // 0, 1, 2, 3, 4,   5,   6,   7,   8,  9, 10, 11
1408        let sequence = RowIdSequence(vec![
1409            U64Segment::Range(0..5),
1410            U64Segment::RangeWithHoles {
1411                range: 100..105,
1412                holes: vec![103].into(),
1413            },
1414            U64Segment::SortedArray(vec![44, 46, 78].into()),
1415        ]);
1416        let mask = RowAddrMask::from_allowed(RowAddrTreeMap::from_iter(&[0, 2, 46, 100, 104]));
1417        let ranges = sequence.mask_to_offset_ranges(&mask);
1418        assert_eq!(ranges, vec![0..1, 2..3, 5..6, 8..9, 10..11]);
1419
1420        // Test with empty mask (should select everything)
1421        let sequence = RowIdSequence(vec![U64Segment::Range(0..10)]);
1422        let mask = RowAddrMask::default();
1423        let ranges = sequence.mask_to_offset_ranges(&mask);
1424        assert_eq!(ranges, vec![0..10]);
1425
1426        // Test with allow nothing mask
1427        let sequence = RowIdSequence(vec![U64Segment::Range(0..10)]);
1428        let mask = RowAddrMask::allow_nothing();
1429        let ranges = sequence.mask_to_offset_ranges(&mask);
1430        assert_eq!(ranges, vec![]);
1431    }
1432
1433    #[test]
1434    fn test_row_id_sequence_rechunk_with_empty_segments() {
1435        // equal case (segment exactly fills remaining space)
1436        let input_sequences = vec![
1437            RowIdSequence::from(0..2),   // [0, 1] - 2 elements
1438            RowIdSequence::from(20..23), // [20, 21, 22] - 3 elements
1439        ];
1440        let chunk_sizes = vec![2, 3]; // First chunk wants 2, second wants 3
1441
1442        let result = rechunk_sequences(input_sequences, chunk_sizes, false).unwrap();
1443        assert_eq!(result.len(), 2);
1444        assert_eq!(result[0].len(), 2);
1445        assert_eq!(result[1].len(), 3);
1446
1447        let first_chunk: Vec<u64> = result[0].iter().collect();
1448        let second_chunk: Vec<u64> = result[1].iter().collect();
1449        assert_eq!(first_chunk, vec![0, 1]);
1450        assert_eq!(second_chunk, vec![20, 21, 22]);
1451
1452        // less case (segment smaller than remaining space)
1453        let input_sequences = vec![
1454            RowIdSequence::from(0..2),   // [0, 1] - 2 elements (less than remaining)
1455            RowIdSequence::from(20..21), // [20] - 1 element (less than remaining)
1456            RowIdSequence::from(30..32), // [30, 31] - 2 elements (exactly fills remaining)
1457        ];
1458        let chunk_sizes = vec![5]; // Request 5 elements, have exactly 5
1459
1460        let result = rechunk_sequences(input_sequences, chunk_sizes, false).unwrap();
1461        assert_eq!(result.len(), 1);
1462        assert_eq!(result[0].len(), 5);
1463
1464        let elements: Vec<u64> = result[0].iter().collect();
1465        assert_eq!(elements, vec![0, 1, 20, 30, 31]);
1466
1467        // empty segment in the middle
1468        let input_sequences = vec![
1469            RowIdSequence::from(0..2),   // [0, 1] - 2 elements
1470            RowIdSequence::from(10..10), // [] - 0 elements (empty)
1471            RowIdSequence::from(20..22), // [20, 21] - 2 elements
1472        ];
1473        let chunk_sizes = vec![3, 1];
1474        let result = rechunk_sequences(input_sequences, chunk_sizes, false).unwrap();
1475
1476        assert_eq!(result.len(), 2);
1477        assert_eq!(result[0].len(), 3);
1478        assert_eq!(result[1].len(), 1);
1479
1480        let first_chunk_elements: Vec<u64> = result[0].iter().collect();
1481        let second_chunk_elements: Vec<u64> = result[1].iter().collect();
1482        assert_eq!(first_chunk_elements, vec![0, 1, 20]);
1483        assert_eq!(second_chunk_elements, vec![21]);
1484
1485        // multiple empty segments
1486        let input_sequences = vec![
1487            RowIdSequence::from(0..1),   // [0] - 1 element
1488            RowIdSequence::from(10..10), // [] - 0 elements (empty)
1489            RowIdSequence::from(20..20), // [] - 0 elements (empty)
1490            RowIdSequence::from(30..32), // [30, 31] - 2 elements
1491        ];
1492        let chunk_sizes = vec![3];
1493        let result = rechunk_sequences(input_sequences, chunk_sizes, false).unwrap();
1494
1495        assert_eq!(result.len(), 1);
1496        assert_eq!(result[0].len(), 3);
1497
1498        let elements: Vec<u64> = result[0].iter().collect();
1499        assert_eq!(elements, vec![0, 30, 31]);
1500
1501        // empty segment at chunk boundary
1502        let input_sequences = vec![
1503            RowIdSequence::from(0..3), // [0, 1, 2] - 3 elements (exactly fills first chunk)
1504            RowIdSequence::from(10..10), // [] - 0 elements (empty, at boundary)
1505            RowIdSequence::from(20..22), // [20, 21] - 2 elements (for second chunk)
1506        ];
1507        let chunk_sizes = vec![3, 2];
1508        let result = rechunk_sequences(input_sequences, chunk_sizes, false).unwrap();
1509
1510        assert_eq!(result.len(), 2);
1511        assert_eq!(result[0].len(), 3);
1512        assert_eq!(result[1].len(), 2);
1513
1514        let first_chunk_elements: Vec<u64> = result[0].iter().collect();
1515        let second_chunk_elements: Vec<u64> = result[1].iter().collect();
1516        assert_eq!(first_chunk_elements, vec![0, 1, 2]);
1517        assert_eq!(second_chunk_elements, vec![20, 21]);
1518
1519        // empty segments with allow_incomplete = true
1520        let input_sequences = vec![
1521            RowIdSequence::from(0..2),   // [0, 1] - 2 elements
1522            RowIdSequence::from(10..10), // [] - 0 elements (empty)
1523        ];
1524        let chunk_sizes = vec![5]; // Request more than available
1525        let result = rechunk_sequences(input_sequences, chunk_sizes, true).unwrap();
1526
1527        assert_eq!(result.len(), 1);
1528        assert_eq!(result[0].len(), 2);
1529
1530        let elements: Vec<u64> = result[0].iter().collect();
1531        assert_eq!(elements, vec![0, 1]);
1532    }
1533
1534    #[test]
1535    fn test_row_id_range_empty() {
1536        let seq = RowIdSequence::from(0u64..0);
1537        assert_eq!(seq.row_id_range(), None);
1538    }
1539
1540    #[test]
1541    fn test_row_id_range_single_contiguous() {
1542        let seq = RowIdSequence::from(10u64..20);
1543        assert_eq!(seq.row_id_range(), Some(10..=19));
1544    }
1545
1546    #[test]
1547    fn test_row_id_range_unsorted_array() {
1548        // Array variant: range() returns min..=max as bounding box
1549        let seq = RowIdSequence::from([50u64, 10, 30].as_slice());
1550        let r = seq.row_id_range().unwrap();
1551        assert!(*r.start() <= 10);
1552        assert!(*r.end() >= 50);
1553    }
1554
1555    #[test]
1556    fn test_row_id_range_multi_segment() {
1557        // Two disjoint ranges; bounding box should span both
1558        let mut seq = RowIdSequence::from(0u64..5);
1559        seq.extend(RowIdSequence::from(100u64..105));
1560        let r = seq.row_id_range().unwrap();
1561        assert_eq!(*r.start(), 0);
1562        assert_eq!(*r.end(), 104);
1563    }
1564}