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    /// Optimize partial fragment selections for compact serialization.
685    ///
686    /// ```
687    /// use lance_select::RowAddrTreeMap;
688    ///
689    /// let mut rows = RowAddrTreeMap::from_iter(0..1_000_000);
690    /// let unoptimized_size = rows.serialized_size();
691    /// rows.optimize();
692    /// assert!(rows.serialized_size() < unoptimized_size);
693    /// ```
694    pub fn optimize(&mut self) {
695        for selection in self.inner.values_mut() {
696            if let RowAddrSelection::Partial(bitmap) = selection {
697                bitmap.optimize();
698            }
699        }
700    }
701
702    /// Compute the serialized size of the set.
703    pub fn serialized_size(&self) -> usize {
704        // Starts at 4 because of the u32 num_entries
705        let mut size = 4;
706        for set in self.inner.values() {
707            // Each entry is 8 bytes for the fragment id and the bitmap size
708            size += 8;
709            if let RowAddrSelection::Partial(set) = set {
710                size += set.serialized_size();
711            }
712        }
713        size
714    }
715
716    /// Serialize the set into the given buffer
717    ///
718    /// The serialization format is stable and used for index serialization
719    ///
720    /// The serialization format is:
721    /// * u32: num_entries
722    ///
723    /// for each entry:
724    ///   * u32: fragment_id
725    ///   * u32: bitmap size
726    ///   * \[u8\]: bitmap
727    ///
728    /// If bitmap size is zero then the entire fragment is selected.
729    pub fn serialize_into<W: Write>(&self, mut writer: W) -> Result<()> {
730        writer.write_u32::<byteorder::LittleEndian>(self.inner.len() as u32)?;
731        for (fragment, set) in &self.inner {
732            writer.write_u32::<byteorder::LittleEndian>(*fragment)?;
733            if let RowAddrSelection::Partial(set) = set {
734                writer.write_u32::<byteorder::LittleEndian>(set.serialized_size() as u32)?;
735                set.serialize_into(&mut writer)?;
736            } else {
737                writer.write_u32::<byteorder::LittleEndian>(0)?;
738            }
739        }
740        Ok(())
741    }
742
743    /// Deserialize the set from the given buffer
744    pub fn deserialize_from<R: Read>(mut reader: R) -> Result<Self> {
745        let num_entries = reader.read_u32::<byteorder::LittleEndian>()?;
746        let mut inner = BTreeMap::new();
747        for _ in 0..num_entries {
748            let fragment = reader.read_u32::<byteorder::LittleEndian>()?;
749            let bitmap_size = reader.read_u32::<byteorder::LittleEndian>()?;
750            if bitmap_size == 0 {
751                inner.insert(fragment, RowAddrSelection::Full);
752            } else {
753                // Grow with the bytes that actually arrive instead of trusting the
754                // declared size. This is reachable from a public byte boundary, so
755                // a 12-byte payload could otherwise declare 4 GiB and abort the
756                // process on the allocation before any read fails.
757                let mut buffer = Vec::new();
758                let read = reader
759                    .by_ref()
760                    .take(u64::from(bitmap_size))
761                    .read_to_end(&mut buffer)?;
762                if read != bitmap_size as usize {
763                    return Err(Error::invalid_input(format!(
764                        "row addr treemap declares a {bitmap_size} byte bitmap for \
765                         fragment {fragment} but only {read} bytes remain"
766                    )));
767                }
768                let set = RoaringBitmap::deserialize_from(&buffer[..])?;
769                inner.insert(fragment, RowAddrSelection::Partial(set));
770            }
771        }
772        Ok(Self { inner })
773    }
774
775    /// Apply a mask to the row addrs
776    ///
777    /// For AllowList: only keep rows that are in the selection and not null
778    /// For BlockList: remove rows that are blocked (not null) and remove nulls
779    pub fn mask(&mut self, mask: &RowAddrMask) {
780        match mask {
781            RowAddrMask::AllowList(allow_list) => {
782                *self &= allow_list;
783            }
784            RowAddrMask::BlockList(block_list) => {
785                *self -= block_list;
786            }
787        }
788    }
789
790    /// Convert the set into an iterator of row addrs
791    ///
792    /// # Panics
793    ///
794    /// Panics if any selection is `Full` because the fragment size is unknown.
795    pub fn into_addr_iter(self) -> impl Iterator<Item = u64> {
796        self.inner
797            .into_iter()
798            .flat_map(|(fragment, selection)| match selection {
799                RowAddrSelection::Full => panic!("Size of full fragment is unknown"),
800                RowAddrSelection::Partial(bitmap) => bitmap.into_iter().map(move |val| {
801                    let fragment = fragment as u64;
802                    let row_offset = val as u64;
803                    (fragment << 32) | row_offset
804                }),
805            })
806    }
807
808    /// Iterate the selected row addresses as `(fragment_id, run)` pairs.
809    ///
810    /// Range-shaped counterpart to [`Self::into_addr_iter`]. A contiguous
811    /// run of N selected rows yields one item, not N. Uses roaring's
812    /// `Iter::next_range`, which walks each underlying container's runs
813    /// rather than its individual bits, so dense ranges cost
814    /// O(num_containers) (roughly num_rows / 65536) instead of O(num_rows).
815    ///
816    /// # Panics
817    ///
818    /// Panics if any selection is `Full` because the fragment size is unknown.
819    pub fn iter_runs(&self) -> impl Iterator<Item = (u32, RangeInclusive<u32>)> + '_ {
820        self.inner
821            .iter()
822            .flat_map(|(&fragment, selection)| match selection {
823                RowAddrSelection::Full => panic!("Size of full fragment is unknown"),
824                RowAddrSelection::Partial(bitmap) => {
825                    let mut iter = bitmap.iter();
826                    std::iter::from_fn(move || iter.next_range().map(|r| (fragment, r)))
827                }
828            })
829    }
830}
831
832impl CacheCodecImpl for RowAddrTreeMap {
833    const TYPE_ID: &'static str = "lance.RowAddrTreeMap";
834    const CURRENT_VERSION: u32 = 1;
835
836    fn serialize(&self, w: &mut CacheEntryWriter<'_>) -> Result<()> {
837        // A roaring bitmap has its own stable, portable serialization; it is
838        // the whole body, so write it raw rather than length-prefixed.
839        self.serialize_into(w.raw_writer())
840    }
841
842    fn deserialize(r: &mut CacheEntryReader<'_>) -> Result<Self> {
843        Self::deserialize_from(r.body().as_ref())
844    }
845}
846
847impl std::ops::BitOr<Self> for RowAddrTreeMap {
848    type Output = Self;
849
850    fn bitor(mut self, rhs: Self) -> Self::Output {
851        self |= rhs;
852        self
853    }
854}
855
856impl std::ops::BitOr<&Self> for RowAddrTreeMap {
857    type Output = Self;
858
859    fn bitor(mut self, rhs: &Self) -> Self::Output {
860        self |= rhs;
861        self
862    }
863}
864
865impl std::ops::BitOrAssign<Self> for RowAddrTreeMap {
866    fn bitor_assign(&mut self, rhs: Self) {
867        *self |= &rhs;
868    }
869}
870
871impl std::ops::BitOrAssign<&Self> for RowAddrTreeMap {
872    fn bitor_assign(&mut self, rhs: &Self) {
873        for (fragment, rhs_set) in &rhs.inner {
874            let lhs_set = self.inner.get_mut(fragment);
875            if let Some(lhs_set) = lhs_set {
876                match lhs_set {
877                    RowAddrSelection::Full => {
878                        // If the fragment is already selected then there is nothing to do
879                    }
880                    RowAddrSelection::Partial(lhs_bitmap) => match rhs_set {
881                        RowAddrSelection::Full => {
882                            *lhs_set = RowAddrSelection::Full;
883                        }
884                        RowAddrSelection::Partial(rhs_set) => {
885                            *lhs_bitmap |= rhs_set;
886                        }
887                    },
888                }
889            } else {
890                self.inner.insert(*fragment, rhs_set.clone());
891            }
892        }
893    }
894}
895
896impl std::ops::BitAnd<Self> for RowAddrTreeMap {
897    type Output = Self;
898
899    fn bitand(mut self, rhs: Self) -> Self::Output {
900        self &= &rhs;
901        self
902    }
903}
904
905impl std::ops::BitAnd<&Self> for RowAddrTreeMap {
906    type Output = Self;
907
908    fn bitand(mut self, rhs: &Self) -> Self::Output {
909        self &= rhs;
910        self
911    }
912}
913
914impl std::ops::BitAndAssign<Self> for RowAddrTreeMap {
915    fn bitand_assign(&mut self, rhs: Self) {
916        *self &= &rhs;
917    }
918}
919
920impl std::ops::BitAndAssign<&Self> for RowAddrTreeMap {
921    fn bitand_assign(&mut self, rhs: &Self) {
922        // Remove fragment that aren't on the RHS
923        self.inner
924            .retain(|fragment, _| rhs.inner.contains_key(fragment));
925
926        // For fragments that are on the RHS, intersect the bitmaps
927        for (fragment, mut lhs_set) in &mut self.inner {
928            match (&mut lhs_set, rhs.inner.get(fragment)) {
929                (_, None) => {} // Already handled by retain
930                (_, Some(RowAddrSelection::Full)) => {
931                    // Everything selected on RHS, so can leave LHS untouched.
932                }
933                (RowAddrSelection::Partial(lhs_set), Some(RowAddrSelection::Partial(rhs_set))) => {
934                    *lhs_set &= rhs_set;
935                }
936                (RowAddrSelection::Full, Some(RowAddrSelection::Partial(rhs_set))) => {
937                    *lhs_set = RowAddrSelection::Partial(rhs_set.clone());
938                }
939            }
940        }
941        // Some bitmaps might now be empty. If they are, we should remove them.
942        self.inner.retain(|_, set| match set {
943            RowAddrSelection::Partial(set) => !set.is_empty(),
944            RowAddrSelection::Full => true,
945        });
946    }
947}
948
949impl std::ops::Sub<Self> for RowAddrTreeMap {
950    type Output = Self;
951
952    fn sub(mut self, rhs: Self) -> Self {
953        self -= &rhs;
954        self
955    }
956}
957
958impl std::ops::Sub<&Self> for RowAddrTreeMap {
959    type Output = Self;
960
961    fn sub(mut self, rhs: &Self) -> Self {
962        self -= rhs;
963        self
964    }
965}
966
967impl std::ops::SubAssign<&Self> for RowAddrTreeMap {
968    fn sub_assign(&mut self, rhs: &Self) {
969        for (fragment, rhs_set) in &rhs.inner {
970            match self.inner.get_mut(fragment) {
971                None => {}
972                Some(RowAddrSelection::Full) => {
973                    // If the fragment is already selected then there is nothing to do
974                    match rhs_set {
975                        RowAddrSelection::Full => {
976                            self.inner.remove(fragment);
977                        }
978                        RowAddrSelection::Partial(rhs_set) => {
979                            // This generally won't be hit.
980                            let mut set = RoaringBitmap::full();
981                            set -= rhs_set;
982                            self.inner.insert(*fragment, RowAddrSelection::Partial(set));
983                        }
984                    }
985                }
986                Some(RowAddrSelection::Partial(lhs_set)) => match rhs_set {
987                    RowAddrSelection::Full => {
988                        self.inner.remove(fragment);
989                    }
990                    RowAddrSelection::Partial(rhs_set) => {
991                        *lhs_set -= rhs_set;
992                        if lhs_set.is_empty() {
993                            self.inner.remove(fragment);
994                        }
995                    }
996                },
997            }
998        }
999    }
1000}
1001
1002impl FromIterator<u64> for RowAddrTreeMap {
1003    fn from_iter<T: IntoIterator<Item = u64>>(iter: T) -> Self {
1004        let mut inner = BTreeMap::new();
1005        for row_addr in iter {
1006            let upper = (row_addr >> 32) as u32;
1007            let lower = row_addr as u32;
1008            match inner.get_mut(&upper) {
1009                None => {
1010                    let mut set = RoaringBitmap::new();
1011                    set.insert(lower);
1012                    inner.insert(upper, RowAddrSelection::Partial(set));
1013                }
1014                Some(RowAddrSelection::Full) => {
1015                    // If the fragment is already selected then there is nothing to do
1016                }
1017                Some(RowAddrSelection::Partial(set)) => {
1018                    set.insert(lower);
1019                }
1020            }
1021        }
1022        Self { inner }
1023    }
1024}
1025
1026impl<'a> FromIterator<&'a u64> for RowAddrTreeMap {
1027    fn from_iter<T: IntoIterator<Item = &'a u64>>(iter: T) -> Self {
1028        Self::from_iter(iter.into_iter().copied())
1029    }
1030}
1031
1032impl From<Range<u64>> for RowAddrTreeMap {
1033    fn from(range: Range<u64>) -> Self {
1034        let mut map = Self::default();
1035        map.insert_range(range);
1036        map
1037    }
1038}
1039
1040impl From<RangeInclusive<u64>> for RowAddrTreeMap {
1041    fn from(range: RangeInclusive<u64>) -> Self {
1042        let mut map = Self::default();
1043        map.insert_range(range);
1044        map
1045    }
1046}
1047
1048impl From<RoaringTreemap> for RowAddrTreeMap {
1049    fn from(roaring: RoaringTreemap) -> Self {
1050        let mut inner = BTreeMap::new();
1051        for (fragment, set) in roaring.bitmaps() {
1052            inner.insert(fragment, RowAddrSelection::Partial(set.clone()));
1053        }
1054        Self { inner }
1055    }
1056}
1057
1058impl Extend<u64> for RowAddrTreeMap {
1059    fn extend<T: IntoIterator<Item = u64>>(&mut self, iter: T) {
1060        for row_addr in iter {
1061            let upper = (row_addr >> 32) as u32;
1062            let lower = row_addr as u32;
1063            match self.inner.get_mut(&upper) {
1064                None => {
1065                    let mut set = RoaringBitmap::new();
1066                    set.insert(lower);
1067                    self.inner.insert(upper, RowAddrSelection::Partial(set));
1068                }
1069                Some(RowAddrSelection::Full) => {
1070                    // If the fragment is already selected then there is nothing to do
1071                }
1072                Some(RowAddrSelection::Partial(set)) => {
1073                    set.insert(lower);
1074                }
1075            }
1076        }
1077    }
1078}
1079
1080impl<'a> Extend<&'a u64> for RowAddrTreeMap {
1081    fn extend<T: IntoIterator<Item = &'a u64>>(&mut self, iter: T) {
1082        self.extend(iter.into_iter().copied())
1083    }
1084}
1085
1086// Extending with RowAddrTreeMap is basically a cumulative set union
1087impl Extend<Self> for RowAddrTreeMap {
1088    fn extend<T: IntoIterator<Item = Self>>(&mut self, iter: T) {
1089        for other in iter {
1090            for (fragment, set) in other.inner {
1091                match self.inner.get_mut(&fragment) {
1092                    None => {
1093                        self.inner.insert(fragment, set);
1094                    }
1095                    Some(RowAddrSelection::Full) => {
1096                        // If the fragment is already selected then there is nothing to do
1097                    }
1098                    Some(RowAddrSelection::Partial(lhs_set)) => match set {
1099                        RowAddrSelection::Full => {
1100                            self.inner.insert(fragment, RowAddrSelection::Full);
1101                        }
1102                        RowAddrSelection::Partial(rhs_set) => {
1103                            *lhs_set |= rhs_set;
1104                        }
1105                    },
1106                }
1107            }
1108        }
1109    }
1110}
1111
1112pub fn bitmap_to_ranges(bitmap: &RoaringBitmap) -> Vec<Range<u64>> {
1113    let mut ranges = Vec::new();
1114    let mut iter = bitmap.iter();
1115    while let Some(r) = iter.next_range() {
1116        ranges.push(*r.start() as u64..(*r.end() as u64 + 1));
1117    }
1118    ranges
1119}
1120
1121pub fn ranges_to_bitmap(ranges: &[Range<u64>], sorted: bool) -> RoaringBitmap {
1122    if ranges.is_empty() {
1123        return RoaringBitmap::new();
1124    }
1125    if sorted {
1126        let sample_size = ranges.len().min(10);
1127        let avg_len: u64 = ranges
1128            .iter()
1129            .take(sample_size)
1130            .map(|r| r.end - r.start)
1131            .sum::<u64>()
1132            / sample_size as u64;
1133        // from_sorted_iter appends each value in O(1) but must visit every u32.
1134        // insert_range bulk-fills containers but does a binary search per call.
1135        // Crossover is ~6: below that, iterating all values is cheaper.
1136        if avg_len <= 6 {
1137            return RoaringBitmap::from_sorted_iter(
1138                ranges.iter().flat_map(|r| r.start as u32..r.end as u32),
1139            )
1140            .unwrap();
1141        }
1142    }
1143    let mut bm = RoaringBitmap::new();
1144    for r in ranges {
1145        bm.insert_range(r.start as u32..r.end as u32);
1146    }
1147    bm
1148}
1149
1150/// A set of stable row ids backed by a 64-bit Roaring bitmap.
1151///
1152/// This is a thin wrapper around [`RoaringTreemap`]. It represents a
1153/// collection of unique row ids and provides the common row-set
1154/// operations defined by [`RowSetOps`].
1155#[derive(Clone, Debug, Default, PartialEq)]
1156pub struct RowIdSet {
1157    inner: RoaringTreemap,
1158}
1159
1160impl RowIdSet {
1161    /// Creates an empty set of row ids.
1162    pub fn new() -> Self {
1163        Self::default()
1164    }
1165    /// Returns an iterator over the contained row ids in ascending order.
1166    pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
1167        self.inner.iter()
1168    }
1169    /// Returns the union of `self` and `other`.
1170    pub fn union(mut self, other: &Self) -> Self {
1171        self.inner |= &other.inner;
1172        self
1173    }
1174    /// Returns the set difference `self \\ other`.
1175    pub fn difference(mut self, other: &Self) -> Self {
1176        self.inner -= &other.inner;
1177        self
1178    }
1179}
1180
1181impl RowSetOps for RowIdSet {
1182    type Row = u64;
1183    fn is_empty(&self) -> bool {
1184        self.inner.is_empty()
1185    }
1186    fn len(&self) -> Option<u64> {
1187        Some(self.inner.len())
1188    }
1189    fn remove(&mut self, row: Self::Row) -> bool {
1190        self.inner.remove(row)
1191    }
1192    fn contains(&self, row: Self::Row) -> bool {
1193        self.inner.contains(row)
1194    }
1195    fn union_all(other: &[&Self]) -> Self {
1196        let mut result = other
1197            .first()
1198            .map_or(Self::default(), |&first| first.clone());
1199        for set in other {
1200            result.inner |= &set.inner;
1201        }
1202        result
1203    }
1204    #[track_caller]
1205    fn from_sorted_iter<I>(iter: I) -> Result<Self>
1206    where
1207        I: IntoIterator<Item = Self::Row>,
1208    {
1209        let mut inner = RoaringTreemap::new();
1210        let mut last: Option<u64> = None;
1211        for value in iter {
1212            if let Some(prev) = last
1213                && value < prev
1214            {
1215                return Err(Error::internal(
1216                    "RowIdSet::from_sorted_iter called with non-sorted input",
1217                ));
1218            }
1219            inner.insert(value);
1220            last = Some(value);
1221        }
1222        Ok(Self { inner })
1223    }
1224}
1225
1226/// A mask over stable row ids based on an allow-list or block-list.
1227///
1228/// The semantics mirror [`RowAddrMask`], but operate on stable
1229/// row ids instead of physical row addresses.
1230#[derive(Clone, Debug, PartialEq)]
1231pub enum RowIdMask {
1232    /// Only the ids in the set are selected.
1233    AllowList(RowIdSet),
1234    /// All ids are selected except those in the set.
1235    BlockList(RowIdSet),
1236}
1237
1238impl Default for RowIdMask {
1239    fn default() -> Self {
1240        // Empty block list means all rows are allowed
1241        Self::BlockList(RowIdSet::default())
1242    }
1243}
1244impl RowIdMask {
1245    /// Create a mask allowing all rows, this is an alias for [`Default`].
1246    pub fn all_rows() -> Self {
1247        Self::default()
1248    }
1249    /// Create a mask that doesn't allow any row id.
1250    pub fn allow_nothing() -> Self {
1251        Self::AllowList(RowIdSet::default())
1252    }
1253    /// Create a mask from an allow list.
1254    pub fn from_allowed(allow_list: RowIdSet) -> Self {
1255        Self::AllowList(allow_list)
1256    }
1257    /// Create a mask from a block list.
1258    pub fn from_block(block_list: RowIdSet) -> Self {
1259        Self::BlockList(block_list)
1260    }
1261    /// True if the row id is selected by the mask, false otherwise.
1262    pub fn selected(&self, row_id: u64) -> bool {
1263        match self {
1264            Self::AllowList(allow_list) => allow_list.contains(row_id),
1265            Self::BlockList(block_list) => !block_list.contains(row_id),
1266        }
1267    }
1268    /// Return the indices of the input row ids that are selected by the mask.
1269    pub fn selected_indices<'a>(&self, row_ids: impl Iterator<Item = &'a u64> + 'a) -> Vec<u64> {
1270        row_ids
1271            .enumerate()
1272            .filter_map(|(idx, row_id)| {
1273                if self.selected(*row_id) {
1274                    Some(idx as u64)
1275                } else {
1276                    None
1277                }
1278            })
1279            .collect()
1280    }
1281    /// Also block the given ids.
1282    ///
1283    /// * `AllowList(a)` -> `AllowList(a \\ block_list)`
1284    /// * `BlockList(b)` -> `BlockList(b union block_list)`
1285    pub fn also_block(self, block_list: RowIdSet) -> Self {
1286        match self {
1287            Self::AllowList(allow_list) => Self::AllowList(allow_list.difference(&block_list)),
1288            Self::BlockList(existing) => Self::BlockList(existing.union(&block_list)),
1289        }
1290    }
1291    /// Also allow the given ids.
1292    ///
1293    /// * `AllowList(a)` -> `AllowList(a union allow_list)`
1294    /// * `BlockList(b)` -> `BlockList(b \\ allow_list)`
1295    pub fn also_allow(self, allow_list: RowIdSet) -> Self {
1296        match self {
1297            Self::AllowList(existing) => Self::AllowList(existing.union(&allow_list)),
1298            Self::BlockList(block_list) => Self::BlockList(block_list.difference(&allow_list)),
1299        }
1300    }
1301    /// Return the maximum number of row ids that could be selected by this mask.
1302    ///
1303    /// Will be `None` if this is a `BlockList` (unbounded).
1304    pub fn max_len(&self) -> Option<u64> {
1305        match self {
1306            Self::AllowList(selection) => selection.len(),
1307            Self::BlockList(_) => None,
1308        }
1309    }
1310    /// Iterate over the row ids that are selected by the mask.
1311    ///
1312    /// This is only possible if this is an `AllowList`. For a `BlockList`
1313    /// the domain of possible row ids is unbounded.
1314    pub fn iter_ids(&self) -> Option<Box<dyn Iterator<Item = u64> + '_>> {
1315        match self {
1316            Self::AllowList(allow_list) => Some(Box::new(allow_list.iter())),
1317            Self::BlockList(_) => None,
1318        }
1319    }
1320}
1321
1322#[cfg(test)]
1323mod tests {
1324    use super::*;
1325    use proptest::{prop_assert, prop_assert_eq};
1326
1327    fn rows(ids: &[u64]) -> RowAddrTreeMap {
1328        RowAddrTreeMap::from_iter(ids)
1329    }
1330
1331    fn assert_mask_selects(mask: &RowAddrMask, selected: &[u64], not_selected: &[u64]) {
1332        for &id in selected {
1333            assert!(mask.selected(id), "Expected row {} to be selected", id);
1334        }
1335        for &id in not_selected {
1336            assert!(!mask.selected(id), "Expected row {} to NOT be selected", id);
1337        }
1338    }
1339
1340    fn selected_in_range(mask: &RowAddrMask, range: std::ops::Range<u64>) -> Vec<u64> {
1341        range.filter(|val| mask.selected(*val)).collect()
1342    }
1343
1344    #[test]
1345    fn test_from_sorted_runs() {
1346        let addr = |frag: u64, off: u64| frag << 32 | off;
1347
1348        // No runs, and runs that are all empty, give an empty set.
1349        assert_eq!(RowAddrTreeMap::from_sorted_runs([]), RowAddrTreeMap::new());
1350        assert_eq!(
1351            RowAddrTreeMap::from_sorted_runs([&[][..], &[][..]]),
1352            RowAddrTreeMap::new()
1353        );
1354
1355        // A single run is taken as-is (no sort needed) and spans fragments.
1356        let single = [addr(0, 3), addr(0, 9), addr(2, 1), addr(7, 0)];
1357        assert_eq!(
1358            RowAddrTreeMap::from_sorted_runs([&single[..]]),
1359            rows(&single)
1360        );
1361
1362        // Interleaved runs whose fragments overlap: every bucket must be
1363        // re-sorted and the union must match the naive construction.
1364        let run_a = [addr(0, 5), addr(1, 2), addr(1, 8), addr(3, 4)];
1365        let run_b = [addr(0, 1), addr(0, 6), addr(1, 3), addr(2, 0)];
1366        let run_c = [addr(1, 0), addr(3, 4), addr(3, 5)]; // repeats addr(3, 4)
1367        let expected: Vec<u64> = run_a
1368            .iter()
1369            .chain(run_b.iter())
1370            .chain(run_c.iter())
1371            .copied()
1372            .collect();
1373        let actual = RowAddrTreeMap::from_sorted_runs([&run_a[..], &run_b[..], &run_c[..]]);
1374        assert_eq!(actual, rows(&expected));
1375        assert_eq!(actual.len(), Some(10));
1376
1377        // Runs that touch disjoint fragment ranges never need a sort, and a
1378        // later run that continues a bucket in order keeps it sorted too.
1379        let lo = [addr(0, 0), addr(0, 1)];
1380        let hi = [addr(0, 2), addr(5, 0)];
1381        assert_eq!(
1382            RowAddrTreeMap::from_sorted_runs([&lo[..], &hi[..]]),
1383            rows(&[addr(0, 0), addr(0, 1), addr(0, 2), addr(5, 0)])
1384        );
1385
1386        // Bare stable row ids (no fragment bits) all land in bucket 0.
1387        let ids_a = [1_u64, 4, 9];
1388        let ids_b = [2_u64, 4, 10];
1389        assert_eq!(
1390            RowAddrTreeMap::from_sorted_runs([&ids_a[..], &ids_b[..]]),
1391            rows(&[1, 2, 4, 9, 10])
1392        );
1393    }
1394
1395    #[test]
1396    fn test_row_addr_mask_construction() {
1397        let full_mask = RowAddrMask::all_rows();
1398        assert_eq!(full_mask.max_len(), None);
1399        assert_mask_selects(&full_mask, &[0, 1, 4 << 32 | 3], &[]);
1400        assert_eq!(full_mask.allow_list(), None);
1401        assert_eq!(full_mask.block_list(), Some(&RowAddrTreeMap::default()));
1402        assert!(full_mask.iter_addrs().is_none());
1403
1404        let empty_mask = RowAddrMask::allow_nothing();
1405        assert_eq!(empty_mask.max_len(), Some(0));
1406        assert_mask_selects(&empty_mask, &[], &[0, 1, 4 << 32 | 3]);
1407        assert_eq!(empty_mask.allow_list(), Some(&RowAddrTreeMap::default()));
1408        assert_eq!(empty_mask.block_list(), None);
1409        let iter = empty_mask.iter_addrs();
1410        assert!(iter.is_some());
1411        assert_eq!(iter.unwrap().count(), 0);
1412
1413        let allow_list = RowAddrMask::from_allowed(rows(&[10, 20, 30]));
1414        assert_eq!(allow_list.max_len(), Some(3));
1415        assert_mask_selects(&allow_list, &[10, 20, 30], &[0, 15, 25, 40]);
1416        assert_eq!(allow_list.allow_list(), Some(&rows(&[10, 20, 30])));
1417        assert_eq!(allow_list.block_list(), None);
1418        let iter = allow_list.iter_addrs();
1419        assert!(iter.is_some());
1420        let ids: Vec<u64> = iter.unwrap().map(|addr| addr.into()).collect();
1421        assert_eq!(ids, vec![10, 20, 30]);
1422
1423        let mut full_frag = RowAddrTreeMap::default();
1424        full_frag.insert_fragment(2);
1425        let allow_list = RowAddrMask::from_allowed(full_frag);
1426        assert_eq!(allow_list.max_len(), None);
1427        assert_mask_selects(&allow_list, &[(2 << 32) + 5], &[(3 << 32) + 5]);
1428        assert!(allow_list.iter_addrs().is_none());
1429    }
1430
1431    #[test]
1432    fn test_row_addr_mask_selects_all_known_rows() {
1433        let partition_rows = rows(&[10, 20, 2_u64 << 32 | 3]);
1434
1435        assert!(RowAddrMask::all_rows().selects_all(&partition_rows));
1436        assert!(
1437            RowAddrMask::from_allowed(rows(&[10, 20, 30, 2_u64 << 32 | 3]))
1438                .selects_all(&partition_rows)
1439        );
1440        assert!(
1441            !RowAddrMask::from_allowed(rows(&[10, 2_u64 << 32 | 3])).selects_all(&partition_rows)
1442        );
1443        assert!(RowAddrMask::from_block(rows(&[30])).selects_all(&partition_rows));
1444        assert!(!RowAddrMask::from_block(rows(&[20, 30])).selects_all(&partition_rows));
1445        assert!(RowAddrMask::allow_nothing().selects_all(&RowAddrTreeMap::new()));
1446    }
1447
1448    #[test]
1449    fn test_selected_indices() {
1450        // Allow list
1451        let mask = RowAddrMask::from_allowed(rows(&[10, 20, 40]));
1452        assert!(mask.selected_indices(std::iter::empty()).is_empty());
1453        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[1, 3]);
1454
1455        // Block list
1456        let mask = RowAddrMask::from_block(rows(&[10, 20, 40]));
1457        assert!(mask.selected_indices(std::iter::empty()).is_empty());
1458        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[0, 2]);
1459    }
1460
1461    #[test]
1462    fn test_also_allow() {
1463        // Allow list
1464        let mask = RowAddrMask::from_allowed(rows(&[10, 20]));
1465        let new_mask = mask.also_allow(rows(&[20, 30, 40]));
1466        assert_eq!(new_mask, RowAddrMask::from_allowed(rows(&[10, 20, 30, 40])));
1467
1468        // Block list
1469        let mask = RowAddrMask::from_block(rows(&[10, 20, 30]));
1470        let new_mask = mask.also_allow(rows(&[20, 40]));
1471        assert_eq!(new_mask, RowAddrMask::from_block(rows(&[10, 30])));
1472    }
1473
1474    #[test]
1475    fn test_also_block() {
1476        // Allow list
1477        let mask = RowAddrMask::from_allowed(rows(&[10, 20, 30]));
1478        let new_mask = mask.also_block(rows(&[20, 40]));
1479        assert_eq!(new_mask, RowAddrMask::from_allowed(rows(&[10, 30])));
1480
1481        // Block list
1482        let mask = RowAddrMask::from_block(rows(&[10, 20]));
1483        let new_mask = mask.also_block(rows(&[20, 30, 40]));
1484        assert_eq!(new_mask, RowAddrMask::from_block(rows(&[10, 20, 30, 40])));
1485    }
1486
1487    #[test]
1488    fn test_iter_ids() {
1489        // Allow list
1490        let mask = RowAddrMask::from_allowed(rows(&[10, 20, 30]));
1491        let expected: Vec<_> = [10, 20, 30].into_iter().map(RowAddress::from).collect();
1492        assert_eq!(mask.iter_addrs().unwrap().collect::<Vec<_>>(), expected);
1493
1494        // Allow list with full fragment
1495        let mut inner = RowAddrTreeMap::default();
1496        inner.insert_fragment(10);
1497        let mask = RowAddrMask::from_allowed(inner);
1498        assert!(mask.iter_addrs().is_none());
1499
1500        // Block list
1501        let mask = RowAddrMask::from_block(rows(&[10, 20, 30]));
1502        assert!(mask.iter_addrs().is_none());
1503    }
1504
1505    #[test]
1506    fn test_row_addr_mask_intersect() {
1507        let a = rows(&[1, 2, 3]);
1508        let b = rows(&[3, 4]);
1509
1510        // allow & allow -> only rows in both
1511        assert_mask_selects(
1512            &RowAddrMask::from_allowed(a.clone()).intersect(RowAddrMask::from_allowed(b.clone())),
1513            &[3],
1514            &[1, 2, 4, 100],
1515        );
1516        // allow & block -> allowed minus blocked
1517        assert_mask_selects(
1518            &RowAddrMask::from_allowed(a.clone()).intersect(RowAddrMask::from_block(b.clone())),
1519            &[1, 2],
1520            &[3, 4, 100],
1521        );
1522        // block & allow -> same, order independent
1523        assert_mask_selects(
1524            &RowAddrMask::from_block(b.clone()).intersect(RowAddrMask::from_allowed(a.clone())),
1525            &[1, 2],
1526            &[3, 4, 100],
1527        );
1528        // block & block -> both exclusions apply
1529        assert_mask_selects(
1530            &RowAddrMask::from_block(a.clone()).intersect(RowAddrMask::from_block(b)),
1531            &[100],
1532            &[1, 2, 3, 4],
1533        );
1534        // all_rows is the identity, and intersecting with itself changes nothing
1535        let allow_a = RowAddrMask::from_allowed(a.clone());
1536        assert_eq!(allow_a.clone().intersect(RowAddrMask::all_rows()), allow_a);
1537        assert_eq!(allow_a.clone().intersect(allow_a.clone()), allow_a);
1538        // allow_nothing absorbs
1539        assert_mask_selects(
1540            &RowAddrMask::allow_nothing().intersect(RowAddrMask::from_allowed(a)),
1541            &[],
1542            &[1, 2, 3, 100],
1543        );
1544    }
1545
1546    #[test]
1547    fn test_row_addr_mask_from_serialized_parts() {
1548        fn ser(tm: &RowAddrTreeMap) -> Vec<u8> {
1549            let mut buf = Vec::new();
1550            tm.serialize_into(&mut buf).unwrap();
1551            buf
1552        }
1553        let allow = ser(&rows(&[1, 2, 3]));
1554        let block = ser(&rows(&[3, 4]));
1555
1556        // Neither part means "no mask", which is not the same as "select nothing".
1557        assert!(
1558            RowAddrMask::from_serialized_parts(None, None)
1559                .unwrap()
1560                .is_none()
1561        );
1562
1563        let m = RowAddrMask::from_serialized_parts(Some(&allow), None)
1564            .unwrap()
1565            .unwrap();
1566        assert_mask_selects(&m, &[1, 2, 3], &[4, 100]);
1567
1568        let m = RowAddrMask::from_serialized_parts(None, Some(&block))
1569            .unwrap()
1570            .unwrap();
1571        assert_mask_selects(&m, &[1, 2, 100], &[3, 4]);
1572
1573        // Block wins on the overlap.
1574        let m = RowAddrMask::from_serialized_parts(Some(&allow), Some(&block))
1575            .unwrap()
1576            .unwrap();
1577        assert_mask_selects(&m, &[1, 2], &[3, 4, 100]);
1578
1579        // Round trips through the same encoding the caller used.
1580        let again = RowAddrMask::from_serialized_parts(Some(&ser(m.allow_list().unwrap())), None)
1581            .unwrap()
1582            .unwrap();
1583        assert_mask_selects(&again, &[1, 2], &[3, 4]);
1584
1585        assert!(RowAddrMask::from_serialized_parts(Some(b"not a treemap"), None).is_err());
1586
1587        // A declared bitmap size must not be allocated before the bytes are
1588        // known to exist: this 12-byte payload claims ~4 GiB.
1589        let bomb = [
1590            1u8, 0, 0, 0, // one entry
1591            0, 0, 0, 0, // fragment zero
1592            0xff, 0xff, 0xff, 0xff, // declared bitmap size
1593        ];
1594        let err = RowAddrMask::from_serialized_parts(Some(&bomb), None).unwrap_err();
1595        assert!(
1596            err.to_string().contains("only 0 bytes remain"),
1597            "expected a length complaint, got: {err}"
1598        );
1599    }
1600
1601    #[test]
1602    fn test_row_addr_tree_map_optimize_compacts_dense_ranges() {
1603        let mut rows = RowAddrTreeMap::from_iter(0..1_000_000);
1604        let unoptimized_size = rows.serialized_size();
1605
1606        rows.optimize();
1607
1608        assert!(rows.serialized_size() < unoptimized_size);
1609        let mut serialized = Vec::with_capacity(rows.serialized_size());
1610        rows.serialize_into(&mut serialized).unwrap();
1611        assert_eq!(
1612            RowAddrTreeMap::deserialize_from(serialized.as_slice()).unwrap(),
1613            rows
1614        );
1615    }
1616
1617    #[test]
1618    fn test_row_addr_mask_not() {
1619        let allow_list = RowAddrMask::from_allowed(rows(&[1, 2, 3]));
1620        let block_list = !allow_list.clone();
1621        assert_eq!(block_list, RowAddrMask::from_block(rows(&[1, 2, 3])));
1622        // Can roundtrip by negating again
1623        assert_eq!(!block_list, allow_list);
1624    }
1625
1626    #[test]
1627    fn test_ops() {
1628        let mask = RowAddrMask::default();
1629        assert_mask_selects(&mask, &[1, 5], &[]);
1630
1631        let block_list = mask.also_block(rows(&[0, 5, 15]));
1632        assert_mask_selects(&block_list, &[1], &[5]);
1633
1634        let allow_list = RowAddrMask::from_allowed(rows(&[0, 2, 5]));
1635        assert_mask_selects(&allow_list, &[5], &[1]);
1636
1637        let combined = block_list & allow_list;
1638        assert_mask_selects(&combined, &[2], &[0, 5]);
1639
1640        let other = RowAddrMask::from_allowed(rows(&[3]));
1641        let combined = combined | other;
1642        assert_mask_selects(&combined, &[2, 3], &[0, 5]);
1643
1644        let block_list = RowAddrMask::from_block(rows(&[0]));
1645        let allow_list = RowAddrMask::from_allowed(rows(&[3]));
1646
1647        let combined = block_list | allow_list;
1648        assert_mask_selects(&combined, &[1], &[]);
1649    }
1650
1651    #[test]
1652    fn test_logical_and() {
1653        let allow1 = RowAddrMask::from_allowed(rows(&[0, 1]));
1654        let block1 = RowAddrMask::from_block(rows(&[1, 2]));
1655        let allow2 = RowAddrMask::from_allowed(rows(&[1, 2, 3, 4]));
1656        let block2 = RowAddrMask::from_block(rows(&[3, 4]));
1657
1658        fn check(lhs: &RowAddrMask, rhs: &RowAddrMask, expected: &[u64]) {
1659            for mask in [lhs.clone() & rhs.clone(), rhs.clone() & lhs.clone()] {
1660                assert_eq!(selected_in_range(&mask, 0..10), expected);
1661            }
1662        }
1663
1664        // Allow & Allow
1665        check(&allow1, &allow1, &[0, 1]);
1666        check(&allow1, &allow2, &[1]);
1667
1668        // Block & Block
1669        check(&block1, &block1, &[0, 3, 4, 5, 6, 7, 8, 9]);
1670        check(&block1, &block2, &[0, 5, 6, 7, 8, 9]);
1671
1672        // Allow & Block
1673        check(&allow1, &block1, &[0]);
1674        check(&allow1, &block2, &[0, 1]);
1675        check(&allow2, &block1, &[3, 4]);
1676        check(&allow2, &block2, &[1, 2]);
1677    }
1678
1679    #[test]
1680    fn test_logical_or() {
1681        let allow1 = RowAddrMask::from_allowed(rows(&[5, 6, 7, 8, 9]));
1682        let block1 = RowAddrMask::from_block(rows(&[5, 6]));
1683        let mixed1 = allow1.clone().also_block(rows(&[5, 6]));
1684        let allow2 = RowAddrMask::from_allowed(rows(&[2, 3, 4, 5, 6, 7, 8]));
1685        let block2 = RowAddrMask::from_block(rows(&[4, 5]));
1686        let mixed2 = allow2.clone().also_block(rows(&[4, 5]));
1687
1688        fn check(lhs: &RowAddrMask, rhs: &RowAddrMask, expected: &[u64]) {
1689            for mask in [lhs.clone() | rhs.clone(), rhs.clone() | lhs.clone()] {
1690                assert_eq!(selected_in_range(&mask, 0..10), expected);
1691            }
1692        }
1693
1694        check(&allow1, &allow1, &[5, 6, 7, 8, 9]);
1695        check(&block1, &block1, &[0, 1, 2, 3, 4, 7, 8, 9]);
1696        check(&mixed1, &mixed1, &[7, 8, 9]);
1697        check(&allow2, &allow2, &[2, 3, 4, 5, 6, 7, 8]);
1698        check(&block2, &block2, &[0, 1, 2, 3, 6, 7, 8, 9]);
1699        check(&mixed2, &mixed2, &[2, 3, 6, 7, 8]);
1700
1701        check(&allow1, &block1, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1702        check(&allow1, &mixed1, &[5, 6, 7, 8, 9]);
1703        check(&allow1, &allow2, &[2, 3, 4, 5, 6, 7, 8, 9]);
1704        check(&allow1, &block2, &[0, 1, 2, 3, 5, 6, 7, 8, 9]);
1705        check(&allow1, &mixed2, &[2, 3, 5, 6, 7, 8, 9]);
1706        check(&block1, &mixed1, &[0, 1, 2, 3, 4, 7, 8, 9]);
1707        check(&block1, &allow2, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1708        check(&block1, &block2, &[0, 1, 2, 3, 4, 6, 7, 8, 9]);
1709        check(&block1, &mixed2, &[0, 1, 2, 3, 4, 6, 7, 8, 9]);
1710        check(&mixed1, &allow2, &[2, 3, 4, 5, 6, 7, 8, 9]);
1711        check(&mixed1, &block2, &[0, 1, 2, 3, 6, 7, 8, 9]);
1712        check(&mixed1, &mixed2, &[2, 3, 6, 7, 8, 9]);
1713        check(&allow2, &block2, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1714        check(&allow2, &mixed2, &[2, 3, 4, 5, 6, 7, 8]);
1715        check(&block2, &mixed2, &[0, 1, 2, 3, 6, 7, 8, 9]);
1716    }
1717
1718    #[test]
1719    fn test_deserialize_legacy_format() {
1720        // Test that we can deserialize the old format where both allow_list
1721        // and block_list could be present in the serialized form.
1722        //
1723        // The old format (before this PR) used a struct with both allow_list and block_list
1724        // fields. The new format uses an enum. The deserialization code should handle
1725        // the case where both lists are present by converting to AllowList(allow - block).
1726
1727        // Create the RowIdTreeMaps and serialize them directly
1728        let allow = rows(&[1, 2, 3, 4, 5, 10, 15]);
1729        let block = rows(&[2, 4, 15]);
1730
1731        // Serialize using the stable RowIdTreeMap serialization format
1732        let block_bytes = {
1733            let mut buf = Vec::with_capacity(block.serialized_size());
1734            block.serialize_into(&mut buf).unwrap();
1735            buf
1736        };
1737        let allow_bytes = {
1738            let mut buf = Vec::with_capacity(allow.serialized_size());
1739            allow.serialize_into(&mut buf).unwrap();
1740            buf
1741        };
1742
1743        // Construct a binary array with both values present (simulating old format)
1744        let old_format_array =
1745            BinaryArray::from_opt_vec(vec![Some(&block_bytes), Some(&allow_bytes)]);
1746
1747        // Deserialize - should handle this by creating AllowList(allow - block)
1748        let deserialized = RowAddrMask::from_arrow(&old_format_array).unwrap();
1749
1750        // The expected result: AllowList([1, 2, 3, 4, 5, 10, 15] - [2, 4, 15]) = [1, 3, 5, 10]
1751        assert_mask_selects(&deserialized, &[1, 3, 5, 10], &[2, 4, 15]);
1752        assert!(
1753            deserialized.allow_list().is_some(),
1754            "Should deserialize to AllowList variant"
1755        );
1756    }
1757
1758    #[test]
1759    fn test_roundtrip_arrow() {
1760        let row_addrs = rows(&[1, 2, 3, 100, 2000]);
1761
1762        // Allow list
1763        let original = RowAddrMask::from_allowed(row_addrs.clone());
1764        let array = original.into_arrow().unwrap();
1765        assert_eq!(RowAddrMask::from_arrow(&array).unwrap(), original);
1766
1767        // Block list
1768        let original = RowAddrMask::from_block(row_addrs);
1769        let array = original.into_arrow().unwrap();
1770        assert_eq!(RowAddrMask::from_arrow(&array).unwrap(), original);
1771    }
1772
1773    #[test]
1774    fn test_deserialize_legacy_empty_lists() {
1775        // Case 1: Both None (should become all_rows)
1776        let array = BinaryArray::from_opt_vec(vec![None, None]);
1777        let mask = RowAddrMask::from_arrow(&array).unwrap();
1778        assert_mask_selects(&mask, &[0, 100, u64::MAX], &[]);
1779
1780        // Case 2: Only block list (no allow list)
1781        let block = rows(&[5, 10]);
1782        let block_bytes = {
1783            let mut buf = Vec::with_capacity(block.serialized_size());
1784            block.serialize_into(&mut buf).unwrap();
1785            buf
1786        };
1787        let array = BinaryArray::from_opt_vec(vec![Some(&block_bytes[..]), None]);
1788        let mask = RowAddrMask::from_arrow(&array).unwrap();
1789        assert_mask_selects(&mask, &[0, 15], &[5, 10]);
1790
1791        // Case 3: Only allow list (no block list)
1792        let allow = rows(&[5, 10]);
1793        let allow_bytes = {
1794            let mut buf = Vec::with_capacity(allow.serialized_size());
1795            allow.serialize_into(&mut buf).unwrap();
1796            buf
1797        };
1798        let array = BinaryArray::from_opt_vec(vec![None, Some(&allow_bytes[..])]);
1799        let mask = RowAddrMask::from_arrow(&array).unwrap();
1800        assert_mask_selects(&mask, &[5, 10], &[0, 15]);
1801    }
1802
1803    #[test]
1804    fn test_map_insert() {
1805        let mut map = RowAddrTreeMap::default();
1806
1807        assert!(!map.contains(20));
1808        assert!(map.insert(20));
1809        assert!(map.contains(20));
1810        assert!(!map.insert(20)); // Inserting again should be no-op
1811
1812        let bitmap = map.get_fragment_bitmap(0);
1813        assert!(bitmap.is_some());
1814        let bitmap = bitmap.unwrap();
1815        assert_eq!(bitmap.len(), 1);
1816
1817        assert!(map.get_fragment_bitmap(1).is_none());
1818
1819        map.insert_fragment(0);
1820        assert!(map.contains(0));
1821        assert!(!map.insert(0)); // Inserting into full fragment should be no-op
1822        assert!(map.get_fragment_bitmap(0).is_none());
1823    }
1824
1825    #[test]
1826    fn test_map_insert_range() {
1827        let ranges = &[
1828            (0..10),
1829            (40..500),
1830            ((u32::MAX as u64 - 10)..(u32::MAX as u64 + 20)),
1831        ];
1832
1833        for range in ranges {
1834            let mut mask = RowAddrTreeMap::default();
1835
1836            let count = mask.insert_range(range.clone());
1837            let expected = range.end - range.start;
1838            assert_eq!(count, expected);
1839
1840            let count = mask.insert_range(range.clone());
1841            assert_eq!(count, 0);
1842
1843            let new_range = range.start + 5..range.end + 5;
1844            let count = mask.insert_range(new_range.clone());
1845            assert_eq!(count, 5);
1846        }
1847
1848        let mut mask = RowAddrTreeMap::default();
1849        let count = mask.insert_range(..10);
1850        assert_eq!(count, 10);
1851        assert!(mask.contains(0));
1852
1853        let count = mask.insert_range(20..=24);
1854        assert_eq!(count, 5);
1855
1856        mask.insert_fragment(0);
1857        let count = mask.insert_range(100..200);
1858        assert_eq!(count, 0);
1859    }
1860
1861    #[test]
1862    fn test_map_remove() {
1863        let mut mask = RowAddrTreeMap::default();
1864
1865        assert!(!mask.remove(20));
1866
1867        mask.insert(20);
1868        assert!(mask.contains(20));
1869        assert!(mask.remove(20));
1870        assert!(!mask.contains(20));
1871
1872        mask.insert_range(10..=20);
1873        assert!(mask.contains(15));
1874        assert!(mask.remove(15));
1875        assert!(!mask.contains(15));
1876
1877        // We don't test removing from a full fragment, because that would take
1878        // a lot of memory.
1879    }
1880
1881    #[test]
1882    fn test_map_mask() {
1883        let mask = rows(&[0, 1, 2]);
1884        let mask2 = rows(&[0, 2, 3]);
1885
1886        let allow_list = RowAddrMask::AllowList(mask2.clone());
1887        let mut actual = mask.clone();
1888        actual.mask(&allow_list);
1889        assert_eq!(actual, rows(&[0, 2]));
1890
1891        let block_list = RowAddrMask::BlockList(mask2);
1892        let mut actual = mask;
1893        actual.mask(&block_list);
1894        assert_eq!(actual, rows(&[1]));
1895    }
1896
1897    #[test]
1898    #[should_panic(expected = "Size of full fragment is unknown")]
1899    fn test_map_insert_full_fragment_row() {
1900        let mut mask = RowAddrTreeMap::default();
1901        mask.insert_fragment(0);
1902
1903        let _ = mask.into_addr_iter().collect::<Vec<u64>>();
1904    }
1905
1906    #[test]
1907    fn test_map_into_addr_iter() {
1908        let mut mask = RowAddrTreeMap::default();
1909        mask.insert(0);
1910        mask.insert(1);
1911        mask.insert(1 << 32 | 5);
1912        mask.insert(2 << 32 | 10);
1913
1914        let expected = vec![0u64, 1, 1 << 32 | 5, 2 << 32 | 10];
1915        let actual: Vec<u64> = mask.into_addr_iter().collect();
1916        assert_eq!(actual, expected);
1917    }
1918
1919    #[test]
1920    fn test_map_iter_runs() {
1921        // Three contiguous regions across two fragments.
1922        let mut mask = RowAddrTreeMap::default();
1923        mask.insert_range(0..3);
1924        mask.insert_range(10..15);
1925        mask.insert_range((1u64 << 32) + 100..(1u64 << 32) + 103);
1926
1927        let runs: Vec<(u32, RangeInclusive<u32>)> = mask.iter_runs().collect();
1928        assert_eq!(runs, vec![(0, 0..=2), (0, 10..=14), (1, 100..=102)]);
1929    }
1930
1931    #[test]
1932    fn test_map_iter_runs_matches_into_addr_iter() {
1933        // Confirm iter_runs and into_addr_iter agree on a non-trivial shape.
1934        let mut mask = RowAddrTreeMap::default();
1935        mask.insert_range(5..7);
1936        mask.insert_range(11..12);
1937        mask.insert_range(20..25);
1938        mask.insert_range((1u64 << 32)..(1u64 << 32) + 3);
1939
1940        let from_runs: Vec<u64> = mask
1941            .iter_runs()
1942            .flat_map(|(frag, run)| {
1943                let frag = u64::from(frag);
1944                (*run.start()..=*run.end()).map(move |v| (frag << 32) | u64::from(v))
1945            })
1946            .collect();
1947        let from_bits: Vec<u64> = mask.clone().into_addr_iter().collect();
1948        assert_eq!(from_runs, from_bits);
1949    }
1950
1951    #[test]
1952    fn test_map_from() {
1953        let map = RowAddrTreeMap::from(10..12);
1954        assert!(map.contains(10));
1955        assert!(map.contains(11));
1956        assert!(!map.contains(12));
1957        assert!(!map.contains(3));
1958
1959        let map = RowAddrTreeMap::from(10..=12);
1960        assert!(map.contains(10));
1961        assert!(map.contains(11));
1962        assert!(map.contains(12));
1963        assert!(!map.contains(3));
1964    }
1965
1966    #[test]
1967    fn test_map_from_roaring() {
1968        let bitmap = RoaringTreemap::from_iter(&[0, 1, 1 << 32]);
1969        let map = RowAddrTreeMap::from(bitmap);
1970        assert!(map.contains(0) && map.contains(1) && map.contains(1 << 32));
1971        assert!(!map.contains(2));
1972    }
1973
1974    #[test]
1975    fn test_map_extend() {
1976        let mut map = RowAddrTreeMap::default();
1977        map.insert(0);
1978        map.insert_fragment(1);
1979
1980        let other_rows = [0, 2, 1 << 32 | 10, 3 << 32 | 5];
1981        map.extend(other_rows.iter().copied());
1982
1983        assert!(map.contains(0));
1984        assert!(map.contains(2));
1985        assert!(map.contains(1 << 32 | 5));
1986        assert!(map.contains(1 << 32 | 10));
1987        assert!(map.contains(3 << 32 | 5));
1988        assert!(!map.contains(3));
1989    }
1990
1991    #[test]
1992    fn test_map_extend_other_maps() {
1993        let mut map = RowAddrTreeMap::default();
1994        map.insert(0);
1995        map.insert_fragment(1);
1996        map.insert(4 << 32);
1997
1998        let mut other_map = rows(&[0, 2, 1 << 32 | 10, 3 << 32 | 5]);
1999        other_map.insert_fragment(4);
2000        map.extend(std::iter::once(other_map));
2001
2002        for id in [
2003            0,
2004            2,
2005            1 << 32 | 5,
2006            1 << 32 | 10,
2007            3 << 32 | 5,
2008            4 << 32,
2009            4 << 32 | 7,
2010        ] {
2011            assert!(map.contains(id), "Expected {} to be contained", id);
2012        }
2013        assert!(!map.contains(3));
2014    }
2015
2016    proptest::proptest! {
2017        #[test]
2018        fn test_map_serialization_roundtrip(
2019            values in proptest::collection::vec(
2020                (0..u32::MAX, proptest::option::of(proptest::collection::vec(0..u32::MAX, 0..1000))),
2021                0..10
2022            )
2023        ) {
2024            let mut mask = RowAddrTreeMap::default();
2025            for (fragment, rows) in values {
2026                if let Some(rows) = rows {
2027                    let bitmap = RoaringBitmap::from_iter(rows);
2028                    mask.insert_bitmap(fragment, bitmap);
2029                } else {
2030                    mask.insert_fragment(fragment);
2031                }
2032            }
2033
2034            let mut data = Vec::new();
2035            mask.serialize_into(&mut data).unwrap();
2036            let deserialized = RowAddrTreeMap::deserialize_from(data.as_slice()).unwrap();
2037            prop_assert_eq!(mask, deserialized);
2038        }
2039
2040        #[test]
2041        fn test_map_intersect(
2042            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2043            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2044            right_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2045            right_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2046        ) {
2047            let mut left = RowAddrTreeMap::default();
2048            for fragment in left_full_fragments.clone() {
2049                left.insert_fragment(fragment);
2050            }
2051            left.extend(left_rows.iter().copied());
2052
2053            let mut right = RowAddrTreeMap::default();
2054            for fragment in right_full_fragments.clone() {
2055                right.insert_fragment(fragment);
2056            }
2057            right.extend(right_rows.iter().copied());
2058
2059            let mut expected = RowAddrTreeMap::default();
2060            for fragment in &left_full_fragments {
2061                if right_full_fragments.contains(fragment) {
2062                    expected.insert_fragment(*fragment);
2063                }
2064            }
2065
2066            let left_in_right = left_rows.iter().filter(|row| {
2067                right_rows.contains(row)
2068                    || right_full_fragments.contains(&((*row >> 32) as u32))
2069            });
2070            expected.extend(left_in_right);
2071            let right_in_left = right_rows.iter().filter(|row| {
2072                left_rows.contains(row)
2073                    || left_full_fragments.contains(&((*row >> 32) as u32))
2074            });
2075            expected.extend(right_in_left);
2076
2077            let actual = left & right;
2078            prop_assert_eq!(expected, actual);
2079        }
2080
2081        #[test]
2082        fn test_map_union(
2083            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2084            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2085            right_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2086            right_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2087        ) {
2088            let mut left = RowAddrTreeMap::default();
2089            for fragment in left_full_fragments.clone() {
2090                left.insert_fragment(fragment);
2091            }
2092            left.extend(left_rows.iter().copied());
2093
2094            let mut right = RowAddrTreeMap::default();
2095            for fragment in right_full_fragments.clone() {
2096                right.insert_fragment(fragment);
2097            }
2098            right.extend(right_rows.iter().copied());
2099
2100            let mut expected = RowAddrTreeMap::default();
2101            for fragment in left_full_fragments {
2102                expected.insert_fragment(fragment);
2103            }
2104            for fragment in right_full_fragments {
2105                expected.insert_fragment(fragment);
2106            }
2107
2108            let combined_rows = left_rows.iter().chain(right_rows.iter());
2109            expected.extend(combined_rows);
2110
2111            let actual = left | right;
2112            for actual_key_val in &actual.inner {
2113                proptest::prop_assert!(expected.inner.contains_key(actual_key_val.0));
2114                let expected_val = expected.inner.get(actual_key_val.0).unwrap();
2115                prop_assert_eq!(
2116                    actual_key_val.1,
2117                    expected_val,
2118                    "error on key {}",
2119                    actual_key_val.0
2120                );
2121            }
2122            prop_assert_eq!(expected, actual);
2123        }
2124
2125        #[test]
2126        fn test_map_subassign_rows(
2127            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2128            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2129            right_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2130        ) {
2131            let mut left = RowAddrTreeMap::default();
2132            for fragment in left_full_fragments {
2133                left.insert_fragment(fragment);
2134            }
2135            left.extend(left_rows.iter().copied());
2136
2137            let mut right = RowAddrTreeMap::default();
2138            right.extend(right_rows.iter().copied());
2139
2140            let mut expected = left.clone();
2141            for row in right_rows {
2142                expected.remove(row);
2143            }
2144
2145            left -= &right;
2146            prop_assert_eq!(expected, left);
2147        }
2148
2149        #[test]
2150        fn test_map_subassign_frags(
2151            left_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2152            right_full_fragments in proptest::collection::vec(0..u32::MAX, 0..10),
2153            left_rows in proptest::collection::vec(0..u64::MAX, 0..1000),
2154        ) {
2155            let mut left = RowAddrTreeMap::default();
2156            for fragment in left_full_fragments {
2157                left.insert_fragment(fragment);
2158            }
2159            left.extend(left_rows.iter().copied());
2160
2161            let mut right = RowAddrTreeMap::default();
2162            for fragment in right_full_fragments.clone() {
2163                right.insert_fragment(fragment);
2164            }
2165
2166            let mut expected = left.clone();
2167            for fragment in right_full_fragments {
2168                expected.inner.remove(&fragment);
2169            }
2170
2171            left -= &right;
2172            prop_assert_eq!(expected, left);
2173        }
2174
2175        #[test]
2176        fn test_from_sorted_iter(
2177            mut rows in proptest::collection::vec(0..u64::MAX, 0..1000)
2178        ) {
2179            rows.sort();
2180            let num_rows = rows.len();
2181            let mask = RowAddrTreeMap::from_sorted_iter(rows).unwrap();
2182            prop_assert_eq!(mask.len(), Some(num_rows as u64));
2183        }
2184
2185
2186    }
2187
2188    #[test]
2189    fn test_row_addr_selection_deep_size_of() {
2190        use lance_core::deepsize::DeepSizeOf;
2191
2192        // Test Full variant - should have minimal size (just the enum discriminant)
2193        let full = RowAddrSelection::Full;
2194        let full_size = full.deep_size_of();
2195        // Full variant has no heap allocations beyond the enum itself
2196        assert!(full_size < 100); // Small sanity check
2197
2198        // Test Partial variant - should include bitmap size
2199        let mut bitmap = RoaringBitmap::new();
2200        bitmap.insert_range(0..100);
2201        let partial = RowAddrSelection::Partial(bitmap.clone());
2202        let partial_size = partial.deep_size_of();
2203        // Partial variant should be larger due to bitmap
2204        assert!(partial_size >= bitmap.serialized_size());
2205    }
2206
2207    #[test]
2208    fn test_row_addr_selection_union_all_with_full() {
2209        let full = RowAddrSelection::Full;
2210        let partial = RowAddrSelection::Partial(RoaringBitmap::from_iter(&[1, 2, 3]));
2211
2212        assert!(matches!(
2213            RowAddrSelection::union_all(&[&full, &partial]),
2214            RowAddrSelection::Full
2215        ));
2216
2217        let partial2 = RowAddrSelection::Partial(RoaringBitmap::from_iter(&[4, 5, 6]));
2218        let RowAddrSelection::Partial(bitmap) = RowAddrSelection::union_all(&[&partial, &partial2])
2219        else {
2220            panic!("Expected Partial");
2221        };
2222        assert!(bitmap.contains(1) && bitmap.contains(4));
2223    }
2224
2225    #[test]
2226    fn test_insert_range_unbounded_start() {
2227        let mut map = RowAddrTreeMap::default();
2228
2229        // Test exclusive start bound
2230        let count = map.insert_range((std::ops::Bound::Excluded(5), std::ops::Bound::Included(10)));
2231        assert_eq!(count, 5); // 6, 7, 8, 9, 10
2232        assert!(!map.contains(5));
2233        assert!(map.contains(6));
2234        assert!(map.contains(10));
2235
2236        // Test unbounded end
2237        let mut map2 = RowAddrTreeMap::default();
2238        let count = map2.insert_range(0..5);
2239        assert_eq!(count, 5);
2240        assert!(map2.contains(0));
2241        assert!(map2.contains(4));
2242        assert!(!map2.contains(5));
2243    }
2244
2245    #[test]
2246    fn test_remove_from_full_fragment() {
2247        let mut map = RowAddrTreeMap::default();
2248        map.insert_fragment(0);
2249
2250        // Verify it's a full fragment - get_fragment_bitmap returns None for Full
2251        for id in [0, 100, u32::MAX as u64] {
2252            assert!(map.contains(id));
2253        }
2254        assert!(map.get_fragment_bitmap(0).is_none());
2255
2256        // Remove a value from the full fragment
2257        assert!(map.remove(50));
2258
2259        // Now it should be partial (a full RoaringBitmap minus one value)
2260        assert!(map.contains(0) && !map.contains(50) && map.contains(100));
2261        assert!(map.get_fragment_bitmap(0).is_some());
2262    }
2263
2264    #[test]
2265    fn test_retain_fragments() {
2266        let mut map = RowAddrTreeMap::default();
2267        map.insert(0); // fragment 0
2268        map.insert(1 << 32 | 5); // fragment 1
2269        map.insert(2 << 32 | 10); // fragment 2
2270        map.insert_fragment(3); // fragment 3
2271
2272        map.retain_fragments([0, 2]);
2273
2274        assert!(map.contains(0) && map.contains(2 << 32 | 10));
2275        assert!(!map.contains(1 << 32 | 5) && !map.contains(3 << 32));
2276    }
2277
2278    #[test]
2279    fn test_bitor_assign_full_fragment() {
2280        // Test BitOrAssign when LHS has Full and RHS has Partial
2281        let mut map1 = RowAddrTreeMap::default();
2282        map1.insert_fragment(0);
2283        let mut map2 = RowAddrTreeMap::default();
2284        map2.insert(5);
2285
2286        map1 |= &map2;
2287        // Full | Partial = Full
2288        assert!(map1.contains(0) && map1.contains(5) && map1.contains(100));
2289
2290        // Test BitOrAssign when LHS has Partial and RHS has Full
2291        let mut map3 = RowAddrTreeMap::default();
2292        map3.insert(5);
2293        let mut map4 = RowAddrTreeMap::default();
2294        map4.insert_fragment(0);
2295
2296        map3 |= &map4;
2297        // Partial | Full = Full
2298        assert!(map3.contains(0) && map3.contains(5) && map3.contains(100));
2299    }
2300
2301    #[test]
2302    fn test_bitand_assign_full_fragments() {
2303        // Test BitAndAssign when both have Full for same fragment
2304        let mut map1 = RowAddrTreeMap::default();
2305        map1.insert_fragment(0);
2306        let mut map2 = RowAddrTreeMap::default();
2307        map2.insert_fragment(0);
2308
2309        map1 &= &map2;
2310        // Full & Full = Full
2311        assert!(map1.contains(0) && map1.contains(100));
2312
2313        // Test BitAndAssign when LHS Full, RHS Partial
2314        let mut map3 = RowAddrTreeMap::default();
2315        map3.insert_fragment(0);
2316        let mut map4 = RowAddrTreeMap::default();
2317        map4.insert(5);
2318        map4.insert(10);
2319
2320        map3 &= &map4;
2321        // Full & Partial([5,10]) = Partial([5,10])
2322        assert!(map3.contains(5) && map3.contains(10));
2323        assert!(!map3.contains(0) && !map3.contains(100));
2324
2325        // Test that empty intersection results in removal
2326        let mut map5 = RowAddrTreeMap::default();
2327        map5.insert(5);
2328        let mut map6 = RowAddrTreeMap::default();
2329        map6.insert(10);
2330
2331        map5 &= &map6;
2332        assert!(map5.is_empty());
2333    }
2334
2335    #[test]
2336    fn test_sub_assign_with_full_fragments() {
2337        // Test SubAssign when LHS is Full and RHS is Partial
2338        let mut map1 = RowAddrTreeMap::default();
2339        map1.insert_fragment(0);
2340        let mut map2 = RowAddrTreeMap::default();
2341        map2.insert(5);
2342        map2.insert(10);
2343
2344        map1 -= &map2;
2345        // Full - Partial([5,10]) = Full minus those values
2346        assert!(map1.contains(0) && map1.contains(100));
2347        assert!(!map1.contains(5) && !map1.contains(10));
2348
2349        // Test SubAssign when both are Full for same fragment
2350        let mut map3 = RowAddrTreeMap::default();
2351        map3.insert_fragment(0);
2352        let mut map4 = RowAddrTreeMap::default();
2353        map4.insert_fragment(0);
2354
2355        map3 -= &map4;
2356        // Full - Full = empty
2357        assert!(map3.is_empty());
2358
2359        // Test SubAssign when LHS is Partial and RHS is Full
2360        let mut map5 = RowAddrTreeMap::default();
2361        map5.insert(5);
2362        map5.insert(10);
2363        let mut map6 = RowAddrTreeMap::default();
2364        map6.insert_fragment(0);
2365
2366        map5 -= &map6;
2367        // Partial - Full = empty
2368        assert!(map5.is_empty());
2369    }
2370
2371    #[test]
2372    fn test_from_iterator_with_full_fragment() {
2373        // Test that inserting into a full fragment is a no-op
2374        let mut map = RowAddrTreeMap::default();
2375        map.insert_fragment(0);
2376
2377        // Extend with values that would go into fragment 0
2378        map.extend([5u64, 10, 100].iter());
2379
2380        // Should still be full fragment
2381        for id in [0, 5, 10, 100, u32::MAX as u64] {
2382            assert!(map.contains(id));
2383        }
2384    }
2385
2386    #[test]
2387    fn test_insert_range_excluded_end() {
2388        // Test excluded end bound (line 391-393)
2389        let mut map = RowAddrTreeMap::default();
2390        // Using RangeFrom with small range won't hit the unbounded case
2391        // Instead test Bound::Excluded for end
2392        let count = map.insert_range((std::ops::Bound::Included(5), std::ops::Bound::Excluded(10)));
2393        assert_eq!(count, 5); // 5, 6, 7, 8, 9
2394        assert!(map.contains(5));
2395        assert!(map.contains(9));
2396        assert!(!map.contains(10));
2397    }
2398
2399    #[test]
2400    fn test_bitand_assign_owned() {
2401        // Test BitAndAssign<Self> (owned, not reference)
2402        let mut map1 = RowAddrTreeMap::default();
2403        map1.insert(5);
2404        map1.insert(10);
2405
2406        // Using owned rhs (not reference)
2407        map1 &= rows(&[5, 15]);
2408
2409        assert!(map1.contains(5));
2410        assert!(!map1.contains(10) && !map1.contains(15));
2411    }
2412
2413    #[test]
2414    fn test_from_iter_with_full_fragment() {
2415        // When we collect into RowAddrTreeMap, it should handle duplicates
2416        let map: RowAddrTreeMap = vec![5u64, 10, 100].into_iter().collect();
2417        assert!(map.contains(5) && map.contains(10));
2418
2419        // Test that extending a map with full fragment ignores new values
2420        let mut map = RowAddrTreeMap::default();
2421        map.insert_fragment(0);
2422        for val in [5, 10, 100] {
2423            map.insert(val); // This should be no-op since fragment is full
2424        }
2425        // Still full fragment
2426        for id in [0, 5, u32::MAX as u64] {
2427            assert!(map.contains(id));
2428        }
2429    }
2430
2431    // ============================================================================
2432    // Tests for bitmap_to_ranges / ranges_to_bitmap
2433    // ============================================================================
2434
2435    #[test]
2436    fn test_bitmap_to_ranges_empty() {
2437        let bm = RoaringBitmap::new();
2438        assert!(bitmap_to_ranges(&bm).is_empty());
2439    }
2440
2441    #[test]
2442    fn test_bitmap_to_ranges_single() {
2443        let bm = RoaringBitmap::from_iter([5]);
2444        assert_eq!(bitmap_to_ranges(&bm), vec![5..6]);
2445    }
2446
2447    #[test]
2448    fn test_bitmap_to_ranges_contiguous() {
2449        let mut bm = RoaringBitmap::new();
2450        bm.insert_range(10..20);
2451        assert_eq!(bitmap_to_ranges(&bm), vec![10..20]);
2452    }
2453
2454    #[test]
2455    fn test_bitmap_to_ranges_multiple() {
2456        let mut bm = RoaringBitmap::new();
2457        bm.insert_range(0..3);
2458        bm.insert_range(10..15);
2459        bm.insert(100);
2460        assert_eq!(bitmap_to_ranges(&bm), vec![0..3, 10..15, 100..101]);
2461    }
2462
2463    #[test]
2464    fn test_ranges_to_bitmap_empty() {
2465        let bm = ranges_to_bitmap(&[], true);
2466        assert!(bm.is_empty());
2467    }
2468
2469    #[test]
2470    fn test_ranges_to_bitmap_sorted_short_ranges() {
2471        // avg len = 1, uses from_sorted_iter path
2472        let ranges = vec![0..1, 5..6, 10..11];
2473        let bm = ranges_to_bitmap(&ranges, true);
2474        assert!(bm.contains(0) && bm.contains(5) && bm.contains(10));
2475        assert_eq!(bm.len(), 3);
2476    }
2477
2478    #[test]
2479    fn test_ranges_to_bitmap_sorted_long_ranges() {
2480        // avg len = 100, uses insert_range path
2481        let ranges = vec![0..100, 200..300];
2482        let bm = ranges_to_bitmap(&ranges, true);
2483        assert_eq!(bm.len(), 200);
2484        assert!(bm.contains(0) && bm.contains(99));
2485        assert!(!bm.contains(100));
2486        assert!(bm.contains(200) && bm.contains(299));
2487    }
2488
2489    #[test]
2490    fn test_ranges_to_bitmap_unsorted() {
2491        let ranges = vec![200..300, 0..100];
2492        let bm = ranges_to_bitmap(&ranges, false);
2493        assert_eq!(bm.len(), 200);
2494        assert!(bm.contains(0) && bm.contains(250));
2495    }
2496
2497    #[test]
2498    fn test_bitmap_ranges_roundtrip() {
2499        let mut original = RoaringBitmap::new();
2500        original.insert_range(0..50);
2501        original.insert_range(100..200);
2502        original.insert(500);
2503        original.insert_range(1000..1010);
2504
2505        let ranges = bitmap_to_ranges(&original);
2506        let reconstructed = ranges_to_bitmap(&ranges, true);
2507        assert_eq!(original, reconstructed);
2508    }
2509
2510    // ============================================================================
2511    // Tests for RowIdSet
2512    // ============================================================================
2513
2514    fn row_ids(ids: &[u64]) -> RowIdSet {
2515        let mut set = RowIdSet::new();
2516        for &id in ids {
2517            set.inner.insert(id);
2518        }
2519        set
2520    }
2521
2522    #[test]
2523    fn test_row_id_set_construction() {
2524        let set = RowIdSet::new();
2525        assert!(set.is_empty());
2526        assert_eq!(set.len(), Some(0));
2527
2528        let set = row_ids(&[10, 20, 30]);
2529        assert!(!set.is_empty());
2530        assert_eq!(set.len(), Some(3));
2531        assert!(set.contains(10));
2532        assert!(set.contains(20));
2533        assert!(set.contains(30));
2534        assert!(!set.contains(15));
2535    }
2536
2537    #[test]
2538    fn test_row_id_set_remove() {
2539        let mut set = row_ids(&[10, 20, 30]);
2540
2541        assert!(!set.remove(15)); // Not present
2542        assert_eq!(set.len(), Some(3));
2543
2544        assert!(set.remove(20)); // Present
2545        assert_eq!(set.len(), Some(2));
2546        assert!(!set.contains(20));
2547        assert!(set.contains(10));
2548        assert!(set.contains(30));
2549
2550        assert!(!set.remove(20)); // Already removed
2551    }
2552
2553    #[test]
2554    fn test_row_id_set_union() {
2555        let set1 = row_ids(&[10, 20, 30]);
2556        let set2 = row_ids(&[20, 30, 40]);
2557
2558        let result = set1.union(&set2);
2559        assert_eq!(result.len(), Some(4));
2560        for id in [10, 20, 30, 40] {
2561            assert!(result.contains(id));
2562        }
2563    }
2564
2565    #[test]
2566    fn test_row_id_set_difference() {
2567        let set1 = row_ids(&[10, 20, 30, 40]);
2568        let set2 = row_ids(&[20, 40]);
2569
2570        let result = set1.difference(&set2);
2571        assert_eq!(result.len(), Some(2));
2572        assert!(result.contains(10));
2573        assert!(result.contains(30));
2574        assert!(!result.contains(20));
2575        assert!(!result.contains(40));
2576    }
2577
2578    #[test]
2579    fn test_row_id_set_union_all() {
2580        let set1 = row_ids(&[10, 20]);
2581        let set2 = row_ids(&[20, 30]);
2582        let set3 = row_ids(&[30, 40]);
2583
2584        let result = RowIdSet::union_all(&[&set1, &set2, &set3]);
2585        assert_eq!(result.len(), Some(4));
2586        for id in [10, 20, 30, 40] {
2587            assert!(result.contains(id));
2588        }
2589
2590        // Empty slice should return empty set
2591        let result = RowIdSet::union_all(&[]);
2592        assert!(result.is_empty());
2593    }
2594
2595    #[test]
2596    fn test_row_id_set_iter() {
2597        let set = row_ids(&[10, 20, 30]);
2598        let collected: Vec<u64> = set.iter().collect();
2599        assert_eq!(collected, vec![10, 20, 30]);
2600
2601        let empty = RowIdSet::new();
2602        assert_eq!(empty.iter().count(), 0);
2603    }
2604
2605    #[test]
2606    fn test_row_id_set_from_sorted_iter() {
2607        // Valid sorted input
2608        let set = RowIdSet::from_sorted_iter([10, 20, 30, 40]).unwrap();
2609        assert_eq!(set.len(), Some(4));
2610        for id in [10, 20, 30, 40] {
2611            assert!(set.contains(id));
2612        }
2613
2614        // Empty iterator
2615        let set = RowIdSet::from_sorted_iter(std::iter::empty()).unwrap();
2616        assert!(set.is_empty());
2617
2618        // Single element
2619        let set = RowIdSet::from_sorted_iter([42]).unwrap();
2620        assert_eq!(set.len(), Some(1));
2621        assert!(set.contains(42));
2622    }
2623
2624    #[test]
2625    fn test_row_id_set_from_sorted_iter_unsorted() {
2626        // Non-sorted input should return error
2627        let result = RowIdSet::from_sorted_iter([30, 10, 20]);
2628        assert!(result.is_err());
2629        assert!(result.unwrap_err().to_string().contains("non-sorted"));
2630    }
2631
2632    #[test]
2633    fn test_row_id_set_large_values() {
2634        // Test with large u64 values
2635        let large_ids = [u64::MAX - 10, u64::MAX - 5, u64::MAX - 1];
2636        let set = row_ids(&large_ids);
2637
2638        for &id in &large_ids {
2639            assert!(set.contains(id));
2640        }
2641        assert!(!set.contains(u64::MAX));
2642        assert_eq!(set.len(), Some(3));
2643    }
2644
2645    // ============================================================================
2646    // Tests for RowIdMask
2647    // ============================================================================
2648
2649    fn assert_row_id_mask_selects(mask: &RowIdMask, selected: &[u64], not_selected: &[u64]) {
2650        for &id in selected {
2651            assert!(mask.selected(id), "Expected row id {} to be selected", id);
2652        }
2653        for &id in not_selected {
2654            assert!(
2655                !mask.selected(id),
2656                "Expected row id {} to NOT be selected",
2657                id
2658            );
2659        }
2660    }
2661
2662    #[test]
2663    fn test_row_id_mask_construction() {
2664        let full_mask = RowIdMask::all_rows();
2665        assert_eq!(full_mask.max_len(), None);
2666        assert_row_id_mask_selects(&full_mask, &[0, 1, 100, u64::MAX - 1], &[]);
2667
2668        let empty_mask = RowIdMask::allow_nothing();
2669        assert_eq!(empty_mask.max_len(), Some(0));
2670        assert_row_id_mask_selects(&empty_mask, &[], &[0, 1, 100]);
2671
2672        let allow_list = RowIdMask::from_allowed(row_ids(&[10, 20, 30]));
2673        assert_eq!(allow_list.max_len(), Some(3));
2674        assert_row_id_mask_selects(&allow_list, &[10, 20, 30], &[0, 15, 25, 40]);
2675
2676        let block_list = RowIdMask::from_block(row_ids(&[10, 20, 30]));
2677        assert_eq!(block_list.max_len(), None);
2678        assert_row_id_mask_selects(&block_list, &[0, 15, 25, 40], &[10, 20, 30]);
2679    }
2680
2681    #[test]
2682    fn test_row_id_mask_selected_indices() {
2683        // Allow list
2684        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 40]));
2685        assert!(mask.selected_indices(std::iter::empty()).is_empty());
2686        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[1, 3]);
2687
2688        // Block list
2689        let mask = RowIdMask::from_block(row_ids(&[10, 20, 40]));
2690        assert!(mask.selected_indices(std::iter::empty()).is_empty());
2691        assert_eq!(mask.selected_indices([25, 20, 14, 10].iter()), &[0, 2]);
2692    }
2693
2694    #[test]
2695    fn test_row_id_mask_also_allow() {
2696        // Allow list
2697        let mask = RowIdMask::from_allowed(row_ids(&[10, 20]));
2698        let new_mask = mask.also_allow(row_ids(&[20, 30, 40]));
2699        assert_eq!(
2700            new_mask,
2701            RowIdMask::from_allowed(row_ids(&[10, 20, 30, 40]))
2702        );
2703
2704        // Block list
2705        let mask = RowIdMask::from_block(row_ids(&[10, 20, 30]));
2706        let new_mask = mask.also_allow(row_ids(&[20, 40]));
2707        assert_eq!(new_mask, RowIdMask::from_block(row_ids(&[10, 30])));
2708    }
2709
2710    #[test]
2711    fn test_row_id_mask_also_block() {
2712        // Allow list
2713        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 30]));
2714        let new_mask = mask.also_block(row_ids(&[20, 40]));
2715        assert_eq!(new_mask, RowIdMask::from_allowed(row_ids(&[10, 30])));
2716
2717        // Block list
2718        let mask = RowIdMask::from_block(row_ids(&[10, 20]));
2719        let new_mask = mask.also_block(row_ids(&[20, 30, 40]));
2720        assert_eq!(new_mask, RowIdMask::from_block(row_ids(&[10, 20, 30, 40])));
2721    }
2722
2723    #[test]
2724    fn test_row_id_mask_iter_ids() {
2725        // Allow list
2726        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 30]));
2727        let ids: Vec<u64> = mask.iter_ids().unwrap().collect();
2728        assert_eq!(ids, vec![10, 20, 30]);
2729
2730        // Empty allow list
2731        let mask = RowIdMask::allow_nothing();
2732        let iter = mask.iter_ids();
2733        assert!(iter.is_some());
2734        assert_eq!(iter.unwrap().count(), 0);
2735
2736        // Block list
2737        let mask = RowIdMask::from_block(row_ids(&[10, 20, 30]));
2738        assert!(mask.iter_ids().is_none());
2739    }
2740
2741    #[test]
2742    fn test_row_id_mask_default() {
2743        let mask = RowIdMask::default();
2744        // Default should be BlockList with empty set (all rows allowed)
2745        assert_row_id_mask_selects(&mask, &[0, 1, 100, 1000], &[]);
2746        assert_eq!(mask.max_len(), None);
2747    }
2748
2749    #[test]
2750    fn test_row_id_mask_ops() {
2751        let mask = RowIdMask::default();
2752        assert_row_id_mask_selects(&mask, &[1, 5, 100], &[]);
2753
2754        let block_list = mask.also_block(row_ids(&[0, 5, 15]));
2755        assert_row_id_mask_selects(&block_list, &[1, 100], &[5]);
2756
2757        let allow_list = RowIdMask::from_allowed(row_ids(&[0, 2, 5]));
2758        assert_row_id_mask_selects(&allow_list, &[5], &[1, 100]);
2759    }
2760
2761    #[test]
2762    fn test_row_id_mask_combined_ops() {
2763        // Test combining allow and block operations
2764        let mask = RowIdMask::from_allowed(row_ids(&[10, 20, 30, 40, 50]));
2765        let mask = mask.also_block(row_ids(&[20, 40]));
2766        assert_row_id_mask_selects(&mask, &[10, 30, 50], &[20, 40]);
2767
2768        let mask = mask.also_allow(row_ids(&[20, 60]));
2769        assert_row_id_mask_selects(&mask, &[10, 20, 30, 50, 60], &[40]);
2770    }
2771
2772    #[test]
2773    fn test_row_id_mask_with_large_values() {
2774        let large_ids = [u64::MAX - 10, u64::MAX - 5, u64::MAX - 1];
2775
2776        // Allow list with large values
2777        let mask = RowIdMask::from_allowed(row_ids(&large_ids));
2778        for &id in &large_ids {
2779            assert!(mask.selected(id));
2780        }
2781        assert!(!mask.selected(u64::MAX));
2782        assert!(!mask.selected(0));
2783
2784        // Block list with large values
2785        let mask = RowIdMask::from_block(row_ids(&large_ids));
2786        for &id in &large_ids {
2787            assert!(!mask.selected(id));
2788        }
2789        assert!(mask.selected(u64::MAX));
2790        assert!(mask.selected(0));
2791    }
2792
2793    proptest::proptest! {
2794        #[test]
2795        fn test_row_id_set_from_sorted_iter_proptest(
2796            mut row_ids in proptest::collection::vec(0..u64::MAX, 0..1000)
2797        ) {
2798            row_ids.sort();
2799            row_ids.dedup();
2800            let num_rows = row_ids.len();
2801            let set = RowIdSet::from_sorted_iter(row_ids.clone()).unwrap();
2802            prop_assert_eq!(set.len(), Some(num_rows as u64));
2803            for id in row_ids {
2804                prop_assert!(set.contains(id));
2805            }
2806        }
2807
2808        #[test]
2809        fn test_row_id_set_union_proptest(
2810            ids1 in proptest::collection::vec(0..u64::MAX, 0..500),
2811            ids2 in proptest::collection::vec(0..u64::MAX, 0..500),
2812        ) {
2813            let set1 = row_ids(&ids1);
2814            let set2 = row_ids(&ids2);
2815
2816            let result = set1.union(&set2);
2817
2818            // All ids from both sets should be in result
2819            for id in ids1.iter().chain(ids2.iter()) {
2820                prop_assert!(result.contains(*id));
2821            }
2822
2823            // Result size should be union size
2824            let expected_size = ids1.iter().chain(ids2.iter()).collect::<std::collections::HashSet<_>>().len();
2825            prop_assert_eq!(result.len(), Some(expected_size as u64));
2826        }
2827
2828        #[test]
2829        fn test_row_id_set_difference_proptest(
2830            ids1 in proptest::collection::vec(0..u64::MAX, 0..500),
2831            ids2 in proptest::collection::vec(0..u64::MAX, 0..500),
2832        ) {
2833            let set1 = row_ids(&ids1);
2834            let set2 = row_ids(&ids2);
2835
2836            let result = set1.difference(&set2);
2837
2838            // Items in ids1 but not in ids2 should be in result
2839            for id in &ids1 {
2840                if !ids2.contains(id) {
2841                    prop_assert!(result.contains(*id));
2842                } else {
2843                    prop_assert!(!result.contains(*id));
2844                }
2845            }
2846        }
2847
2848        #[test]
2849        fn test_row_id_mask_allow_block_proptest(
2850            allow_ids in proptest::collection::vec(0..10000u64, 0..100),
2851            block_ids in proptest::collection::vec(0..10000u64, 0..100),
2852            test_ids in proptest::collection::vec(0..10000u64, 0..50),
2853        ) {
2854            let mask = RowIdMask::from_allowed(row_ids(&allow_ids))
2855                .also_block(row_ids(&block_ids));
2856
2857            for id in test_ids {
2858                let expected = allow_ids.contains(&id) && !block_ids.contains(&id);
2859                prop_assert_eq!(mask.selected(id), expected);
2860            }
2861        }
2862    }
2863}