use std::fmt;
const BITS_PER_WORD: usize = u64::BITS as usize;
#[inline]
const fn word_of(row: usize) -> usize {
row / BITS_PER_WORD
}
#[inline]
const fn bit_of(row: usize) -> u64 {
1 << (row % BITS_PER_WORD)
}
#[derive(Clone, Default)]
pub(super) struct LineSet {
first_word: usize,
words: Vec<u64>,
}
impl LineSet {
#[inline]
pub(super) fn insert(&mut self, row: usize) {
let word = word_of(row);
self.reserve(word);
self.words[word - self.first_word] |= bit_of(row);
}
pub(super) fn insert_range(&mut self, start: usize, end: usize) {
if end < start {
return;
}
self.reserve(word_of(start));
self.reserve(word_of(end));
let first = word_of(start) - self.first_word;
let last = word_of(end) - self.first_word;
let from_start = u64::MAX << (start % BITS_PER_WORD);
let through_end = u64::MAX >> (BITS_PER_WORD - 1 - end % BITS_PER_WORD);
if first == last {
self.words[first] |= from_start & through_end;
} else {
self.words[first] |= from_start;
self.words[first + 1..last].fill(u64::MAX);
self.words[last] |= through_end;
}
}
#[inline]
pub(super) fn remove(&mut self, row: usize) {
if let Some(slot) = self.slot(row) {
self.words[slot] &= !bit_of(row);
}
}
#[inline]
pub(super) fn contains(&self, row: usize) -> bool {
self.slot(row)
.is_some_and(|slot| self.words[slot] & bit_of(row) != 0)
}
#[inline]
pub(super) fn len(&self) -> usize {
self.words.iter().map(|w| w.count_ones() as usize).sum()
}
pub(super) fn union_len(&self, other: &Self) -> usize {
self.len() + other.len() - self.intersection_len(other)
}
pub(super) fn union_with(&mut self, other: &Self) {
let Some(last_word) = other.last_word() else {
return;
};
self.reserve(other.first_word);
self.reserve(last_word);
let base = other.first_word - self.first_word;
for (dst, src) in self.words[base..].iter_mut().zip(&other.words) {
*dst |= src;
}
}
fn rows(&self) -> impl Iterator<Item = usize> {
self.words.iter().enumerate().flat_map(|(index, &word)| {
let base = (self.first_word + index) * BITS_PER_WORD;
(0..BITS_PER_WORD)
.filter(move |bit| word >> bit & 1 == 1)
.map(move |bit| base + bit)
})
}
#[inline]
fn last_word(&self) -> Option<usize> {
(!self.words.is_empty()).then(|| self.first_word + self.words.len() - 1)
}
#[inline]
fn slot(&self, row: usize) -> Option<usize> {
word_of(row)
.checked_sub(self.first_word)
.filter(|slot| *slot < self.words.len())
}
#[inline]
fn word(&self, word: usize) -> u64 {
word.checked_sub(self.first_word)
.and_then(|slot| self.words.get(slot))
.copied()
.unwrap_or(0)
}
fn intersection_len(&self, other: &Self) -> usize {
let start = self.first_word.max(other.first_word);
let end = (self.first_word + self.words.len()).min(other.first_word + other.words.len());
(start..end)
.map(|word| {
(self.words[word - self.first_word] & other.words[word - other.first_word])
.count_ones() as usize
})
.sum()
}
fn reserve(&mut self, word: usize) {
if self.words.is_empty() {
self.first_word = word;
self.words.push(0);
} else if word < self.first_word {
let below = self.first_word - word;
self.words.splice(0..0, std::iter::repeat_n(0, below));
self.first_word = word;
} else if word - self.first_word >= self.words.len() {
self.words.resize(word - self.first_word + 1, 0);
}
}
}
impl PartialEq for LineSet {
fn eq(&self, other: &Self) -> bool {
let start = self.first_word.min(other.first_word);
let end = (self.first_word + self.words.len()).max(other.first_word + other.words.len());
(start..end).all(|word| self.word(word) == other.word(word))
}
}
impl fmt::Debug for LineSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.rows()).finish()
}
}
#[cfg(test)]
mod tests {
use super::{BITS_PER_WORD, LineSet};
fn set_of(rows: &[usize]) -> LineSet {
let mut set = LineSet::default();
for row in rows {
set.insert(*row);
}
set
}
fn rows_of(set: &LineSet) -> Vec<usize> {
set.rows().collect()
}
#[test]
fn default_is_empty_and_unallocated() {
let set = LineSet::default();
assert_eq!(set.len(), 0);
assert!(!set.contains(0));
assert_eq!(set.words.capacity(), 0, "an unused set must not allocate");
}
#[test]
fn insert_is_idempotent_and_order_independent() {
let ascending = set_of(&[3, 64, 65, 4_000]);
let descending = set_of(&[4_000, 65, 64, 3]);
let repeated = set_of(&[64, 3, 64, 4_000, 65, 3]);
assert_eq!(rows_of(&ascending), vec![3, 64, 65, 4_000]);
assert_eq!(ascending.len(), 4);
assert_eq!(ascending, descending);
assert_eq!(ascending, repeated);
}
#[test]
fn insert_below_offset_grows_downward() {
let mut set = set_of(&[300]);
set.insert(1);
set.insert(299);
assert_eq!(rows_of(&set), vec![1, 299, 300]);
assert!(set.contains(1));
assert!(!set.contains(0));
assert!(!set.contains(2));
}
#[test]
fn insert_range_covers_exactly_the_inclusive_span() {
let mut single = LineSet::default();
single.insert_range(7, 7);
assert_eq!(rows_of(&single), vec![7]);
let mut within_word = LineSet::default();
within_word.insert_range(5, 9);
assert_eq!(rows_of(&within_word), vec![5, 6, 7, 8, 9]);
let mut across_words = LineSet::default();
across_words.insert_range(63, 129);
assert_eq!(across_words.len(), 67);
assert!(!across_words.contains(62));
assert!(across_words.contains(63));
assert!(across_words.contains(64));
assert!(across_words.contains(129));
assert!(!across_words.contains(130));
let mut whole_words = LineSet::default();
whole_words.insert_range(64, 191);
assert_eq!(whole_words.len(), 128);
assert!(!whole_words.contains(63));
assert!(!whole_words.contains(192));
}
#[test]
fn insert_range_ignores_an_inverted_span() {
let mut within_word = set_of(&[10]);
within_word.insert_range(9, 8);
assert_eq!(rows_of(&within_word), vec![10]);
let mut across_words = set_of(&[10]);
across_words.insert_range(BITS_PER_WORD, BITS_PER_WORD - 1);
assert_eq!(rows_of(&across_words), vec![10]);
}
#[test]
fn insert_range_grows_a_seeded_set_in_both_directions() {
let mut set = set_of(&[200, 205]);
set.insert_range(70, 202);
set.insert_range(300, 301);
assert!(set.contains(70), "the prepended low end must be set");
assert!(set.contains(202));
assert!(set.contains(205), "the seeded rows must survive the growth");
assert!(set.contains(300));
assert!(!set.contains(69));
assert!(!set.contains(203));
assert!(!set.contains(204));
assert!(!set.contains(299));
assert!(!set.contains(302));
assert_eq!(set.len(), 136);
}
#[test]
fn remove_clears_one_row_and_tolerates_absent_rows() {
let mut set = set_of(&[64, 65, 66]);
set.remove(65);
assert_eq!(rows_of(&set), vec![64, 66]);
set.remove(0);
set.remove(10_000);
set.remove(65);
assert_eq!(rows_of(&set), vec![64, 66]);
assert_eq!(set.len(), 2);
}
#[test]
fn union_with_deduplicates_shared_rows() {
let mut parent = set_of(&[5, 70]);
let child = set_of(&[6, 70, 130]);
parent.union_with(&child);
assert_eq!(rows_of(&parent), vec![5, 6, 70, 130]);
assert_eq!(
parent.len(),
4,
"the shared row 70 must not be counted twice"
);
}
#[test]
fn union_with_extends_in_both_directions() {
let mut below = set_of(&[500]);
below.union_with(&set_of(&[1]));
assert_eq!(rows_of(&below), vec![1, 500]);
let mut above = set_of(&[1]);
above.union_with(&set_of(&[500]));
assert_eq!(rows_of(&above), vec![1, 500]);
}
#[test]
fn union_with_handles_an_empty_side() {
let mut parent = set_of(&[9, 200]);
parent.union_with(&LineSet::default());
assert_eq!(rows_of(&parent), vec![9, 200]);
let mut empty = LineSet::default();
empty.union_with(&set_of(&[9, 200]));
assert_eq!(rows_of(&empty), vec![9, 200]);
}
#[test]
fn union_len_counts_each_row_once() {
let only_comments = set_of(&[2, 64, 300]);
let code_comments = set_of(&[64, 301]);
assert_eq!(only_comments.union_len(&code_comments), 4);
assert_eq!(
only_comments.union_len(&code_comments),
code_comments.union_len(&only_comments),
"union cardinality is symmetric"
);
assert_eq!(set_of(&[1]).union_len(&set_of(&[1_000])), 2);
assert_eq!(only_comments.union_len(&LineSet::default()), 3);
}
#[test]
fn equality_ignores_offset_and_padding() {
let direct = set_of(&[500]);
let mut padded = set_of(&[1, 500]);
padded.remove(1);
assert_eq!(direct, padded);
assert_eq!(padded, direct);
assert_eq!(LineSet::default(), padded_but_cleared());
assert_ne!(direct, set_of(&[499]));
}
fn padded_but_cleared() -> LineSet {
let mut set = set_of(&[4_096]);
set.remove(4_096);
set
}
#[test]
fn dense_rows_cost_one_word_per_sixty_four() {
let mut set = LineSet::default();
set.insert_range(0, 10_239);
assert_eq!(set.len(), 10_240);
assert_eq!(set.words.len(), 10_240 / BITS_PER_WORD);
assert_eq!(set.words.len(), 160);
}
#[test]
fn large_file_span_merges_without_loss() {
const ROWS: usize = 100_000;
let mut unit = LineSet::default();
for chunk in 0..10 {
let start = chunk * (ROWS / 10);
let mut child = LineSet::default();
child.insert_range(start, start + ROWS / 10);
unit.union_with(&child);
}
assert_eq!(unit.len(), ROWS + 1);
assert!(unit.contains(0));
assert!(unit.contains(ROWS));
assert!(!unit.contains(ROWS + 1));
}
#[test]
fn debug_renders_rows_not_words() {
let rendered = format!("{:?}", set_of(&[3, BITS_PER_WORD + 1, 200]));
assert_eq!(rendered, format!("{{3, {}, 200}}", BITS_PER_WORD + 1));
assert_eq!(format!("{:?}", LineSet::default()), "{}");
}
}