Skip to main content

lance_select/
mask.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4use std::collections::{HashMap, HashSet};
5use std::io::Write;
6use std::ops::{Range, RangeBounds, RangeInclusive};
7use std::{collections::BTreeMap, io::Read};
8
9use arrow_array::{Array, BinaryArray, GenericBinaryArray};
10use arrow_buffer::{Buffer, NullBuffer, OffsetBuffer};
11use byteorder::{ReadBytesExt, WriteBytesExt};
12use itertools::Itertools;
13use lance_core::deepsize::DeepSizeOf;
14use roaring::{MultiOps, RoaringBitmap, RoaringTreemap};
15
16use lance_core::cache::{CacheCodecImpl, CacheEntryReader, CacheEntryWriter};
17use lance_core::utils::address::RowAddress;
18use lance_core::{Error, Result};
19
20mod nullable;
21
22pub use nullable::{NullableRowAddrMask, NullableRowAddrSet};
23
24/// A mask that selects or deselects rows based on an allow-list or block-list.
25#[derive(Clone, Debug, DeepSizeOf, PartialEq)]
26pub enum RowAddrMask {
27    AllowList(RowAddrTreeMap),
28    BlockList(RowAddrTreeMap),
29}
30
31impl Default for RowAddrMask {
32    fn default() -> Self {
33        // Empty block list means all rows are allowed
34        Self::BlockList(RowAddrTreeMap::new())
35    }
36}
37
38impl RowAddrMask {
39    // Create a mask allowing all rows, this is an alias for [default]
40    pub fn all_rows() -> Self {
41        Self::default()
42    }
43
44    // Create a mask that doesn't allow anything
45    pub fn allow_nothing() -> Self {
46        Self::AllowList(RowAddrTreeMap::new())
47    }
48
49    // Create a mask from an allow list
50    pub fn from_allowed(allow_list: RowAddrTreeMap) -> Self {
51        Self::AllowList(allow_list)
52    }
53
54    // Create a mask from a block list
55    pub fn from_block(block_list: RowAddrTreeMap) -> Self {
56        Self::BlockList(block_list)
57    }
58
59    pub fn block_list(&self) -> Option<&RowAddrTreeMap> {
60        match self {
61            Self::BlockList(block_list) => Some(block_list),
62            _ => None,
63        }
64    }
65
66    pub fn allow_list(&self) -> Option<&RowAddrTreeMap> {
67        match self {
68            Self::AllowList(allow_list) => Some(allow_list),
69            _ => None,
70        }
71    }
72
73    /// True if the row_id is selected by the mask, false otherwise
74    pub fn selected(&self, row_id: u64) -> bool {
75        match self {
76            Self::AllowList(allow_list) => allow_list.contains(row_id),
77            Self::BlockList(block_list) => !block_list.contains(row_id),
78        }
79    }
80
81    /// True if every row_id is selected. Lets callers (e.g. the FTS wand
82    /// loop) skip per-row mask checks entirely, which in turn lets the
83    /// deferred-row_id scoring path skip loading the row_id column.
84    pub fn is_select_all(&self) -> bool {
85        matches!(self, Self::BlockList(b) if b.is_empty())
86    }
87
88    /// Returns whether this mask selects every row in `rows`.
89    pub fn selects_all(&self, rows: &RowAddrTreeMap) -> bool {
90        match self {
91            Self::AllowList(allow_list) => (rows.clone() - allow_list).is_empty(),
92            Self::BlockList(block_list) => (rows.clone() & block_list).is_empty(),
93        }
94    }
95
96    /// Return the indices of the input row ids that were valid
97    pub fn selected_indices<'a>(&self, row_ids: impl Iterator<Item = &'a u64> + 'a) -> Vec<u64> {
98        row_ids
99            .enumerate()
100            .filter_map(|(idx, row_id)| {
101                if self.selected(*row_id) {
102                    Some(idx as u64)
103                } else {
104                    None
105                }
106            })
107            .collect()
108    }
109
110    /// Also block the given addrs
111    pub fn also_block(self, block_list: RowAddrTreeMap) -> Self {
112        match self {
113            Self::AllowList(allow_list) => Self::AllowList(allow_list - block_list),
114            Self::BlockList(existing) => Self::BlockList(existing | block_list),
115        }
116    }
117
118    /// Build a mask from serialized [`RowAddrTreeMap`] payloads.
119    ///
120    /// `allow` selects rows, `block` excludes them; each is the output of
121    /// [`RowAddrTreeMap::serialize_into`]. Returns `None` when neither is given,
122    /// which callers read as "no mask" rather than "select nothing".
123    ///
124    /// Bytes rather than treemaps on purpose: a caller living in a different
125    /// dynamically-linked extension module has its own copy of these Rust types
126    /// and cannot hand one over, but both sides agree on this encoding.
127    pub fn from_serialized_parts(
128        allow: Option<&[u8]>,
129        block: Option<&[u8]>,
130    ) -> Result<Option<Self>> {
131        // Name the offending side: the underlying failure is a bare "failed to
132        // fill whole buffer", which tells a caller holding two blobs nothing.
133        fn decode(bytes: &[u8], which: &str) -> Result<RowAddrTreeMap> {
134            RowAddrTreeMap::deserialize_from(bytes).map_err(|e| {
135                Error::invalid_input(format!(
136                    "row address {which} is not a serialized RowAddrTreeMap: {e}"
137                ))
138            })
139        }
140        let allow = allow.map(|b| decode(b, "allowlist")).transpose()?;
141        let block = block.map(|b| decode(b, "blocklist")).transpose()?;
142        Ok(match (allow, block) {
143            (Some(allow), Some(block)) => Some(Self::from_allowed(allow).also_block(block)),
144            (Some(allow), None) => Some(Self::from_allowed(allow)),
145            (None, Some(block)) => Some(Self::from_block(block)),
146            (None, None) => None,
147        })
148    }
149
150    /// Intersect two masks: a row survives only if both select it.
151    ///
152    /// Lets a planner apply a caller-supplied mask at one boundary rather than
153    /// at every branch that produces rows, which is how branches get missed.
154    pub fn intersect(self, other: Self) -> Self {
155        match (self, other) {
156            (Self::AllowList(a), Self::AllowList(b)) => Self::AllowList(a & b),
157            (Self::AllowList(a), Self::BlockList(b)) => Self::AllowList(a).also_block(b),
158            (Self::BlockList(a), Self::AllowList(b)) => Self::AllowList(b).also_block(a),
159            (Self::BlockList(a), Self::BlockList(b)) => Self::BlockList(a | b),
160        }
161    }
162
163    /// Also allow the given addrs
164    pub fn also_allow(self, allow_list: RowAddrTreeMap) -> Self {
165        match self {
166            Self::AllowList(existing) => Self::AllowList(existing | allow_list),
167            Self::BlockList(block_list) => Self::BlockList(block_list - allow_list),
168        }
169    }
170
171    /// Convert a mask into an arrow array
172    ///
173    /// A row addr mask is not very arrow-compatible.  We can't make it a batch with
174    /// two columns because the block list and allow list will have different lengths.  Also,
175    /// there is no Arrow type for compressed bitmaps.
176    ///
177    /// However, we need to shove it into some kind of Arrow container to pass it along the
178    /// datafusion stream.  Perhaps, in the future, we can add row addr masks as first class
179    /// types in datafusion, and this can be passed along as a mask / selection vector.
180    ///
181    /// We serialize this as a variable length binary array with two items.  The first item
182    /// is the block list and the second item is the allow list.
183    pub fn into_arrow(&self) -> Result<BinaryArray> {
184        // NOTE: This serialization format must be stable as it is used in IPC.
185        let (block_list, allow_list) = match self {
186            Self::AllowList(allow_list) => (None, Some(allow_list)),
187            Self::BlockList(block_list) => (Some(block_list), None),
188        };
189
190        let block_list_length = block_list
191            .as_ref()
192            .map(|bl| bl.serialized_size())
193            .unwrap_or(0);
194        let allow_list_length = allow_list
195            .as_ref()
196            .map(|al| al.serialized_size())
197            .unwrap_or(0);
198        let lengths = vec![block_list_length, allow_list_length];
199        let offsets = OffsetBuffer::from_lengths(lengths);
200        let mut value_bytes = vec![0; block_list_length + allow_list_length];
201        let mut validity = vec![false, false];
202        if let Some(block_list) = &block_list {
203            validity[0] = true;
204            block_list.serialize_into(&mut value_bytes[0..])?;
205        }
206        if let Some(allow_list) = &allow_list {
207            validity[1] = true;
208            allow_list.serialize_into(&mut value_bytes[block_list_length..])?;
209        }
210        let values = Buffer::from(value_bytes);
211        let nulls = NullBuffer::from(validity);
212        Ok(BinaryArray::try_new(offsets, values, Some(nulls))?)
213    }
214
215    /// Deserialize a row address mask from Arrow
216    pub fn from_arrow(array: &GenericBinaryArray<i32>) -> Result<Self> {
217        let block_list = if array.is_null(0) {
218            None
219        } else {
220            Some(RowAddrTreeMap::deserialize_from(array.value(0)))
221        }
222        .transpose()?;
223
224        let allow_list = if array.is_null(1) {
225            None
226        } else {
227            Some(RowAddrTreeMap::deserialize_from(array.value(1)))
228        }
229        .transpose()?;
230
231        let res = match (block_list, allow_list) {
232            (Some(bl), None) => Self::BlockList(bl),
233            (None, Some(al)) => Self::AllowList(al),
234            (Some(block), Some(allow)) => Self::AllowList(allow).also_block(block),
235            (None, None) => Self::all_rows(),
236        };
237        Ok(res)
238    }
239
240    /// Return the maximum number of row addresses that could be selected by this mask
241    ///
242    /// Will be None if this is a BlockList (unbounded)
243    pub fn max_len(&self) -> Option<u64> {
244        match self {
245            Self::AllowList(selection) => selection.len(),
246            Self::BlockList(_) => None,
247        }
248    }
249
250    /// Iterate over the row addresses that are selected by the mask
251    ///
252    /// This is only possible if this is an AllowList and the maps don't contain
253    /// any "full fragment" blocks.
254    pub fn iter_addrs(&self) -> Option<Box<dyn Iterator<Item = RowAddress> + '_>> {
255        match self {
256            Self::AllowList(allow_list) => {
257                if let Some(allow_iter) = allow_list.row_addrs() {
258                    Some(Box::new(allow_iter))
259                } else {
260                    None
261                }
262            }
263            Self::BlockList(_) => None, // Can't iterate over block list
264        }
265    }
266}
267
268impl std::ops::Not for RowAddrMask {
269    type Output = Self;
270
271    fn not(self) -> Self::Output {
272        match self {
273            Self::AllowList(allow_list) => Self::BlockList(allow_list),
274            Self::BlockList(block_list) => Self::AllowList(block_list),
275        }
276    }
277}
278
279impl std::ops::BitAnd for RowAddrMask {
280    type Output = Self;
281
282    fn bitand(self, rhs: Self) -> Self::Output {
283        match (self, rhs) {
284            (Self::AllowList(a), Self::AllowList(b)) => Self::AllowList(a & b),
285            (Self::AllowList(allow), Self::BlockList(block))
286            | (Self::BlockList(block), Self::AllowList(allow)) => Self::AllowList(allow - block),
287            (Self::BlockList(a), Self::BlockList(b)) => Self::BlockList(a | b),
288        }
289    }
290}
291
292impl std::ops::BitOr for RowAddrMask {
293    type Output = Self;
294
295    fn bitor(self, rhs: Self) -> Self::Output {
296        match (self, rhs) {
297            (Self::AllowList(a), Self::AllowList(b)) => Self::AllowList(a | b),
298            (Self::AllowList(allow), Self::BlockList(block))
299            | (Self::BlockList(block), Self::AllowList(allow)) => Self::BlockList(block - allow),
300            (Self::BlockList(a), Self::BlockList(b)) => Self::BlockList(a & b),
301        }
302    }
303}
304
305/// Common operations over a set of rows (either row ids or row addresses).
306///
307/// The concrete representation can be address-based (`RowAddrTreeMap`) or
308/// id-based (for example a future `RowIdSet`), but the semantics are the same:
309/// a set of unique rows.
310pub trait RowSetOps: Clone + Sized {
311    /// Logical row handle (`u64` for both row ids and row addresses).
312    type Row;
313
314    /// Returns true if the set is empty.
315    fn is_empty(&self) -> bool;
316
317    /// Returns the number of rows in the set, if it is known.
318    ///
319    /// Implementations that cannot always compute an exact size (for example
320    /// because of "full fragment" markers) should return `None`.
321    fn len(&self) -> Option<u64>;
322
323    /// Remove a value from the row set.
324    fn remove(&mut self, row: Self::Row) -> bool;
325
326    /// Returns whether this set contains the given row.
327    fn contains(&self, row: Self::Row) -> bool;
328
329    /// Returns the union of `other` and init self.
330    fn union_all(other: &[&Self]) -> Self;
331
332    /// Builds a row set from an iterator of rows.
333    fn from_sorted_iter<I>(iter: I) -> Result<Self>
334    where
335        I: IntoIterator<Item = Self::Row>;
336}
337
338/// A collection of row addresses.
339///
340/// Note: For stable row id mode, this may be split into a separate structure in the future.
341///
342/// These row ids may either be stable-style (where they can be an incrementing
343/// u64 sequence) or address style, where they are a fragment id and a row offset.
344/// When address style, this supports setting entire fragments as selected,
345/// without needing to enumerate all the ids in the fragment.
346///
347/// This is similar to a [RoaringTreemap] but it is optimized for the case where
348/// entire fragments are selected or deselected.
349#[derive(Clone, Debug, Default, PartialEq, DeepSizeOf)]
350pub struct RowAddrTreeMap {
351    /// The contents of the set. If there is a pair (k, Full) then the entire
352    /// fragment k is selected. If there is a pair (k, Partial(v)) then the
353    /// fragment k has the selected rows in v.
354    inner: BTreeMap<u32, RowAddrSelection>,
355}
356
357#[derive(Clone, Debug, PartialEq)]
358pub enum RowAddrSelection {
359    Full,
360    Partial(RoaringBitmap),
361}
362
363impl DeepSizeOf for RowAddrSelection {
364    fn deep_size_of_children(&self, _context: &mut lance_core::deepsize::Context) -> usize {
365        match self {
366            Self::Full => 0,
367            Self::Partial(bitmap) => bitmap.serialized_size(),
368        }
369    }
370}
371
372impl RowAddrSelection {
373    fn union_all(selections: &[&Self]) -> Self {
374        let mut is_full = false;
375
376        let res = Self::Partial(
377            selections
378                .iter()
379                .filter_map(|selection| match selection {
380                    Self::Full => {
381                        is_full = true;
382                        None
383                    }
384                    Self::Partial(bitmap) => Some(bitmap),
385                })
386                .union(),
387        );
388
389        if is_full { Self::Full } else { res }
390    }
391}
392
393impl RowSetOps for RowAddrTreeMap {
394    type Row = u64;
395
396    fn is_empty(&self) -> bool {
397        self.inner.is_empty()
398    }
399
400    fn len(&self) -> Option<u64> {
401        self.inner
402            .values()
403            .map(|row_addr_selection| match row_addr_selection {
404                RowAddrSelection::Full => None,
405                RowAddrSelection::Partial(indices) => Some(indices.len()),
406            })
407            .try_fold(0_u64, |acc, next| next.map(|next| next + acc))
408    }
409
410    fn remove(&mut self, row: Self::Row) -> bool {
411        let upper = (row >> 32) as u32;
412        let lower = row as u32;
413        match self.inner.get_mut(&upper) {
414            None => false,
415            Some(RowAddrSelection::Full) => {
416                let mut set = RoaringBitmap::full();
417                set.remove(lower);
418                self.inner.insert(upper, RowAddrSelection::Partial(set));
419                true
420            }
421            Some(RowAddrSelection::Partial(lower_set)) => {
422                let removed = lower_set.remove(lower);
423                if lower_set.is_empty() {
424                    self.inner.remove(&upper);
425                }
426                removed
427            }
428        }
429    }
430
431    fn contains(&self, row: Self::Row) -> bool {
432        let upper = (row >> 32) as u32;
433        let lower = row as u32;
434        match self.inner.get(&upper) {
435            None => false,
436            Some(RowAddrSelection::Full) => true,
437            Some(RowAddrSelection::Partial(fragment_set)) => fragment_set.contains(lower),
438        }
439    }
440
441    fn union_all(other: &[&Self]) -> Self {
442        let mut new_map = BTreeMap::new();
443
444        for map in other {
445            for (fragment, selection) in &map.inner {
446                new_map
447                    .entry(fragment)
448                    // I hate this allocation, but I can't think of a better way
449                    .or_insert_with(|| Vec::with_capacity(other.len()))
450                    .push(selection);
451            }
452        }
453
454        let new_map = new_map
455            .into_iter()
456            .map(|(&fragment, selections)| (fragment, RowAddrSelection::union_all(&selections)))
457            .collect();
458
459        Self { inner: new_map }
460    }
461
462    #[track_caller]
463    fn from_sorted_iter<I>(iter: I) -> Result<Self>
464    where
465        I: IntoIterator<Item = Self::Row>,
466    {
467        let mut iter = iter.into_iter().peekable();
468        let mut inner = BTreeMap::new();
469
470        while let Some(row_id) = iter.peek() {
471            let fragment_id = (row_id >> 32) as u32;
472            let next_bitmap_iter = iter
473                .peeking_take_while(|row_id| (row_id >> 32) as u32 == fragment_id)
474                .map(|row_id| row_id as u32);
475            let Ok(bitmap) = RoaringBitmap::from_sorted_iter(next_bitmap_iter) else {
476                return Err(Error::internal(
477                    "RowAddrTreeMap::from_sorted_iter called with non-sorted input",
478                ));
479            };
480            inner.insert(fragment_id, RowAddrSelection::Partial(bitmap));
481        }
482
483        Ok(Self { inner })
484    }
485}
486
487impl RowAddrTreeMap {
488    /// Create an empty set
489    pub fn new() -> Self {
490        Self::default()
491    }
492
493    /// An iterator of row addrs
494    ///
495    /// If there are any "full fragment" items then this can't be calculated and None
496    /// is returned
497    pub fn row_addrs(&self) -> Option<impl Iterator<Item = RowAddress> + '_> {
498        let inner_iters = self
499            .inner
500            .iter()
501            .filter_map(|(frag_id, row_addr_selection)| match row_addr_selection {
502                RowAddrSelection::Full => None,
503                RowAddrSelection::Partial(bitmap) => Some(
504                    bitmap
505                        .iter()
506                        .map(|row_offset| RowAddress::new_from_parts(*frag_id, row_offset)),
507                ),
508            })
509            .collect::<Vec<_>>();
510        if inner_iters.len() != self.inner.len() {
511            None
512        } else {
513            Some(inner_iters.into_iter().flatten())
514        }
515    }
516
517    /// Insert a single value into the set
518    ///
519    /// Returns true if the value was not already in the set.
520    ///
521    /// ```rust
522    /// use lance_select::{RowAddrTreeMap, RowSetOps};
523    ///
524    /// let mut set = RowAddrTreeMap::new();
525    /// assert_eq!(set.insert(10), true);
526    /// assert_eq!(set.insert(10), false);
527    /// assert_eq!(set.contains(10), true);
528    /// ```
529    pub fn insert(&mut self, value: u64) -> bool {
530        let fragment = (value >> 32) as u32;
531        let row_addr = value as u32;
532        match self.inner.get_mut(&fragment) {
533            None => {
534                let mut set = RoaringBitmap::new();
535                set.insert(row_addr);
536                self.inner.insert(fragment, RowAddrSelection::Partial(set));
537                true
538            }
539            Some(RowAddrSelection::Full) => false,
540            Some(RowAddrSelection::Partial(set)) => set.insert(row_addr),
541        }
542    }
543
544    /// Insert a range of values into the set
545    pub fn insert_range<R: RangeBounds<u64>>(&mut self, range: R) -> u64 {
546        // Separate the start and end into high and low bits.
547        let (mut start_high, mut start_low) = match range.start_bound() {
548            std::ops::Bound::Included(&start) => ((start >> 32) as u32, start as u32),
549            std::ops::Bound::Excluded(&start) => {
550                let start = start.saturating_add(1);
551                ((start >> 32) as u32, start as u32)
552            }
553            std::ops::Bound::Unbounded => (0, 0),
554        };
555
556        let (end_high, end_low) = match range.end_bound() {
557            std::ops::Bound::Included(&end) => ((end >> 32) as u32, end as u32),
558            std::ops::Bound::Excluded(&end) => {
559                let end = end.saturating_sub(1);
560                ((end >> 32) as u32, end as u32)
561            }
562            std::ops::Bound::Unbounded => (u32::MAX, u32::MAX),
563        };
564
565        let mut count = 0;
566
567        while start_high <= end_high {
568            let start = start_low;
569            let end = if start_high == end_high {
570                end_low
571            } else {
572                u32::MAX
573            };
574            let fragment = start_high;
575            match self.inner.get_mut(&fragment) {
576                None => {
577                    let mut set = RoaringBitmap::new();
578                    count += set.insert_range(start..=end);
579                    self.inner.insert(fragment, RowAddrSelection::Partial(set));
580                }
581                Some(RowAddrSelection::Full) => {}
582                Some(RowAddrSelection::Partial(set)) => {
583                    count += set.insert_range(start..=end);
584                }
585            }
586            start_high += 1;
587            start_low = 0;
588        }
589
590        count
591    }
592
593    /// Build a set from many independently sorted runs of row addresses.
594    ///
595    /// Each run must be sorted ascending; the runs may interleave arbitrarily
596    /// and may repeat addresses (the result is a set). This is the cheap way
597    /// to assemble the output of a scan that produced one sorted chunk per
598    /// page or partition: building one map per run and unioning them costs
599    /// O(runs x fragments) tiny bitmaps, whereas here every address is
600    /// bucketed by fragment in a single pass, a bucket is sorted only if the
601    /// runs that fed it actually interleaved, and exactly one bitmap is built
602    /// per fragment.
603    ///
604    /// Cost is O(N) plus O(n log n) for each bucket whose runs interleaved,
605    /// where N is the total number of addresses and n the bucket size.
606    pub fn from_sorted_runs<'a, I>(runs: I) -> Self
607    where
608        I: IntoIterator<Item = &'a [u64]>,
609    {
610        // fragment -> (low 32 bits of every address seen, still sorted?)
611        let mut buckets: HashMap<u32, (Vec<u32>, bool)> = HashMap::new();
612        for run in runs {
613            let mut rest = run;
614            while let Some(&first) = rest.first() {
615                let fragment = (first >> 32) as u32;
616                // A sorted run keeps each fragment's addresses contiguous, so
617                // the segment for `fragment` is a prefix of `rest`.
618                let end = rest
619                    .iter()
620                    .position(|addr| (addr >> 32) as u32 != fragment)
621                    .unwrap_or(rest.len());
622                let (segment, tail) = rest.split_at(end);
623                let (offsets, sorted) = buckets
624                    .entry(fragment)
625                    .or_insert_with(|| (Vec::new(), true));
626                if *sorted && offsets.last().is_some_and(|&last| last > first as u32) {
627                    *sorted = false;
628                }
629                offsets.extend(segment.iter().map(|addr| *addr as u32));
630                rest = tail;
631            }
632        }
633
634        let inner = buckets
635            .into_iter()
636            .map(|(fragment, (mut offsets, sorted))| {
637                if !sorted {
638                    offsets.sort_unstable();
639                }
640                offsets.dedup();
641                let bitmap = RoaringBitmap::from_sorted_iter(offsets)
642                    .expect("offsets were sorted and deduplicated");
643                (fragment, RowAddrSelection::Partial(bitmap))
644            })
645            .collect();
646        Self { inner }
647    }
648
649    /// Add a bitmap for a single fragment
650    pub fn insert_bitmap(&mut self, fragment: u32, bitmap: RoaringBitmap) {
651        self.inner
652            .insert(fragment, RowAddrSelection::Partial(bitmap));
653    }
654
655    /// Add a whole fragment to the set
656    pub fn insert_fragment(&mut self, fragment_id: u32) {
657        self.inner.insert(fragment_id, RowAddrSelection::Full);
658    }
659
660    pub fn get_fragment_bitmap(&self, fragment_id: u32) -> Option<&RoaringBitmap> {
661        match self.inner.get(&fragment_id) {
662            None => None,
663            Some(RowAddrSelection::Full) => None,
664            Some(RowAddrSelection::Partial(set)) => Some(set),
665        }
666    }
667
668    /// Get the selection for a fragment
669    pub fn get(&self, fragment_id: &u32) -> Option<&RowAddrSelection> {
670        self.inner.get(fragment_id)
671    }
672
673    /// Iterate over (fragment_id, selection) pairs
674    pub fn iter(&self) -> impl Iterator<Item = (&u32, &RowAddrSelection)> {
675        self.inner.iter()
676    }
677
678    pub fn retain_fragments(&mut self, frag_ids: impl IntoIterator<Item = u32>) {
679        let frag_id_set = frag_ids.into_iter().collect::<HashSet<_>>();
680        self.inner
681            .retain(|frag_id, _| frag_id_set.contains(frag_id));
682    }
683
684    /// Compute the serialized size of the set.
685    pub fn serialized_size(&self) -> usize {
686        // Starts at 4 because of the u32 num_entries
687        let mut size = 4;
688        for set in self.inner.values() {
689            // Each entry is 8 bytes for the fragment id and the bitmap size
690            size += 8;
691            if let RowAddrSelection::Partial(set) = set {
692                size += set.serialized_size();
693            }
694        }
695        size
696    }
697
698    /// Serialize the set into the given buffer
699    ///
700    /// The serialization format is stable and used for index serialization
701    ///
702    /// The serialization format is:
703    /// * u32: num_entries
704    ///
705    /// for each entry:
706    ///   * u32: fragment_id
707    ///   * u32: bitmap size
708    ///   * \[u8\]: bitmap
709    ///
710    /// If bitmap size is zero then the entire fragment is selected.
711    pub fn serialize_into<W: Write>(&self, mut writer: W) -> Result<()> {
712        writer.write_u32::<byteorder::LittleEndian>(self.inner.len() as u32)?;
713        for (fragment, set) in &self.inner {
714            writer.write_u32::<byteorder::LittleEndian>(*fragment)?;
715            if let RowAddrSelection::Partial(set) = set {
716                writer.write_u32::<byteorder::LittleEndian>(set.serialized_size() as u32)?;
717                set.serialize_into(&mut writer)?;
718            } else {
719                writer.write_u32::<byteorder::LittleEndian>(0)?;
720            }
721        }
722        Ok(())
723    }
724
725    /// Deserialize the set from the given buffer
726    pub fn deserialize_from<R: Read>(mut reader: R) -> Result<Self> {
727        let num_entries = reader.read_u32::<byteorder::LittleEndian>()?;
728        let mut inner = BTreeMap::new();
729        for _ in 0..num_entries {
730            let fragment = reader.read_u32::<byteorder::LittleEndian>()?;
731            let bitmap_size = reader.read_u32::<byteorder::LittleEndian>()?;
732            if bitmap_size == 0 {
733                inner.insert(fragment, RowAddrSelection::Full);
734            } else {
735                // Grow with the bytes that actually arrive instead of trusting the
736                // declared size. This is reachable from a public byte boundary, so
737                // a 12-byte payload could otherwise declare 4 GiB and abort the
738                // process on the allocation before any read fails.
739                let mut buffer = Vec::new();
740                let read = reader
741                    .by_ref()
742                    .take(u64::from(bitmap_size))
743                    .read_to_end(&mut buffer)?;
744                if read != bitmap_size as usize {
745                    return Err(Error::invalid_input(format!(
746                        "row addr treemap declares a {bitmap_size} byte bitmap for \
747                         fragment {fragment} but only {read} bytes remain"
748                    )));
749                }
750                let set = RoaringBitmap::deserialize_from(&buffer[..])?;
751                inner.insert(fragment, RowAddrSelection::Partial(set));
752            }
753        }
754        Ok(Self { inner })
755    }
756
757    /// Apply a mask to the row addrs
758    ///
759    /// For AllowList: only keep rows that are in the selection and not null
760    /// For BlockList: remove rows that are blocked (not null) and remove nulls
761    pub fn mask(&mut self, mask: &RowAddrMask) {
762        match mask {
763            RowAddrMask::AllowList(allow_list) => {
764                *self &= allow_list;
765            }
766            RowAddrMask::BlockList(block_list) => {
767                *self -= block_list;
768            }
769        }
770    }
771
772    /// Convert the set into an iterator of row addrs
773    ///
774    /// # Panics
775    ///
776    /// Panics if any selection is `Full` because the fragment size is unknown.
777    pub fn into_addr_iter(self) -> impl Iterator<Item = u64> {
778        self.inner
779            .into_iter()
780            .flat_map(|(fragment, selection)| match selection {
781                RowAddrSelection::Full => panic!("Size of full fragment is unknown"),
782                RowAddrSelection::Partial(bitmap) => bitmap.into_iter().map(move |val| {
783                    let fragment = fragment as u64;
784                    let row_offset = val as u64;
785                    (fragment << 32) | row_offset
786                }),
787            })
788    }
789
790    /// Iterate the selected row addresses as `(fragment_id, run)` pairs.
791    ///
792    /// Range-shaped counterpart to [`Self::into_addr_iter`]. A contiguous
793    /// run of N selected rows yields one item, not N. Uses roaring's
794    /// `Iter::next_range`, which walks each underlying container's runs
795    /// rather than its individual bits, so dense ranges cost
796    /// O(num_containers) (roughly num_rows / 65536) instead of O(num_rows).
797    ///
798    /// # Panics
799    ///
800    /// Panics if any selection is `Full` because the fragment size is unknown.
801    pub fn iter_runs(&self) -> impl Iterator<Item = (u32, RangeInclusive<u32>)> + '_ {
802        self.inner
803            .iter()
804            .flat_map(|(&fragment, selection)| match selection {
805                RowAddrSelection::Full => panic!("Size of full fragment is unknown"),
806                RowAddrSelection::Partial(bitmap) => {
807                    let mut iter = bitmap.iter();
808                    std::iter::from_fn(move || iter.next_range().map(|r| (fragment, r)))
809                }
810            })
811    }
812}
813
814impl CacheCodecImpl for RowAddrTreeMap {
815    const TYPE_ID: &'static str = "lance.RowAddrTreeMap";
816    const CURRENT_VERSION: u32 = 1;
817
818    fn serialize(&self, w: &mut CacheEntryWriter<'_>) -> Result<()> {
819        // A roaring bitmap has its own stable, portable serialization; it is
820        // the whole body, so write it raw rather than length-prefixed.
821        self.serialize_into(w.raw_writer())
822    }
823
824    fn deserialize(r: &mut CacheEntryReader<'_>) -> Result<Self> {
825        Self::deserialize_from(r.body().as_ref())
826    }
827}
828
829impl std::ops::BitOr<Self> for RowAddrTreeMap {
830    type Output = Self;
831
832    fn bitor(mut self, rhs: Self) -> Self::Output {
833        self |= rhs;
834        self
835    }
836}
837
838impl std::ops::BitOr<&Self> for RowAddrTreeMap {
839    type Output = Self;
840
841    fn bitor(mut self, rhs: &Self) -> Self::Output {
842        self |= rhs;
843        self
844    }
845}
846
847impl std::ops::BitOrAssign<Self> for RowAddrTreeMap {
848    fn bitor_assign(&mut self, rhs: Self) {
849        *self |= &rhs;
850    }
851}
852
853impl std::ops::BitOrAssign<&Self> for RowAddrTreeMap {
854    fn bitor_assign(&mut self, rhs: &Self) {
855        for (fragment, rhs_set) in &rhs.inner {
856            let lhs_set = self.inner.get_mut(fragment);
857            if let Some(lhs_set) = lhs_set {
858                match lhs_set {
859                    RowAddrSelection::Full => {
860                        // If the fragment is already selected then there is nothing to do
861                    }
862                    RowAddrSelection::Partial(lhs_bitmap) => match rhs_set {
863                        RowAddrSelection::Full => {
864                            *lhs_set = RowAddrSelection::Full;
865                        }
866                        RowAddrSelection::Partial(rhs_set) => {
867                            *lhs_bitmap |= rhs_set;
868                        }
869                    },
870                }
871            } else {
872                self.inner.insert(*fragment, rhs_set.clone());
873            }
874        }
875    }
876}
877
878impl std::ops::BitAnd<Self> for RowAddrTreeMap {
879    type Output = Self;
880
881    fn bitand(mut self, rhs: Self) -> Self::Output {
882        self &= &rhs;
883        self
884    }
885}
886
887impl std::ops::BitAnd<&Self> for RowAddrTreeMap {
888    type Output = Self;
889
890    fn bitand(mut self, rhs: &Self) -> Self::Output {
891        self &= rhs;
892        self
893    }
894}
895
896impl std::ops::BitAndAssign<Self> for RowAddrTreeMap {
897    fn bitand_assign(&mut self, rhs: Self) {
898        *self &= &rhs;
899    }
900}
901
902impl std::ops::BitAndAssign<&Self> for RowAddrTreeMap {
903    fn bitand_assign(&mut self, rhs: &Self) {
904        // Remove fragment that aren't on the RHS
905        self.inner
906            .retain(|fragment, _| rhs.inner.contains_key(fragment));
907
908        // For fragments that are on the RHS, intersect the bitmaps
909        for (fragment, mut lhs_set) in &mut self.inner {
910            match (&mut lhs_set, rhs.inner.get(fragment)) {
911                (_, None) => {} // Already handled by retain
912                (_, Some(RowAddrSelection::Full)) => {
913                    // Everything selected on RHS, so can leave LHS untouched.
914                }
915                (RowAddrSelection::Partial(lhs_set), Some(RowAddrSelection::Partial(rhs_set))) => {
916                    *lhs_set &= rhs_set;
917                }
918                (RowAddrSelection::Full, Some(RowAddrSelection::Partial(rhs_set))) => {
919                    *lhs_set = RowAddrSelection::Partial(rhs_set.clone());
920                }
921            }
922        }
923        // Some bitmaps might now be empty. If they are, we should remove them.
924        self.inner.retain(|_, set| match set {
925            RowAddrSelection::Partial(set) => !set.is_empty(),
926            RowAddrSelection::Full => true,
927        });
928    }
929}
930
931impl std::ops::Sub<Self> for RowAddrTreeMap {
932    type Output = Self;
933
934    fn sub(mut self, rhs: Self) -> Self {
935        self -= &rhs;
936        self
937    }
938}
939
940impl std::ops::Sub<&Self> for RowAddrTreeMap {
941    type Output = Self;
942
943    fn sub(mut self, rhs: &Self) -> Self {
944        self -= rhs;
945        self
946    }
947}
948
949impl std::ops::SubAssign<&Self> for RowAddrTreeMap {
950    fn sub_assign(&mut self, rhs: &Self) {
951        for (fragment, rhs_set) in &rhs.inner {
952            match self.inner.get_mut(fragment) {
953                None => {}
954                Some(RowAddrSelection::Full) => {
955                    // If the fragment is already selected then there is nothing to do
956                    match rhs_set {
957                        RowAddrSelection::Full => {
958                            self.inner.remove(fragment);
959                        }
960                        RowAddrSelection::Partial(rhs_set) => {
961                            // This generally won't be hit.
962                            let mut set = RoaringBitmap::full();
963                            set -= rhs_set;
964                            self.inner.insert(*fragment, RowAddrSelection::Partial(set));
965                        }
966                    }
967                }
968                Some(RowAddrSelection::Partial(lhs_set)) => match rhs_set {
969                    RowAddrSelection::Full => {
970                        self.inner.remove(fragment);
971                    }
972                    RowAddrSelection::Partial(rhs_set) => {
973                        *lhs_set -= rhs_set;
974                        if lhs_set.is_empty() {
975                            self.inner.remove(fragment);
976                        }
977                    }
978                },
979            }
980        }
981    }
982}
983
984impl FromIterator<u64> for RowAddrTreeMap {
985    fn from_iter<T: IntoIterator<Item = u64>>(iter: T) -> Self {
986        let mut inner = BTreeMap::new();
987        for row_addr in iter {
988            let upper = (row_addr >> 32) as u32;
989            let lower = row_addr as u32;
990            match inner.get_mut(&upper) {
991                None => {
992                    let mut set = RoaringBitmap::new();
993                    set.insert(lower);
994                    inner.insert(upper, RowAddrSelection::Partial(set));
995                }
996                Some(RowAddrSelection::Full) => {
997                    // If the fragment is already selected then there is nothing to do
998                }
999                Some(RowAddrSelection::Partial(set)) => {
1000                    set.insert(lower);
1001                }
1002            }
1003        }
1004        Self { inner }
1005    }
1006}
1007
1008impl<'a> FromIterator<&'a u64> for RowAddrTreeMap {
1009    fn from_iter<T: IntoIterator<Item = &'a u64>>(iter: T) -> Self {
1010        Self::from_iter(iter.into_iter().copied())
1011    }
1012}
1013
1014impl From<Range<u64>> for RowAddrTreeMap {
1015    fn from(range: Range<u64>) -> Self {
1016        let mut map = Self::default();
1017        map.insert_range(range);
1018        map
1019    }
1020}
1021
1022impl From<RangeInclusive<u64>> for RowAddrTreeMap {
1023    fn from(range: RangeInclusive<u64>) -> Self {
1024        let mut map = Self::default();
1025        map.insert_range(range);
1026        map
1027    }
1028}
1029
1030impl From<RoaringTreemap> for RowAddrTreeMap {
1031    fn from(roaring: RoaringTreemap) -> Self {
1032        let mut inner = BTreeMap::new();
1033        for (fragment, set) in roaring.bitmaps() {
1034            inner.insert(fragment, RowAddrSelection::Partial(set.clone()));
1035        }
1036        Self { inner }
1037    }
1038}
1039
1040impl Extend<u64> for RowAddrTreeMap {
1041    fn extend<T: IntoIterator<Item = u64>>(&mut self, iter: T) {
1042        for row_addr in iter {
1043            let upper = (row_addr >> 32) as u32;
1044            let lower = row_addr as u32;
1045            match self.inner.get_mut(&upper) {
1046                None => {
1047                    let mut set = RoaringBitmap::new();
1048                    set.insert(lower);
1049                    self.inner.insert(upper, RowAddrSelection::Partial(set));
1050                }
1051                Some(RowAddrSelection::Full) => {
1052                    // If the fragment is already selected then there is nothing to do
1053                }
1054                Some(RowAddrSelection::Partial(set)) => {
1055                    set.insert(lower);
1056                }
1057            }
1058        }
1059    }
1060}
1061
1062impl<'a> Extend<&'a u64> for RowAddrTreeMap {
1063    fn extend<T: IntoIterator<Item = &'a u64>>(&mut self, iter: T) {
1064        self.extend(iter.into_iter().copied())
1065    }
1066}
1067
1068// Extending with RowAddrTreeMap is basically a cumulative set union
1069impl Extend<Self> for RowAddrTreeMap {
1070    fn extend<T: IntoIterator<Item = Self>>(&mut self, iter: T) {
1071        for other in iter {
1072            for (fragment, set) in other.inner {
1073                match self.inner.get_mut(&fragment) {
1074                    None => {
1075                        self.inner.insert(fragment, set);
1076                    }
1077                    Some(RowAddrSelection::Full) => {
1078                        // If the fragment is already selected then there is nothing to do
1079                    }
1080                    Some(RowAddrSelection::Partial(lhs_set)) => match set {
1081                        RowAddrSelection::Full => {
1082                            self.inner.insert(fragment, RowAddrSelection::Full);
1083                        }
1084                        RowAddrSelection::Partial(rhs_set) => {
1085                            *lhs_set |= rhs_set;
1086                        }
1087                    },
1088                }
1089            }
1090        }
1091    }
1092}
1093
1094pub fn bitmap_to_ranges(bitmap: &RoaringBitmap) -> Vec<Range<u64>> {
1095    let mut ranges = Vec::new();
1096    let mut iter = bitmap.iter();
1097    while let Some(r) = iter.next_range() {
1098        ranges.push(*r.start() as u64..(*r.end() as u64 + 1));
1099    }
1100    ranges
1101}
1102
1103pub fn ranges_to_bitmap(ranges: &[Range<u64>], sorted: bool) -> RoaringBitmap {
1104    if ranges.is_empty() {
1105        return RoaringBitmap::new();
1106    }
1107    if sorted {
1108        let sample_size = ranges.len().min(10);
1109        let avg_len: u64 = ranges
1110            .iter()
1111            .take(sample_size)
1112            .map(|r| r.end - r.start)
1113            .sum::<u64>()
1114            / sample_size as u64;
1115        // from_sorted_iter appends each value in O(1) but must visit every u32.
1116        // insert_range bulk-fills containers but does a binary search per call.
1117        // Crossover is ~6: below that, iterating all values is cheaper.
1118        if avg_len <= 6 {
1119            return RoaringBitmap::from_sorted_iter(
1120                ranges.iter().flat_map(|r| r.start as u32..r.end as u32),
1121            )
1122            .unwrap();
1123        }
1124    }
1125    let mut bm = RoaringBitmap::new();
1126    for r in ranges {
1127        bm.insert_range(r.start as u32..r.end as u32);
1128    }
1129    bm
1130}
1131
1132/// A set of stable row ids backed by a 64-bit Roaring bitmap.
1133///
1134/// This is a thin wrapper around [`RoaringTreemap`]. It represents a
1135/// collection of unique row ids and provides the common row-set
1136/// operations defined by [`RowSetOps`].
1137#[derive(Clone, Debug, Default, PartialEq)]
1138pub struct RowIdSet {
1139    inner: RoaringTreemap,
1140}
1141
1142impl RowIdSet {
1143    /// Creates an empty set of row ids.
1144    pub fn new() -> Self {
1145        Self::default()
1146    }
1147    /// Returns an iterator over the contained row ids in ascending order.
1148    pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
1149        self.inner.iter()
1150    }
1151    /// Returns the union of `self` and `other`.
1152    pub fn union(mut self, other: &Self) -> Self {
1153        self.inner |= &other.inner;
1154        self
1155    }
1156    /// Returns the set difference `self \\ other`.
1157    pub fn difference(mut self, other: &Self) -> Self {
1158        self.inner -= &other.inner;
1159        self
1160    }
1161}
1162
1163impl RowSetOps for RowIdSet {
1164    type Row = u64;
1165    fn is_empty(&self) -> bool {
1166        self.inner.is_empty()
1167    }
1168    fn len(&self) -> Option<u64> {
1169        Some(self.inner.len())
1170    }
1171    fn remove(&mut self, row: Self::Row) -> bool {
1172        self.inner.remove(row)
1173    }
1174    fn contains(&self, row: Self::Row) -> bool {
1175        self.inner.contains(row)
1176    }
1177    fn union_all(other: &[&Self]) -> Self {
1178        let mut result = other
1179            .first()
1180            .map_or(Self::default(), |&first| first.clone());
1181        for set in other {
1182            result.inner |= &set.inner;
1183        }
1184        result
1185    }
1186    #[track_caller]
1187    fn from_sorted_iter<I>(iter: I) -> Result<Self>
1188    where
1189        I: IntoIterator<Item = Self::Row>,
1190    {
1191        let mut inner = RoaringTreemap::new();
1192        let mut last: Option<u64> = None;
1193        for value in iter {
1194            if let Some(prev) = last
1195                && value < prev
1196            {
1197                return Err(Error::internal(
1198                    "RowIdSet::from_sorted_iter called with non-sorted input",
1199                ));
1200            }
1201            inner.insert(value);
1202            last = Some(value);
1203        }
1204        Ok(Self { inner })
1205    }
1206}
1207
1208/// A mask over stable row ids based on an allow-list or block-list.
1209///
1210/// The semantics mirror [`RowAddrMask`], but operate on stable
1211/// row ids instead of physical row addresses.
1212#[derive(Clone, Debug, PartialEq)]
1213pub enum RowIdMask {
1214    /// Only the ids in the set are selected.
1215    AllowList(RowIdSet),
1216    /// All ids are selected except those in the set.
1217    BlockList(RowIdSet),
1218}
1219
1220impl Default for RowIdMask {
1221    fn default() -> Self {
1222        // Empty block list means all rows are allowed
1223        Self::BlockList(RowIdSet::default())
1224    }
1225}
1226impl RowIdMask {
1227    /// Create a mask allowing all rows, this is an alias for [`Default`].
1228    pub fn all_rows() -> Self {
1229        Self::default()
1230    }
1231    /// Create a mask that doesn't allow any row id.
1232    pub fn allow_nothing() -> Self {
1233        Self::AllowList(RowIdSet::default())
1234    }
1235    /// Create a mask from an allow list.
1236    pub fn from_allowed(allow_list: RowIdSet) -> Self {
1237        Self::AllowList(allow_list)
1238    }
1239    /// Create a mask from a block list.
1240    pub fn from_block(block_list: RowIdSet) -> Self {
1241        Self::BlockList(block_list)
1242    }
1243    /// True if the row id is selected by the mask, false otherwise.
1244    pub fn selected(&self, row_id: u64) -> bool {
1245        match self {
1246            Self::AllowList(allow_list) => allow_list.contains(row_id),
1247            Self::BlockList(block_list) => !block_list.contains(row_id),
1248        }
1249    }
1250    /// Return the indices of the input row ids that are selected by the mask.
1251    pub fn selected_indices<'a>(&self, row_ids: impl Iterator<Item = &'a u64> + 'a) -> Vec<u64> {
1252        row_ids
1253            .enumerate()
1254            .filter_map(|(idx, row_id)| {
1255                if self.selected(*row_id) {
1256                    Some(idx as u64)
1257                } else {
1258                    None
1259                }
1260            })
1261            .collect()
1262    }
1263    /// Also block the given ids.
1264    ///
1265    /// * `AllowList(a)` -> `AllowList(a \\ block_list)`
1266    /// * `BlockList(b)` -> `BlockList(b union block_list)`
1267    pub fn also_block(self, block_list: RowIdSet) -> Self {
1268        match self {
1269            Self::AllowList(allow_list) => Self::AllowList(allow_list.difference(&block_list)),
1270            Self::BlockList(existing) => Self::BlockList(existing.union(&block_list)),
1271        }
1272    }
1273    /// Also allow the given ids.
1274    ///
1275    /// * `AllowList(a)` -> `AllowList(a union allow_list)`
1276    /// * `BlockList(b)` -> `BlockList(b \\ allow_list)`
1277    pub fn also_allow(self, allow_list: RowIdSet) -> Self {
1278        match self {
1279            Self::AllowList(existing) => Self::AllowList(existing.union(&allow_list)),
1280            Self::BlockList(block_list) => Self::BlockList(block_list.difference(&allow_list)),
1281        }
1282    }
1283    /// Return the maximum number of row ids that could be selected by this mask.
1284    ///
1285    /// Will be `None` if this is a `BlockList` (unbounded).
1286    pub fn max_len(&self) -> Option<u64> {
1287        match self {
1288            Self::AllowList(selection) => selection.len(),
1289            Self::BlockList(_) => None,
1290        }
1291    }
1292    /// Iterate over the row ids that are selected by the mask.
1293    ///
1294    /// This is only possible if this is an `AllowList`. For a `BlockList`
1295    /// the domain of possible row ids is unbounded.
1296    pub fn iter_ids(&self) -> Option<Box<dyn Iterator<Item = u64> + '_>> {
1297        match self {
1298            Self::AllowList(allow_list) => Some(Box::new(allow_list.iter())),
1299            Self::BlockList(_) => None,
1300        }
1301    }
1302}
1303
1304#[cfg(test)]
1305mod tests {
1306    use super::*;
1307    use proptest::{prop_assert, prop_assert_eq};
1308
1309    fn rows(ids: &[u64]) -> RowAddrTreeMap {
1310        RowAddrTreeMap::from_iter(ids)
1311    }
1312
1313    fn assert_mask_selects(mask: &RowAddrMask, selected: &[u64], not_selected: &[u64]) {
1314        for &id in selected {
1315            assert!(mask.selected(id), "Expected row {} to be selected", id);
1316        }
1317        for &id in not_selected {
1318            assert!(!mask.selected(id), "Expected row {} to NOT be selected", id);
1319        }
1320    }
1321
1322    fn selected_in_range(mask: &RowAddrMask, range: std::ops::Range<u64>) -> Vec<u64> {
1323        range.filter(|val| mask.selected(*val)).collect()
1324    }
1325
1326    #[test]
1327    fn test_from_sorted_runs() {
1328        let addr = |frag: u64, off: u64| frag << 32 | off;
1329
1330        // No runs, and runs that are all empty, give an empty set.
1331        assert_eq!(RowAddrTreeMap::from_sorted_runs([]), RowAddrTreeMap::new());
1332        assert_eq!(
1333            RowAddrTreeMap::from_sorted_runs([&[][..], &[][..]]),
1334            RowAddrTreeMap::new()
1335        );
1336
1337        // A single run is taken as-is (no sort needed) and spans fragments.
1338        let single = [addr(0, 3), addr(0, 9), addr(2, 1), addr(7, 0)];
1339        assert_eq!(
1340            RowAddrTreeMap::from_sorted_runs([&single[..]]),
1341            rows(&single)
1342        );
1343
1344        // Interleaved runs whose fragments overlap: every bucket must be
1345        // re-sorted and the union must match the naive construction.
1346        let run_a = [addr(0, 5), addr(1, 2), addr(1, 8), addr(3, 4)];
1347        let run_b = [addr(0, 1), addr(0, 6), addr(1, 3), addr(2, 0)];
1348        let run_c = [addr(1, 0), addr(3, 4), addr(3, 5)]; // repeats addr(3, 4)
1349        let expected: Vec<u64> = run_a
1350            .iter()
1351            .chain(run_b.iter())
1352            .chain(run_c.iter())
1353            .copied()
1354            .collect();
1355        let actual = RowAddrTreeMap::from_sorted_runs([&run_a[..], &run_b[..], &run_c[..]]);
1356        assert_eq!(actual, rows(&expected));
1357        assert_eq!(actual.len(), Some(10));
1358
1359        // Runs that touch disjoint fragment ranges never need a sort, and a
1360        // later run that continues a bucket in order keeps it sorted too.
1361        let lo = [addr(0, 0), addr(0, 1)];
1362        let hi = [addr(0, 2), addr(5, 0)];
1363        assert_eq!(
1364            RowAddrTreeMap::from_sorted_runs([&lo[..], &hi[..]]),
1365            rows(&[addr(0, 0), addr(0, 1), addr(0, 2), addr(5, 0)])
1366        );
1367
1368        // Bare stable row ids (no fragment bits) all land in bucket 0.
1369        let ids_a = [1_u64, 4, 9];
1370        let ids_b = [2_u64, 4, 10];
1371        assert_eq!(
1372            RowAddrTreeMap::from_sorted_runs([&ids_a[..], &ids_b[..]]),
1373            rows(&[1, 2, 4, 9, 10])
1374        );
1375    }
1376
1377    #[test]
1378    fn test_row_addr_mask_construction() {
1379        let full_mask = RowAddrMask::all_rows();
1380        assert_eq!(full_mask.max_len(), None);
1381        assert_mask_selects(&full_mask, &[0, 1, 4 << 32 | 3], &[]);
1382        assert_eq!(full_mask.allow_list(), None);
1383        assert_eq!(full_mask.block_list(), Some(&RowAddrTreeMap::default()));
1384        assert!(full_mask.iter_addrs().is_none());
1385
1386        let empty_mask = RowAddrMask::allow_nothing();
1387        assert_eq!(empty_mask.max_len(), Some(0));
1388        assert_mask_selects(&empty_mask, &[], &[0, 1, 4 << 32 | 3]);
1389        assert_eq!(empty_mask.allow_list(), Some(&RowAddrTreeMap::default()));
1390        assert_eq!(empty_mask.block_list(), None);
1391        let iter = empty_mask.iter_addrs();
1392        assert!(iter.is_some());
1393        assert_eq!(iter.unwrap().count(), 0);
1394
1395        let allow_list = RowAddrMask::from_allowed(rows(&[10, 20, 30]));
1396        assert_eq!(allow_list.max_len(), Some(3));
1397        assert_mask_selects(&allow_list, &[10, 20, 30], &[0, 15, 25, 40]);
1398        assert_eq!(allow_list.allow_list(), Some(&rows(&[10, 20, 30])));
1399        assert_eq!(allow_list.block_list(), None);
1400        let iter = allow_list.iter_addrs();
1401        assert!(iter.is_some());
1402        let ids: Vec<u64> = iter.unwrap().map(|addr| addr.into()).collect();
1403        assert_eq!(ids, vec![10, 20, 30]);
1404
1405        let mut full_frag = RowAddrTreeMap::default();
1406        full_frag.insert_fragment(2);
1407        let allow_list = RowAddrMask::from_allowed(full_frag);
1408        assert_eq!(allow_list.max_len(), None);
1409        assert_mask_selects(&allow_list, &[(2 << 32) + 5], &[(3 << 32) + 5]);
1410        assert!(allow_list.iter_addrs().is_none());
1411    }
1412
1413    #[test]
1414    fn test_row_addr_mask_selects_all_known_rows() {
1415        let partition_rows = rows(&[10, 20, 2_u64 << 32 | 3]);
1416
1417        assert!(RowAddrMask::all_rows().selects_all(&partition_rows));
1418        assert!(
1419            RowAddrMask::from_allowed(rows(&[10, 20, 30, 2_u64 << 32 | 3]))
1420                .selects_all(&partition_rows)
1421        );
1422        assert!(
1423            !RowAddrMask::from_allowed(rows(&[10, 2_u64 << 32 | 3])).selects_all(&partition_rows)
1424        );
1425        assert!(RowAddrMask::from_block(rows(&[30])).selects_all(&partition_rows));
1426        assert!(!RowAddrMask::from_block(rows(&[20, 30])).selects_all(&partition_rows));
1427        assert!(RowAddrMask::allow_nothing().selects_all(&RowAddrTreeMap::new()));
1428    }
1429
1430    #[test]
1431    fn test_selected_indices() {
1432        // Allow list
1433        let mask = RowAddrMask::from_allowed(rows(&[10, 20, 40]));
1434        assert!(mask.selected_indices(std::iter::empty()).is_empty());
1435        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[1, 3]);
1436
1437        // Block list
1438        let mask = RowAddrMask::from_block(rows(&[10, 20, 40]));
1439        assert!(mask.selected_indices(std::iter::empty()).is_empty());
1440        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[0, 2]);
1441    }
1442
1443    #[test]
1444    fn test_also_allow() {
1445        // Allow list
1446        let mask = RowAddrMask::from_allowed(rows(&[10, 20]));
1447        let new_mask = mask.also_allow(rows(&[20, 30, 40]));
1448        assert_eq!(new_mask, RowAddrMask::from_allowed(rows(&[10, 20, 30, 40])));
1449
1450        // Block list
1451        let mask = RowAddrMask::from_block(rows(&[10, 20, 30]));
1452        let new_mask = mask.also_allow(rows(&[20, 40]));
1453        assert_eq!(new_mask, RowAddrMask::from_block(rows(&[10, 30])));
1454    }
1455
1456    #[test]
1457    fn test_also_block() {
1458        // Allow list
1459        let mask = RowAddrMask::from_allowed(rows(&[10, 20, 30]));
1460        let new_mask = mask.also_block(rows(&[20, 40]));
1461        assert_eq!(new_mask, RowAddrMask::from_allowed(rows(&[10, 30])));
1462
1463        // Block list
1464        let mask = RowAddrMask::from_block(rows(&[10, 20]));
1465        let new_mask = mask.also_block(rows(&[20, 30, 40]));
1466        assert_eq!(new_mask, RowAddrMask::from_block(rows(&[10, 20, 30, 40])));
1467    }
1468
1469    #[test]
1470    fn test_iter_ids() {
1471        // Allow list
1472        let mask = RowAddrMask::from_allowed(rows(&[10, 20, 30]));
1473        let expected: Vec<_> = [10, 20, 30].into_iter().map(RowAddress::from).collect();
1474        assert_eq!(mask.iter_addrs().unwrap().collect::<Vec<_>>(), expected);
1475
1476        // Allow list with full fragment
1477        let mut inner = RowAddrTreeMap::default();
1478        inner.insert_fragment(10);
1479        let mask = RowAddrMask::from_allowed(inner);
1480        assert!(mask.iter_addrs().is_none());
1481
1482        // Block list
1483        let mask = RowAddrMask::from_block(rows(&[10, 20, 30]));
1484        assert!(mask.iter_addrs().is_none());
1485    }
1486
1487    #[test]
1488    fn test_row_addr_mask_intersect() {
1489        let a = rows(&[1, 2, 3]);
1490        let b = rows(&[3, 4]);
1491
1492        // allow & allow -> only rows in both
1493        assert_mask_selects(
1494            &RowAddrMask::from_allowed(a.clone()).intersect(RowAddrMask::from_allowed(b.clone())),
1495            &[3],
1496            &[1, 2, 4, 100],
1497        );
1498        // allow & block -> allowed minus blocked
1499        assert_mask_selects(
1500            &RowAddrMask::from_allowed(a.clone()).intersect(RowAddrMask::from_block(b.clone())),
1501            &[1, 2],
1502            &[3, 4, 100],
1503        );
1504        // block & allow -> same, order independent
1505        assert_mask_selects(
1506            &RowAddrMask::from_block(b.clone()).intersect(RowAddrMask::from_allowed(a.clone())),
1507            &[1, 2],
1508            &[3, 4, 100],
1509        );
1510        // block & block -> both exclusions apply
1511        assert_mask_selects(
1512            &RowAddrMask::from_block(a.clone()).intersect(RowAddrMask::from_block(b)),
1513            &[100],
1514            &[1, 2, 3, 4],
1515        );
1516        // all_rows is the identity, and intersecting with itself changes nothing
1517        let allow_a = RowAddrMask::from_allowed(a.clone());
1518        assert_eq!(allow_a.clone().intersect(RowAddrMask::all_rows()), allow_a);
1519        assert_eq!(allow_a.clone().intersect(allow_a.clone()), allow_a);
1520        // allow_nothing absorbs
1521        assert_mask_selects(
1522            &RowAddrMask::allow_nothing().intersect(RowAddrMask::from_allowed(a)),
1523            &[],
1524            &[1, 2, 3, 100],
1525        );
1526    }
1527
1528    #[test]
1529    fn test_row_addr_mask_from_serialized_parts() {
1530        fn ser(tm: &RowAddrTreeMap) -> Vec<u8> {
1531            let mut buf = Vec::new();
1532            tm.serialize_into(&mut buf).unwrap();
1533            buf
1534        }
1535        let allow = ser(&rows(&[1, 2, 3]));
1536        let block = ser(&rows(&[3, 4]));
1537
1538        // Neither part means "no mask", which is not the same as "select nothing".
1539        assert!(
1540            RowAddrMask::from_serialized_parts(None, None)
1541                .unwrap()
1542                .is_none()
1543        );
1544
1545        let m = RowAddrMask::from_serialized_parts(Some(&allow), None)
1546            .unwrap()
1547            .unwrap();
1548        assert_mask_selects(&m, &[1, 2, 3], &[4, 100]);
1549
1550        let m = RowAddrMask::from_serialized_parts(None, Some(&block))
1551            .unwrap()
1552            .unwrap();
1553        assert_mask_selects(&m, &[1, 2, 100], &[3, 4]);
1554
1555        // Block wins on the overlap.
1556        let m = RowAddrMask::from_serialized_parts(Some(&allow), Some(&block))
1557            .unwrap()
1558            .unwrap();
1559        assert_mask_selects(&m, &[1, 2], &[3, 4, 100]);
1560
1561        // Round trips through the same encoding the caller used.
1562        let again = RowAddrMask::from_serialized_parts(Some(&ser(m.allow_list().unwrap())), None)
1563            .unwrap()
1564            .unwrap();
1565        assert_mask_selects(&again, &[1, 2], &[3, 4]);
1566
1567        assert!(RowAddrMask::from_serialized_parts(Some(b"not a treemap"), None).is_err());
1568
1569        // A declared bitmap size must not be allocated before the bytes are
1570        // known to exist: this 12-byte payload claims ~4 GiB.
1571        let bomb = [
1572            1u8, 0, 0, 0, // one entry
1573            0, 0, 0, 0, // fragment zero
1574            0xff, 0xff, 0xff, 0xff, // declared bitmap size
1575        ];
1576        let err = RowAddrMask::from_serialized_parts(Some(&bomb), None).unwrap_err();
1577        assert!(
1578            err.to_string().contains("only 0 bytes remain"),
1579            "expected a length complaint, got: {err}"
1580        );
1581    }
1582
1583    #[test]
1584    fn test_row_addr_mask_not() {
1585        let allow_list = RowAddrMask::from_allowed(rows(&[1, 2, 3]));
1586        let block_list = !allow_list.clone();
1587        assert_eq!(block_list, RowAddrMask::from_block(rows(&[1, 2, 3])));
1588        // Can roundtrip by negating again
1589        assert_eq!(!block_list, allow_list);
1590    }
1591
1592    #[test]
1593    fn test_ops() {
1594        let mask = RowAddrMask::default();
1595        assert_mask_selects(&mask, &[1, 5], &[]);
1596
1597        let block_list = mask.also_block(rows(&[0, 5, 15]));
1598        assert_mask_selects(&block_list, &[1], &[5]);
1599
1600        let allow_list = RowAddrMask::from_allowed(rows(&[0, 2, 5]));
1601        assert_mask_selects(&allow_list, &[5], &[1]);
1602
1603        let combined = block_list & allow_list;
1604        assert_mask_selects(&combined, &[2], &[0, 5]);
1605
1606        let other = RowAddrMask::from_allowed(rows(&[3]));
1607        let combined = combined | other;
1608        assert_mask_selects(&combined, &[2, 3], &[0, 5]);
1609
1610        let block_list = RowAddrMask::from_block(rows(&[0]));
1611        let allow_list = RowAddrMask::from_allowed(rows(&[3]));
1612
1613        let combined = block_list | allow_list;
1614        assert_mask_selects(&combined, &[1], &[]);
1615    }
1616
1617    #[test]
1618    fn test_logical_and() {
1619        let allow1 = RowAddrMask::from_allowed(rows(&[0, 1]));
1620        let block1 = RowAddrMask::from_block(rows(&[1, 2]));
1621        let allow2 = RowAddrMask::from_allowed(rows(&[1, 2, 3, 4]));
1622        let block2 = RowAddrMask::from_block(rows(&[3, 4]));
1623
1624        fn check(lhs: &RowAddrMask, rhs: &RowAddrMask, expected: &[u64]) {
1625            for mask in [lhs.clone() & rhs.clone(), rhs.clone() & lhs.clone()] {
1626                assert_eq!(selected_in_range(&mask, 0..10), expected);
1627            }
1628        }
1629
1630        // Allow & Allow
1631        check(&allow1, &allow1, &[0, 1]);
1632        check(&allow1, &allow2, &[1]);
1633
1634        // Block & Block
1635        check(&block1, &block1, &[0, 3, 4, 5, 6, 7, 8, 9]);
1636        check(&block1, &block2, &[0, 5, 6, 7, 8, 9]);
1637
1638        // Allow & Block
1639        check(&allow1, &block1, &[0]);
1640        check(&allow1, &block2, &[0, 1]);
1641        check(&allow2, &block1, &[3, 4]);
1642        check(&allow2, &block2, &[1, 2]);
1643    }
1644
1645    #[test]
1646    fn test_logical_or() {
1647        let allow1 = RowAddrMask::from_allowed(rows(&[5, 6, 7, 8, 9]));
1648        let block1 = RowAddrMask::from_block(rows(&[5, 6]));
1649        let mixed1 = allow1.clone().also_block(rows(&[5, 6]));
1650        let allow2 = RowAddrMask::from_allowed(rows(&[2, 3, 4, 5, 6, 7, 8]));
1651        let block2 = RowAddrMask::from_block(rows(&[4, 5]));
1652        let mixed2 = allow2.clone().also_block(rows(&[4, 5]));
1653
1654        fn check(lhs: &RowAddrMask, rhs: &RowAddrMask, expected: &[u64]) {
1655            for mask in [lhs.clone() | rhs.clone(), rhs.clone() | lhs.clone()] {
1656                assert_eq!(selected_in_range(&mask, 0..10), expected);
1657            }
1658        }
1659
1660        check(&allow1, &allow1, &[5, 6, 7, 8, 9]);
1661        check(&block1, &block1, &[0, 1, 2, 3, 4, 7, 8, 9]);
1662        check(&mixed1, &mixed1, &[7, 8, 9]);
1663        check(&allow2, &allow2, &[2, 3, 4, 5, 6, 7, 8]);
1664        check(&block2, &block2, &[0, 1, 2, 3, 6, 7, 8, 9]);
1665        check(&mixed2, &mixed2, &[2, 3, 6, 7, 8]);
1666
1667        check(&allow1, &block1, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1668        check(&allow1, &mixed1, &[5, 6, 7, 8, 9]);
1669        check(&allow1, &allow2, &[2, 3, 4, 5, 6, 7, 8, 9]);
1670        check(&allow1, &block2, &[0, 1, 2, 3, 5, 6, 7, 8, 9]);
1671        check(&allow1, &mixed2, &[2, 3, 5, 6, 7, 8, 9]);
1672        check(&block1, &mixed1, &[0, 1, 2, 3, 4, 7, 8, 9]);
1673        check(&block1, &allow2, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1674        check(&block1, &block2, &[0, 1, 2, 3, 4, 6, 7, 8, 9]);
1675        check(&block1, &mixed2, &[0, 1, 2, 3, 4, 6, 7, 8, 9]);
1676        check(&mixed1, &allow2, &[2, 3, 4, 5, 6, 7, 8, 9]);
1677        check(&mixed1, &block2, &[0, 1, 2, 3, 6, 7, 8, 9]);
1678        check(&mixed1, &mixed2, &[2, 3, 6, 7, 8, 9]);
1679        check(&allow2, &block2, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1680        check(&allow2, &mixed2, &[2, 3, 4, 5, 6, 7, 8]);
1681        check(&block2, &mixed2, &[0, 1, 2, 3, 6, 7, 8, 9]);
1682    }
1683
1684    #[test]
1685    fn test_deserialize_legacy_format() {
1686        // Test that we can deserialize the old format where both allow_list
1687        // and block_list could be present in the serialized form.
1688        //
1689        // The old format (before this PR) used a struct with both allow_list and block_list
1690        // fields. The new format uses an enum. The deserialization code should handle
1691        // the case where both lists are present by converting to AllowList(allow - block).
1692
1693        // Create the RowIdTreeMaps and serialize them directly
1694        let allow = rows(&[1, 2, 3, 4, 5, 10, 15]);
1695        let block = rows(&[2, 4, 15]);
1696
1697        // Serialize using the stable RowIdTreeMap serialization format
1698        let block_bytes = {
1699            let mut buf = Vec::with_capacity(block.serialized_size());
1700            block.serialize_into(&mut buf).unwrap();
1701            buf
1702        };
1703        let allow_bytes = {
1704            let mut buf = Vec::with_capacity(allow.serialized_size());
1705            allow.serialize_into(&mut buf).unwrap();
1706            buf
1707        };
1708
1709        // Construct a binary array with both values present (simulating old format)
1710        let old_format_array =
1711            BinaryArray::from_opt_vec(vec![Some(&block_bytes), Some(&allow_bytes)]);
1712
1713        // Deserialize - should handle this by creating AllowList(allow - block)
1714        let deserialized = RowAddrMask::from_arrow(&old_format_array).unwrap();
1715
1716        // The expected result: AllowList([1, 2, 3, 4, 5, 10, 15] - [2, 4, 15]) = [1, 3, 5, 10]
1717        assert_mask_selects(&deserialized, &[1, 3, 5, 10], &[2, 4, 15]);
1718        assert!(
1719            deserialized.allow_list().is_some(),
1720            "Should deserialize to AllowList variant"
1721        );
1722    }
1723
1724    #[test]
1725    fn test_roundtrip_arrow() {
1726        let row_addrs = rows(&[1, 2, 3, 100, 2000]);
1727
1728        // Allow list
1729        let original = RowAddrMask::from_allowed(row_addrs.clone());
1730        let array = original.into_arrow().unwrap();
1731        assert_eq!(RowAddrMask::from_arrow(&array).unwrap(), original);
1732
1733        // Block list
1734        let original = RowAddrMask::from_block(row_addrs);
1735        let array = original.into_arrow().unwrap();
1736        assert_eq!(RowAddrMask::from_arrow(&array).unwrap(), original);
1737    }
1738
1739    #[test]
1740    fn test_deserialize_legacy_empty_lists() {
1741        // Case 1: Both None (should become all_rows)
1742        let array = BinaryArray::from_opt_vec(vec![None, None]);
1743        let mask = RowAddrMask::from_arrow(&array).unwrap();
1744        assert_mask_selects(&mask, &[0, 100, u64::MAX], &[]);
1745
1746        // Case 2: Only block list (no allow list)
1747        let block = rows(&[5, 10]);
1748        let block_bytes = {
1749            let mut buf = Vec::with_capacity(block.serialized_size());
1750            block.serialize_into(&mut buf).unwrap();
1751            buf
1752        };
1753        let array = BinaryArray::from_opt_vec(vec![Some(&block_bytes[..]), None]);
1754        let mask = RowAddrMask::from_arrow(&array).unwrap();
1755        assert_mask_selects(&mask, &[0, 15], &[5, 10]);
1756
1757        // Case 3: Only allow list (no block list)
1758        let allow = rows(&[5, 10]);
1759        let allow_bytes = {
1760            let mut buf = Vec::with_capacity(allow.serialized_size());
1761            allow.serialize_into(&mut buf).unwrap();
1762            buf
1763        };
1764        let array = BinaryArray::from_opt_vec(vec![None, Some(&allow_bytes[..])]);
1765        let mask = RowAddrMask::from_arrow(&array).unwrap();
1766        assert_mask_selects(&mask, &[5, 10], &[0, 15]);
1767    }
1768
1769    #[test]
1770    fn test_map_insert() {
1771        let mut map = RowAddrTreeMap::default();
1772
1773        assert!(!map.contains(20));
1774        assert!(map.insert(20));
1775        assert!(map.contains(20));
1776        assert!(!map.insert(20)); // Inserting again should be no-op
1777
1778        let bitmap = map.get_fragment_bitmap(0);
1779        assert!(bitmap.is_some());
1780        let bitmap = bitmap.unwrap();
1781        assert_eq!(bitmap.len(), 1);
1782
1783        assert!(map.get_fragment_bitmap(1).is_none());
1784
1785        map.insert_fragment(0);
1786        assert!(map.contains(0));
1787        assert!(!map.insert(0)); // Inserting into full fragment should be no-op
1788        assert!(map.get_fragment_bitmap(0).is_none());
1789    }
1790
1791    #[test]
1792    fn test_map_insert_range() {
1793        let ranges = &[
1794            (0..10),
1795            (40..500),
1796            ((u32::MAX as u64 - 10)..(u32::MAX as u64 + 20)),
1797        ];
1798
1799        for range in ranges {
1800            let mut mask = RowAddrTreeMap::default();
1801
1802            let count = mask.insert_range(range.clone());
1803            let expected = range.end - range.start;
1804            assert_eq!(count, expected);
1805
1806            let count = mask.insert_range(range.clone());
1807            assert_eq!(count, 0);
1808
1809            let new_range = range.start + 5..range.end + 5;
1810            let count = mask.insert_range(new_range.clone());
1811            assert_eq!(count, 5);
1812        }
1813
1814        let mut mask = RowAddrTreeMap::default();
1815        let count = mask.insert_range(..10);
1816        assert_eq!(count, 10);
1817        assert!(mask.contains(0));
1818
1819        let count = mask.insert_range(20..=24);
1820        assert_eq!(count, 5);
1821
1822        mask.insert_fragment(0);
1823        let count = mask.insert_range(100..200);
1824        assert_eq!(count, 0);
1825    }
1826
1827    #[test]
1828    fn test_map_remove() {
1829        let mut mask = RowAddrTreeMap::default();
1830
1831        assert!(!mask.remove(20));
1832
1833        mask.insert(20);
1834        assert!(mask.contains(20));
1835        assert!(mask.remove(20));
1836        assert!(!mask.contains(20));
1837
1838        mask.insert_range(10..=20);
1839        assert!(mask.contains(15));
1840        assert!(mask.remove(15));
1841        assert!(!mask.contains(15));
1842
1843        // We don't test removing from a full fragment, because that would take
1844        // a lot of memory.
1845    }
1846
1847    #[test]
1848    fn test_map_mask() {
1849        let mask = rows(&[0, 1, 2]);
1850        let mask2 = rows(&[0, 2, 3]);
1851
1852        let allow_list = RowAddrMask::AllowList(mask2.clone());
1853        let mut actual = mask.clone();
1854        actual.mask(&allow_list);
1855        assert_eq!(actual, rows(&[0, 2]));
1856
1857        let block_list = RowAddrMask::BlockList(mask2);
1858        let mut actual = mask;
1859        actual.mask(&block_list);
1860        assert_eq!(actual, rows(&[1]));
1861    }
1862
1863    #[test]
1864    #[should_panic(expected = "Size of full fragment is unknown")]
1865    fn test_map_insert_full_fragment_row() {
1866        let mut mask = RowAddrTreeMap::default();
1867        mask.insert_fragment(0);
1868
1869        let _ = mask.into_addr_iter().collect::<Vec<u64>>();
1870    }
1871
1872    #[test]
1873    fn test_map_into_addr_iter() {
1874        let mut mask = RowAddrTreeMap::default();
1875        mask.insert(0);
1876        mask.insert(1);
1877        mask.insert(1 << 32 | 5);
1878        mask.insert(2 << 32 | 10);
1879
1880        let expected = vec![0u64, 1, 1 << 32 | 5, 2 << 32 | 10];
1881        let actual: Vec<u64> = mask.into_addr_iter().collect();
1882        assert_eq!(actual, expected);
1883    }
1884
1885    #[test]
1886    fn test_map_iter_runs() {
1887        // Three contiguous regions across two fragments.
1888        let mut mask = RowAddrTreeMap::default();
1889        mask.insert_range(0..3);
1890        mask.insert_range(10..15);
1891        mask.insert_range((1u64 << 32) + 100..(1u64 << 32) + 103);
1892
1893        let runs: Vec<(u32, RangeInclusive<u32>)> = mask.iter_runs().collect();
1894        assert_eq!(runs, vec![(0, 0..=2), (0, 10..=14), (1, 100..=102)]);
1895    }
1896
1897    #[test]
1898    fn test_map_iter_runs_matches_into_addr_iter() {
1899        // Confirm iter_runs and into_addr_iter agree on a non-trivial shape.
1900        let mut mask = RowAddrTreeMap::default();
1901        mask.insert_range(5..7);
1902        mask.insert_range(11..12);
1903        mask.insert_range(20..25);
1904        mask.insert_range((1u64 << 32)..(1u64 << 32) + 3);
1905
1906        let from_runs: Vec<u64> = mask
1907            .iter_runs()
1908            .flat_map(|(frag, run)| {
1909                let frag = u64::from(frag);
1910                (*run.start()..=*run.end()).map(move |v| (frag << 32) | u64::from(v))
1911            })
1912            .collect();
1913        let from_bits: Vec<u64> = mask.clone().into_addr_iter().collect();
1914        assert_eq!(from_runs, from_bits);
1915    }
1916
1917    #[test]
1918    fn test_map_from() {
1919        let map = RowAddrTreeMap::from(10..12);
1920        assert!(map.contains(10));
1921        assert!(map.contains(11));
1922        assert!(!map.contains(12));
1923        assert!(!map.contains(3));
1924
1925        let map = RowAddrTreeMap::from(10..=12);
1926        assert!(map.contains(10));
1927        assert!(map.contains(11));
1928        assert!(map.contains(12));
1929        assert!(!map.contains(3));
1930    }
1931
1932    #[test]
1933    fn test_map_from_roaring() {
1934        let bitmap = RoaringTreemap::from_iter(&[0, 1, 1 << 32]);
1935        let map = RowAddrTreeMap::from(bitmap);
1936        assert!(map.contains(0) && map.contains(1) && map.contains(1 << 32));
1937        assert!(!map.contains(2));
1938    }
1939
1940    #[test]
1941    fn test_map_extend() {
1942        let mut map = RowAddrTreeMap::default();
1943        map.insert(0);
1944        map.insert_fragment(1);
1945
1946        let other_rows = [0, 2, 1 << 32 | 10, 3 << 32 | 5];
1947        map.extend(other_rows.iter().copied());
1948
1949        assert!(map.contains(0));
1950        assert!(map.contains(2));
1951        assert!(map.contains(1 << 32 | 5));
1952        assert!(map.contains(1 << 32 | 10));
1953        assert!(map.contains(3 << 32 | 5));
1954        assert!(!map.contains(3));
1955    }
1956
1957    #[test]
1958    fn test_map_extend_other_maps() {
1959        let mut map = RowAddrTreeMap::default();
1960        map.insert(0);
1961        map.insert_fragment(1);
1962        map.insert(4 << 32);
1963
1964        let mut other_map = rows(&[0, 2, 1 << 32 | 10, 3 << 32 | 5]);
1965        other_map.insert_fragment(4);
1966        map.extend(std::iter::once(other_map));
1967
1968        for id in [
1969            0,
1970            2,
1971            1 << 32 | 5,
1972            1 << 32 | 10,
1973            3 << 32 | 5,
1974            4 << 32,
1975            4 << 32 | 7,
1976        ] {
1977            assert!(map.contains(id), "Expected {} to be contained", id);
1978        }
1979        assert!(!map.contains(3));
1980    }
1981
1982    proptest::proptest! {
1983        #[test]
1984        fn test_map_serialization_roundtrip(
1985            values in proptest::collection::vec(
1986                (0..u32::MAX, proptest::option::of(proptest::collection::vec(0..u32::MAX, 0..1000))),
1987                0..10
1988            )
1989        ) {
1990            let mut mask = RowAddrTreeMap::default();
1991            for (fragment, rows) in values {
1992                if let Some(rows) = rows {
1993                    let bitmap = RoaringBitmap::from_iter(rows);
1994                    mask.insert_bitmap(fragment, bitmap);
1995                } else {
1996                    mask.insert_fragment(fragment);
1997                }
1998            }
1999
2000            let mut data = Vec::new();
2001            mask.serialize_into(&mut data).unwrap();
2002            let deserialized = RowAddrTreeMap::deserialize_from(data.as_slice()).unwrap();
2003            prop_assert_eq!(mask, deserialized);
2004        }
2005
2006        #[test]
2007        fn test_map_intersect(
2008            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2009            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2010            right_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2011            right_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2012        ) {
2013            let mut left = RowAddrTreeMap::default();
2014            for fragment in left_full_fragments.clone() {
2015                left.insert_fragment(fragment);
2016            }
2017            left.extend(left_rows.iter().copied());
2018
2019            let mut right = RowAddrTreeMap::default();
2020            for fragment in right_full_fragments.clone() {
2021                right.insert_fragment(fragment);
2022            }
2023            right.extend(right_rows.iter().copied());
2024
2025            let mut expected = RowAddrTreeMap::default();
2026            for fragment in &left_full_fragments {
2027                if right_full_fragments.contains(fragment) {
2028                    expected.insert_fragment(*fragment);
2029                }
2030            }
2031
2032            let left_in_right = left_rows.iter().filter(|row| {
2033                right_rows.contains(row)
2034                    || right_full_fragments.contains(&((*row >> 32) as u32))
2035            });
2036            expected.extend(left_in_right);
2037            let right_in_left = right_rows.iter().filter(|row| {
2038                left_rows.contains(row)
2039                    || left_full_fragments.contains(&((*row >> 32) as u32))
2040            });
2041            expected.extend(right_in_left);
2042
2043            let actual = left & right;
2044            prop_assert_eq!(expected, actual);
2045        }
2046
2047        #[test]
2048        fn test_map_union(
2049            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2050            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2051            right_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2052            right_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2053        ) {
2054            let mut left = RowAddrTreeMap::default();
2055            for fragment in left_full_fragments.clone() {
2056                left.insert_fragment(fragment);
2057            }
2058            left.extend(left_rows.iter().copied());
2059
2060            let mut right = RowAddrTreeMap::default();
2061            for fragment in right_full_fragments.clone() {
2062                right.insert_fragment(fragment);
2063            }
2064            right.extend(right_rows.iter().copied());
2065
2066            let mut expected = RowAddrTreeMap::default();
2067            for fragment in left_full_fragments {
2068                expected.insert_fragment(fragment);
2069            }
2070            for fragment in right_full_fragments {
2071                expected.insert_fragment(fragment);
2072            }
2073
2074            let combined_rows = left_rows.iter().chain(right_rows.iter());
2075            expected.extend(combined_rows);
2076
2077            let actual = left | right;
2078            for actual_key_val in &actual.inner {
2079                proptest::prop_assert!(expected.inner.contains_key(actual_key_val.0));
2080                let expected_val = expected.inner.get(actual_key_val.0).unwrap();
2081                prop_assert_eq!(
2082                    actual_key_val.1,
2083                    expected_val,
2084                    "error on key {}",
2085                    actual_key_val.0
2086                );
2087            }
2088            prop_assert_eq!(expected, actual);
2089        }
2090
2091        #[test]
2092        fn test_map_subassign_rows(
2093            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2094            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2095            right_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2096        ) {
2097            let mut left = RowAddrTreeMap::default();
2098            for fragment in left_full_fragments {
2099                left.insert_fragment(fragment);
2100            }
2101            left.extend(left_rows.iter().copied());
2102
2103            let mut right = RowAddrTreeMap::default();
2104            right.extend(right_rows.iter().copied());
2105
2106            let mut expected = left.clone();
2107            for row in right_rows {
2108                expected.remove(row);
2109            }
2110
2111            left -= &right;
2112            prop_assert_eq!(expected, left);
2113        }
2114
2115        #[test]
2116        fn test_map_subassign_frags(
2117            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2118            right_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2119            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2120        ) {
2121            let mut left = RowAddrTreeMap::default();
2122            for fragment in left_full_fragments {
2123                left.insert_fragment(fragment);
2124            }
2125            left.extend(left_rows.iter().copied());
2126
2127            let mut right = RowAddrTreeMap::default();
2128            for fragment in right_full_fragments.clone() {
2129                right.insert_fragment(fragment);
2130            }
2131
2132            let mut expected = left.clone();
2133            for fragment in right_full_fragments {
2134                expected.inner.remove(&fragment);
2135            }
2136
2137            left -= &right;
2138            prop_assert_eq!(expected, left);
2139        }
2140
2141        #[test]
2142        fn test_from_sorted_iter(
2143            mut rows in proptest::collection::vec(0..u64::MAX, 0..1000)
2144        ) {
2145            rows.sort();
2146            let num_rows = rows.len();
2147            let mask = RowAddrTreeMap::from_sorted_iter(rows).unwrap();
2148            prop_assert_eq!(mask.len(), Some(num_rows as u64));
2149        }
2150
2151
2152    }
2153
2154    #[test]
2155    fn test_row_addr_selection_deep_size_of() {
2156        use lance_core::deepsize::DeepSizeOf;
2157
2158        // Test Full variant - should have minimal size (just the enum discriminant)
2159        let full = RowAddrSelection::Full;
2160        let full_size = full.deep_size_of();
2161        // Full variant has no heap allocations beyond the enum itself
2162        assert!(full_size < 100); // Small sanity check
2163
2164        // Test Partial variant - should include bitmap size
2165        let mut bitmap = RoaringBitmap::new();
2166        bitmap.insert_range(0..100);
2167        let partial = RowAddrSelection::Partial(bitmap.clone());
2168        let partial_size = partial.deep_size_of();
2169        // Partial variant should be larger due to bitmap
2170        assert!(partial_size >= bitmap.serialized_size());
2171    }
2172
2173    #[test]
2174    fn test_row_addr_selection_union_all_with_full() {
2175        let full = RowAddrSelection::Full;
2176        let partial = RowAddrSelection::Partial(RoaringBitmap::from_iter(&[1, 2, 3]));
2177
2178        assert!(matches!(
2179            RowAddrSelection::union_all(&[&full, &partial]),
2180            RowAddrSelection::Full
2181        ));
2182
2183        let partial2 = RowAddrSelection::Partial(RoaringBitmap::from_iter(&[4, 5, 6]));
2184        let RowAddrSelection::Partial(bitmap) = RowAddrSelection::union_all(&[&partial, &partial2])
2185        else {
2186            panic!("Expected Partial");
2187        };
2188        assert!(bitmap.contains(1) && bitmap.contains(4));
2189    }
2190
2191    #[test]
2192    fn test_insert_range_unbounded_start() {
2193        let mut map = RowAddrTreeMap::default();
2194
2195        // Test exclusive start bound
2196        let count = map.insert_range((std::ops::Bound::Excluded(5), std::ops::Bound::Included(10)));
2197        assert_eq!(count, 5); // 6, 7, 8, 9, 10
2198        assert!(!map.contains(5));
2199        assert!(map.contains(6));
2200        assert!(map.contains(10));
2201
2202        // Test unbounded end
2203        let mut map2 = RowAddrTreeMap::default();
2204        let count = map2.insert_range(0..5);
2205        assert_eq!(count, 5);
2206        assert!(map2.contains(0));
2207        assert!(map2.contains(4));
2208        assert!(!map2.contains(5));
2209    }
2210
2211    #[test]
2212    fn test_remove_from_full_fragment() {
2213        let mut map = RowAddrTreeMap::default();
2214        map.insert_fragment(0);
2215
2216        // Verify it's a full fragment - get_fragment_bitmap returns None for Full
2217        for id in [0, 100, u32::MAX as u64] {
2218            assert!(map.contains(id));
2219        }
2220        assert!(map.get_fragment_bitmap(0).is_none());
2221
2222        // Remove a value from the full fragment
2223        assert!(map.remove(50));
2224
2225        // Now it should be partial (a full RoaringBitmap minus one value)
2226        assert!(map.contains(0) && !map.contains(50) && map.contains(100));
2227        assert!(map.get_fragment_bitmap(0).is_some());
2228    }
2229
2230    #[test]
2231    fn test_retain_fragments() {
2232        let mut map = RowAddrTreeMap::default();
2233        map.insert(0); // fragment 0
2234        map.insert(1 << 32 | 5); // fragment 1
2235        map.insert(2 << 32 | 10); // fragment 2
2236        map.insert_fragment(3); // fragment 3
2237
2238        map.retain_fragments([0, 2]);
2239
2240        assert!(map.contains(0) && map.contains(2 << 32 | 10));
2241        assert!(!map.contains(1 << 32 | 5) && !map.contains(3 << 32));
2242    }
2243
2244    #[test]
2245    fn test_bitor_assign_full_fragment() {
2246        // Test BitOrAssign when LHS has Full and RHS has Partial
2247        let mut map1 = RowAddrTreeMap::default();
2248        map1.insert_fragment(0);
2249        let mut map2 = RowAddrTreeMap::default();
2250        map2.insert(5);
2251
2252        map1 |= &map2;
2253        // Full | Partial = Full
2254        assert!(map1.contains(0) && map1.contains(5) && map1.contains(100));
2255
2256        // Test BitOrAssign when LHS has Partial and RHS has Full
2257        let mut map3 = RowAddrTreeMap::default();
2258        map3.insert(5);
2259        let mut map4 = RowAddrTreeMap::default();
2260        map4.insert_fragment(0);
2261
2262        map3 |= &map4;
2263        // Partial | Full = Full
2264        assert!(map3.contains(0) && map3.contains(5) && map3.contains(100));
2265    }
2266
2267    #[test]
2268    fn test_bitand_assign_full_fragments() {
2269        // Test BitAndAssign when both have Full for same fragment
2270        let mut map1 = RowAddrTreeMap::default();
2271        map1.insert_fragment(0);
2272        let mut map2 = RowAddrTreeMap::default();
2273        map2.insert_fragment(0);
2274
2275        map1 &= &map2;
2276        // Full & Full = Full
2277        assert!(map1.contains(0) && map1.contains(100));
2278
2279        // Test BitAndAssign when LHS Full, RHS Partial
2280        let mut map3 = RowAddrTreeMap::default();
2281        map3.insert_fragment(0);
2282        let mut map4 = RowAddrTreeMap::default();
2283        map4.insert(5);
2284        map4.insert(10);
2285
2286        map3 &= &map4;
2287        // Full & Partial([5,10]) = Partial([5,10])
2288        assert!(map3.contains(5) && map3.contains(10));
2289        assert!(!map3.contains(0) && !map3.contains(100));
2290
2291        // Test that empty intersection results in removal
2292        let mut map5 = RowAddrTreeMap::default();
2293        map5.insert(5);
2294        let mut map6 = RowAddrTreeMap::default();
2295        map6.insert(10);
2296
2297        map5 &= &map6;
2298        assert!(map5.is_empty());
2299    }
2300
2301    #[test]
2302    fn test_sub_assign_with_full_fragments() {
2303        // Test SubAssign when LHS is Full and RHS is Partial
2304        let mut map1 = RowAddrTreeMap::default();
2305        map1.insert_fragment(0);
2306        let mut map2 = RowAddrTreeMap::default();
2307        map2.insert(5);
2308        map2.insert(10);
2309
2310        map1 -= &map2;
2311        // Full - Partial([5,10]) = Full minus those values
2312        assert!(map1.contains(0) && map1.contains(100));
2313        assert!(!map1.contains(5) && !map1.contains(10));
2314
2315        // Test SubAssign when both are Full for same fragment
2316        let mut map3 = RowAddrTreeMap::default();
2317        map3.insert_fragment(0);
2318        let mut map4 = RowAddrTreeMap::default();
2319        map4.insert_fragment(0);
2320
2321        map3 -= &map4;
2322        // Full - Full = empty
2323        assert!(map3.is_empty());
2324
2325        // Test SubAssign when LHS is Partial and RHS is Full
2326        let mut map5 = RowAddrTreeMap::default();
2327        map5.insert(5);
2328        map5.insert(10);
2329        let mut map6 = RowAddrTreeMap::default();
2330        map6.insert_fragment(0);
2331
2332        map5 -= &map6;
2333        // Partial - Full = empty
2334        assert!(map5.is_empty());
2335    }
2336
2337    #[test]
2338    fn test_from_iterator_with_full_fragment() {
2339        // Test that inserting into a full fragment is a no-op
2340        let mut map = RowAddrTreeMap::default();
2341        map.insert_fragment(0);
2342
2343        // Extend with values that would go into fragment 0
2344        map.extend([5u64, 10, 100].iter());
2345
2346        // Should still be full fragment
2347        for id in [0, 5, 10, 100, u32::MAX as u64] {
2348            assert!(map.contains(id));
2349        }
2350    }
2351
2352    #[test]
2353    fn test_insert_range_excluded_end() {
2354        // Test excluded end bound (line 391-393)
2355        let mut map = RowAddrTreeMap::default();
2356        // Using RangeFrom with small range won't hit the unbounded case
2357        // Instead test Bound::Excluded for end
2358        let count = map.insert_range((std::ops::Bound::Included(5), std::ops::Bound::Excluded(10)));
2359        assert_eq!(count, 5); // 5, 6, 7, 8, 9
2360        assert!(map.contains(5));
2361        assert!(map.contains(9));
2362        assert!(!map.contains(10));
2363    }
2364
2365    #[test]
2366    fn test_bitand_assign_owned() {
2367        // Test BitAndAssign<Self> (owned, not reference)
2368        let mut map1 = RowAddrTreeMap::default();
2369        map1.insert(5);
2370        map1.insert(10);
2371
2372        // Using owned rhs (not reference)
2373        map1 &= rows(&[5, 15]);
2374
2375        assert!(map1.contains(5));
2376        assert!(!map1.contains(10) && !map1.contains(15));
2377    }
2378
2379    #[test]
2380    fn test_from_iter_with_full_fragment() {
2381        // When we collect into RowAddrTreeMap, it should handle duplicates
2382        let map: RowAddrTreeMap = vec![5u64, 10, 100].into_iter().collect();
2383        assert!(map.contains(5) && map.contains(10));
2384
2385        // Test that extending a map with full fragment ignores new values
2386        let mut map = RowAddrTreeMap::default();
2387        map.insert_fragment(0);
2388        for val in [5, 10, 100] {
2389            map.insert(val); // This should be no-op since fragment is full
2390        }
2391        // Still full fragment
2392        for id in [0, 5, u32::MAX as u64] {
2393            assert!(map.contains(id));
2394        }
2395    }
2396
2397    // ============================================================================
2398    // Tests for bitmap_to_ranges / ranges_to_bitmap
2399    // ============================================================================
2400
2401    #[test]
2402    fn test_bitmap_to_ranges_empty() {
2403        let bm = RoaringBitmap::new();
2404        assert!(bitmap_to_ranges(&bm).is_empty());
2405    }
2406
2407    #[test]
2408    fn test_bitmap_to_ranges_single() {
2409        let bm = RoaringBitmap::from_iter([5]);
2410        assert_eq!(bitmap_to_ranges(&bm), vec![5..6]);
2411    }
2412
2413    #[test]
2414    fn test_bitmap_to_ranges_contiguous() {
2415        let mut bm = RoaringBitmap::new();
2416        bm.insert_range(10..20);
2417        assert_eq!(bitmap_to_ranges(&bm), vec![10..20]);
2418    }
2419
2420    #[test]
2421    fn test_bitmap_to_ranges_multiple() {
2422        let mut bm = RoaringBitmap::new();
2423        bm.insert_range(0..3);
2424        bm.insert_range(10..15);
2425        bm.insert(100);
2426        assert_eq!(bitmap_to_ranges(&bm), vec![0..3, 10..15, 100..101]);
2427    }
2428
2429    #[test]
2430    fn test_ranges_to_bitmap_empty() {
2431        let bm = ranges_to_bitmap(&[], true);
2432        assert!(bm.is_empty());
2433    }
2434
2435    #[test]
2436    fn test_ranges_to_bitmap_sorted_short_ranges() {
2437        // avg len = 1, uses from_sorted_iter path
2438        let ranges = vec![0..1, 5..6, 10..11];
2439        let bm = ranges_to_bitmap(&ranges, true);
2440        assert!(bm.contains(0) && bm.contains(5) && bm.contains(10));
2441        assert_eq!(bm.len(), 3);
2442    }
2443
2444    #[test]
2445    fn test_ranges_to_bitmap_sorted_long_ranges() {
2446        // avg len = 100, uses insert_range path
2447        let ranges = vec![0..100, 200..300];
2448        let bm = ranges_to_bitmap(&ranges, true);
2449        assert_eq!(bm.len(), 200);
2450        assert!(bm.contains(0) && bm.contains(99));
2451        assert!(!bm.contains(100));
2452        assert!(bm.contains(200) && bm.contains(299));
2453    }
2454
2455    #[test]
2456    fn test_ranges_to_bitmap_unsorted() {
2457        let ranges = vec![200..300, 0..100];
2458        let bm = ranges_to_bitmap(&ranges, false);
2459        assert_eq!(bm.len(), 200);
2460        assert!(bm.contains(0) && bm.contains(250));
2461    }
2462
2463    #[test]
2464    fn test_bitmap_ranges_roundtrip() {
2465        let mut original = RoaringBitmap::new();
2466        original.insert_range(0..50);
2467        original.insert_range(100..200);
2468        original.insert(500);
2469        original.insert_range(1000..1010);
2470
2471        let ranges = bitmap_to_ranges(&original);
2472        let reconstructed = ranges_to_bitmap(&ranges, true);
2473        assert_eq!(original, reconstructed);
2474    }
2475
2476    // ============================================================================
2477    // Tests for RowIdSet
2478    // ============================================================================
2479
2480    fn row_ids(ids: &[u64]) -> RowIdSet {
2481        let mut set = RowIdSet::new();
2482        for &id in ids {
2483            set.inner.insert(id);
2484        }
2485        set
2486    }
2487
2488    #[test]
2489    fn test_row_id_set_construction() {
2490        let set = RowIdSet::new();
2491        assert!(set.is_empty());
2492        assert_eq!(set.len(), Some(0));
2493
2494        let set = row_ids(&[10, 20, 30]);
2495        assert!(!set.is_empty());
2496        assert_eq!(set.len(), Some(3));
2497        assert!(set.contains(10));
2498        assert!(set.contains(20));
2499        assert!(set.contains(30));
2500        assert!(!set.contains(15));
2501    }
2502
2503    #[test]
2504    fn test_row_id_set_remove() {
2505        let mut set = row_ids(&[10, 20, 30]);
2506
2507        assert!(!set.remove(15)); // Not present
2508        assert_eq!(set.len(), Some(3));
2509
2510        assert!(set.remove(20)); // Present
2511        assert_eq!(set.len(), Some(2));
2512        assert!(!set.contains(20));
2513        assert!(set.contains(10));
2514        assert!(set.contains(30));
2515
2516        assert!(!set.remove(20)); // Already removed
2517    }
2518
2519    #[test]
2520    fn test_row_id_set_union() {
2521        let set1 = row_ids(&[10, 20, 30]);
2522        let set2 = row_ids(&[20, 30, 40]);
2523
2524        let result = set1.union(&set2);
2525        assert_eq!(result.len(), Some(4));
2526        for id in [10, 20, 30, 40] {
2527            assert!(result.contains(id));
2528        }
2529    }
2530
2531    #[test]
2532    fn test_row_id_set_difference() {
2533        let set1 = row_ids(&[10, 20, 30, 40]);
2534        let set2 = row_ids(&[20, 40]);
2535
2536        let result = set1.difference(&set2);
2537        assert_eq!(result.len(), Some(2));
2538        assert!(result.contains(10));
2539        assert!(result.contains(30));
2540        assert!(!result.contains(20));
2541        assert!(!result.contains(40));
2542    }
2543
2544    #[test]
2545    fn test_row_id_set_union_all() {
2546        let set1 = row_ids(&[10, 20]);
2547        let set2 = row_ids(&[20, 30]);
2548        let set3 = row_ids(&[30, 40]);
2549
2550        let result = RowIdSet::union_all(&[&set1, &set2, &set3]);
2551        assert_eq!(result.len(), Some(4));
2552        for id in [10, 20, 30, 40] {
2553            assert!(result.contains(id));
2554        }
2555
2556        // Empty slice should return empty set
2557        let result = RowIdSet::union_all(&[]);
2558        assert!(result.is_empty());
2559    }
2560
2561    #[test]
2562    fn test_row_id_set_iter() {
2563        let set = row_ids(&[10, 20, 30]);
2564        let collected: Vec<u64> = set.iter().collect();
2565        assert_eq!(collected, vec![10, 20, 30]);
2566
2567        let empty = RowIdSet::new();
2568        assert_eq!(empty.iter().count(), 0);
2569    }
2570
2571    #[test]
2572    fn test_row_id_set_from_sorted_iter() {
2573        // Valid sorted input
2574        let set = RowIdSet::from_sorted_iter([10, 20, 30, 40]).unwrap();
2575        assert_eq!(set.len(), Some(4));
2576        for id in [10, 20, 30, 40] {
2577            assert!(set.contains(id));
2578        }
2579
2580        // Empty iterator
2581        let set = RowIdSet::from_sorted_iter(std::iter::empty()).unwrap();
2582        assert!(set.is_empty());
2583
2584        // Single element
2585        let set = RowIdSet::from_sorted_iter([42]).unwrap();
2586        assert_eq!(set.len(), Some(1));
2587        assert!(set.contains(42));
2588    }
2589
2590    #[test]
2591    fn test_row_id_set_from_sorted_iter_unsorted() {
2592        // Non-sorted input should return error
2593        let result = RowIdSet::from_sorted_iter([30, 10, 20]);
2594        assert!(result.is_err());
2595        assert!(result.unwrap_err().to_string().contains("non-sorted"));
2596    }
2597
2598    #[test]
2599    fn test_row_id_set_large_values() {
2600        // Test with large u64 values
2601        let large_ids = [u64::MAX - 10, u64::MAX - 5, u64::MAX - 1];
2602        let set = row_ids(&large_ids);
2603
2604        for &id in &large_ids {
2605            assert!(set.contains(id));
2606        }
2607        assert!(!set.contains(u64::MAX));
2608        assert_eq!(set.len(), Some(3));
2609    }
2610
2611    // ============================================================================
2612    // Tests for RowIdMask
2613    // ============================================================================
2614
2615    fn assert_row_id_mask_selects(mask: &RowIdMask, selected: &[u64], not_selected: &[u64]) {
2616        for &id in selected {
2617            assert!(mask.selected(id), "Expected row id {} to be selected", id);
2618        }
2619        for &id in not_selected {
2620            assert!(
2621                !mask.selected(id),
2622                "Expected row id {} to NOT be selected",
2623                id
2624            );
2625        }
2626    }
2627
2628    #[test]
2629    fn test_row_id_mask_construction() {
2630        let full_mask = RowIdMask::all_rows();
2631        assert_eq!(full_mask.max_len(), None);
2632        assert_row_id_mask_selects(&full_mask, &[0, 1, 100, u64::MAX - 1], &[]);
2633
2634        let empty_mask = RowIdMask::allow_nothing();
2635        assert_eq!(empty_mask.max_len(), Some(0));
2636        assert_row_id_mask_selects(&empty_mask, &[], &[0, 1, 100]);
2637
2638        let allow_list = RowIdMask::from_allowed(row_ids(&[10, 20, 30]));
2639        assert_eq!(allow_list.max_len(), Some(3));
2640        assert_row_id_mask_selects(&allow_list, &[10, 20, 30], &[0, 15, 25, 40]);
2641
2642        let block_list = RowIdMask::from_block(row_ids(&[10, 20, 30]));
2643        assert_eq!(block_list.max_len(), None);
2644        assert_row_id_mask_selects(&block_list, &[0, 15, 25, 40], &[10, 20, 30]);
2645    }
2646
2647    #[test]
2648    fn test_row_id_mask_selected_indices() {
2649        // Allow list
2650        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 40]));
2651        assert!(mask.selected_indices(std::iter::empty()).is_empty());
2652        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[1, 3]);
2653
2654        // Block list
2655        let mask = RowIdMask::from_block(row_ids(&[10, 20, 40]));
2656        assert!(mask.selected_indices(std::iter::empty()).is_empty());
2657        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[0, 2]);
2658    }
2659
2660    #[test]
2661    fn test_row_id_mask_also_allow() {
2662        // Allow list
2663        let mask = RowIdMask::from_allowed(row_ids(&[10, 20]));
2664        let new_mask = mask.also_allow(row_ids(&[20, 30, 40]));
2665        assert_eq!(
2666            new_mask,
2667            RowIdMask::from_allowed(row_ids(&[10, 20, 30, 40]))
2668        );
2669
2670        // Block list
2671        let mask = RowIdMask::from_block(row_ids(&[10, 20, 30]));
2672        let new_mask = mask.also_allow(row_ids(&[20, 40]));
2673        assert_eq!(new_mask, RowIdMask::from_block(row_ids(&[10, 30])));
2674    }
2675
2676    #[test]
2677    fn test_row_id_mask_also_block() {
2678        // Allow list
2679        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 30]));
2680        let new_mask = mask.also_block(row_ids(&[20, 40]));
2681        assert_eq!(new_mask, RowIdMask::from_allowed(row_ids(&[10, 30])));
2682
2683        // Block list
2684        let mask = RowIdMask::from_block(row_ids(&[10, 20]));
2685        let new_mask = mask.also_block(row_ids(&[20, 30, 40]));
2686        assert_eq!(new_mask, RowIdMask::from_block(row_ids(&[10, 20, 30, 40])));
2687    }
2688
2689    #[test]
2690    fn test_row_id_mask_iter_ids() {
2691        // Allow list
2692        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 30]));
2693        let ids: Vec<u64> = mask.iter_ids().unwrap().collect();
2694        assert_eq!(ids, vec![10, 20, 30]);
2695
2696        // Empty allow list
2697        let mask = RowIdMask::allow_nothing();
2698        let iter = mask.iter_ids();
2699        assert!(iter.is_some());
2700        assert_eq!(iter.unwrap().count(), 0);
2701
2702        // Block list
2703        let mask = RowIdMask::from_block(row_ids(&[10, 20, 30]));
2704        assert!(mask.iter_ids().is_none());
2705    }
2706
2707    #[test]
2708    fn test_row_id_mask_default() {
2709        let mask = RowIdMask::default();
2710        // Default should be BlockList with empty set (all rows allowed)
2711        assert_row_id_mask_selects(&mask, &[0, 1, 100, 1000], &[]);
2712        assert_eq!(mask.max_len(), None);
2713    }
2714
2715    #[test]
2716    fn test_row_id_mask_ops() {
2717        let mask = RowIdMask::default();
2718        assert_row_id_mask_selects(&mask, &[1, 5, 100], &[]);
2719
2720        let block_list = mask.also_block(row_ids(&[0, 5, 15]));
2721        assert_row_id_mask_selects(&block_list, &[1, 100], &[5]);
2722
2723        let allow_list = RowIdMask::from_allowed(row_ids(&[0, 2, 5]));
2724        assert_row_id_mask_selects(&allow_list, &[5], &[1, 100]);
2725    }
2726
2727    #[test]
2728    fn test_row_id_mask_combined_ops() {
2729        // Test combining allow and block operations
2730        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 30, 40, 50]));
2731        let mask = mask.also_block(row_ids(&[20, 40]));
2732        assert_row_id_mask_selects(&mask, &[10, 30, 50], &[20, 40]);
2733
2734        let mask = mask.also_allow(row_ids(&[20, 60]));
2735        assert_row_id_mask_selects(&mask, &[10, 20, 30, 50, 60], &[40]);
2736    }
2737
2738    #[test]
2739    fn test_row_id_mask_with_large_values() {
2740        let large_ids = [u64::MAX - 10, u64::MAX - 5, u64::MAX - 1];
2741
2742        // Allow list with large values
2743        let mask = RowIdMask::from_allowed(row_ids(&large_ids));
2744        for &id in &large_ids {
2745            assert!(mask.selected(id));
2746        }
2747        assert!(!mask.selected(u64::MAX));
2748        assert!(!mask.selected(0));
2749
2750        // Block list with large values
2751        let mask = RowIdMask::from_block(row_ids(&large_ids));
2752        for &id in &large_ids {
2753            assert!(!mask.selected(id));
2754        }
2755        assert!(mask.selected(u64::MAX));
2756        assert!(mask.selected(0));
2757    }
2758
2759    proptest::proptest! {
2760        #[test]
2761        fn test_row_id_set_from_sorted_iter_proptest(
2762            mut row_ids in proptest::collection::vec(0..u64::MAX, 0..1000)
2763        ) {
2764            row_ids.sort();
2765            row_ids.dedup();
2766            let num_rows = row_ids.len();
2767            let set = RowIdSet::from_sorted_iter(row_ids.clone()).unwrap();
2768            prop_assert_eq!(set.len(), Some(num_rows as u64));
2769            for id in row_ids {
2770                prop_assert!(set.contains(id));
2771            }
2772        }
2773
2774        #[test]
2775        fn test_row_id_set_union_proptest(
2776            ids1 in proptest::collection::vec(0..u64::MAX, 0..500),
2777            ids2 in proptest::collection::vec(0..u64::MAX, 0..500),
2778        ) {
2779            let set1 = row_ids(&ids1);
2780            let set2 = row_ids(&ids2);
2781
2782            let result = set1.union(&set2);
2783
2784            // All ids from both sets should be in result
2785            for id in ids1.iter().chain(ids2.iter()) {
2786                prop_assert!(result.contains(*id));
2787            }
2788
2789            // Result size should be union size
2790            let expected_size = ids1.iter().chain(ids2.iter()).collect::<std::collections::HashSet<_>>().len();
2791            prop_assert_eq!(result.len(), Some(expected_size as u64));
2792        }
2793
2794        #[test]
2795        fn test_row_id_set_difference_proptest(
2796            ids1 in proptest::collection::vec(0..u64::MAX, 0..500),
2797            ids2 in proptest::collection::vec(0..u64::MAX, 0..500),
2798        ) {
2799            let set1 = row_ids(&ids1);
2800            let set2 = row_ids(&ids2);
2801
2802            let result = set1.difference(&set2);
2803
2804            // Items in ids1 but not in ids2 should be in result
2805            for id in &ids1 {
2806                if !ids2.contains(id) {
2807                    prop_assert!(result.contains(*id));
2808                } else {
2809                    prop_assert!(!result.contains(*id));
2810                }
2811            }
2812        }
2813
2814        #[test]
2815        fn test_row_id_mask_allow_block_proptest(
2816            allow_ids in proptest::collection::vec(0..10000u64, 0..100),
2817            block_ids in proptest::collection::vec(0..10000u64, 0..100),
2818            test_ids in proptest::collection::vec(0..10000u64, 0..50),
2819        ) {
2820            let mask = RowIdMask::from_allowed(row_ids(&allow_ids))
2821                .also_block(row_ids(&block_ids));
2822
2823            for id in test_ids {
2824                let expected = allow_ids.contains(&id) && !block_ids.contains(&id);
2825                prop_assert_eq!(mask.selected(id), expected);
2826            }
2827        }
2828    }
2829}