Skip to content

Commit 4999e4a

Browse files
committed
feat: minor changes to the bitvec (polish)
1 parent c52cf69 commit 4999e4a

File tree

1 file changed

+51
-64
lines changed
  • libs/@local/hashql/core/src/id/bit_vec/matrix

1 file changed

+51
-64
lines changed

libs/@local/hashql/core/src/id/bit_vec/matrix/mod.rs

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use super::{
2121
BitIter, DenseBitSet, WORD_BITS, Word, bitwise, clear_excess_bits_in_final_word, count_ones,
2222
num_words, word_index_and_mask,
2323
};
24-
use crate::id::Id;
24+
use crate::id::{Id, IdVec};
2525

2626
#[cfg(test)]
2727
mod tests;
@@ -38,8 +38,8 @@ mod tests;
3838
///
3939
/// Obtained via [`BitMatrix::row`] or [`SparseBitMatrix::row`].
4040
#[derive(Clone, Copy)]
41-
pub struct RowRef<'a, C> {
42-
words: &'a [Word],
41+
pub struct RowRef<'set, C> {
42+
words: &'set [Word],
4343
col_domain_size: usize,
4444
marker: PhantomData<C>,
4545
}
@@ -130,8 +130,8 @@ impl<C: Id> fmt::Debug for RowRef<'_, C> {
130130
}
131131
}
132132

133-
impl<'a, C: Id> IntoIterator for &RowRef<'a, C> {
134-
type IntoIter = BitIter<'a, C>;
133+
impl<'set, C: Id> IntoIterator for &RowRef<'set, C> {
134+
type IntoIter = BitIter<'set, C>;
135135
type Item = C;
136136

137137
#[inline]
@@ -161,15 +161,15 @@ impl<'a, C: Id> IntoIterator for RowRef<'a, C> {
161161
/// operations against other rows or [`DenseBitSet`]s.
162162
///
163163
/// Obtained via [`BitMatrix::row_mut`].
164-
pub struct RowMut<'a, C> {
165-
words: &'a mut [Word],
164+
pub struct RowMut<'set, C> {
165+
words: &'set mut [Word],
166166
col_domain_size: usize,
167167
marker: PhantomData<C>,
168168
}
169169

170-
impl<'a, C: Id> RowMut<'a, C> {
170+
impl<'set, C: Id> RowMut<'set, C> {
171171
#[inline]
172-
const fn new(words: &'a mut [Word], col_domain_size: usize) -> Self {
172+
const fn new(words: &'set mut [Word], col_domain_size: usize) -> Self {
173173
Self {
174174
words,
175175
col_domain_size,
@@ -312,8 +312,8 @@ impl<C: Id> fmt::Debug for RowMut<'_, C> {
312312
}
313313
}
314314

315-
impl<'a, C: Id> IntoIterator for &'a RowMut<'a, C> {
316-
type IntoIter = BitIter<'a, C>;
315+
impl<'row, C: Id> IntoIterator for &'row RowMut<'_, C> {
316+
type IntoIter = BitIter<'row, C>;
317317
type Item = C;
318318

319319
#[inline]
@@ -351,7 +351,7 @@ pub struct BitMatrix<R, C, A: Allocator = Global> {
351351
row_domain_size: usize,
352352
col_domain_size: usize,
353353
words: Vec<Word, A>,
354-
marker: PhantomData<fn(R, C)>,
354+
marker: PhantomData<(R, C)>,
355355
}
356356

357357
impl<R: Id, C: Id> BitMatrix<R, C> {
@@ -685,9 +685,13 @@ impl<T: Id, A: Allocator> BitMatrix<T, T, A> {
685685
///
686686
/// Runs in O(n³/w) time where n is the domain size and w is the word size (64),
687687
/// achieving a 64× speedup over the scalar version through bitwise parallelism.
688+
///
689+
/// # Panics
690+
///
691+
/// If the matrix is not square.
688692
pub fn transitive_closure(&mut self) {
693+
assert_eq!(self.row_domain_size, self.col_domain_size);
689694
let size = self.row_domain_size;
690-
debug_assert_eq!(size, self.col_domain_size);
691695

692696
for pivot in 0..size {
693697
let pivot_id = T::from_usize(pivot);
@@ -773,18 +777,17 @@ struct RowSlot {
773777
#[derive(Clone)]
774778
pub struct SparseBitMatrix<R, C, A: Allocator = Global> {
775779
col_domain_size: usize,
776-
words_per_row: usize,
777780

778781
/// Shared backing buffer for all row data.
779782
backing: Vec<Word, A>,
780783

781784
/// Maps row index → slot (or `None` if unallocated).
782-
index: Vec<Option<RowSlot>, A>,
785+
index: IdVec<R, Option<RowSlot>, A>,
783786

784787
/// Free-list of word offsets for reuse by newly activated rows.
785788
free_slots: Vec<u32, A>,
786789

787-
marker: PhantomData<fn(R, C)>,
790+
marker: PhantomData<(R, C)>,
788791
}
789792

790793
impl<R: Id, C: Id> SparseBitMatrix<R, C> {
@@ -804,64 +807,62 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
804807
where
805808
A: Clone,
806809
{
807-
let words_per_row = num_words(col_domain_size);
808-
809810
Self {
810811
col_domain_size,
811-
words_per_row,
812812
backing: Vec::new_in(alloc.clone()),
813-
index: Vec::new_in(alloc.clone()),
813+
index: IdVec::new_in(alloc.clone()),
814814
free_slots: Vec::new_in(alloc),
815815
marker: PhantomData,
816816
}
817817
}
818818

819+
#[inline]
820+
const fn words_per_row(&self) -> usize {
821+
num_words(self.col_domain_size)
822+
}
823+
819824
/// Ensures `row` has an allocated slot, returning the word offset.
820825
#[inline]
821826
fn ensure_row(&mut self, row: R) -> u32 {
822-
let row_idx = row.as_usize();
823-
824-
// Extend the index if needed.
825-
if row_idx >= self.index.len() {
826-
self.index.resize(row_idx + 1, None);
827-
}
828-
829-
if let Some(slot) = self.index[row_idx] {
827+
if let Some(slot) = self.index.lookup(row) {
830828
return slot.offset;
831829
}
832830

833831
// Allocate from free-list or extend backing.
834832
let offset = if let Some(offset) = self.free_slots.pop() {
835833
// Reuse a freed slot — zero it.
836834
let start = offset as usize;
837-
self.backing[start..start + self.words_per_row].fill(0);
835+
let words_per_row = self.words_per_row();
836+
self.backing[start..start + words_per_row].fill(0);
838837
offset
839838
} else {
840839
debug_assert!(u32::try_from(self.backing.len()).is_ok());
840+
841841
let offset = self.backing.len() as u32;
842842
self.backing
843-
.resize(self.backing.len() + self.words_per_row, 0);
843+
.resize(self.backing.len() + self.words_per_row(), 0);
844844
offset
845845
};
846846

847-
self.index[row_idx] = Some(RowSlot { offset });
847+
self.index.insert(row, RowSlot { offset });
848848
offset
849849
}
850850

851851
/// Returns the word slice for an allocated row, or `None`.
852852
#[inline]
853853
fn row_words(&self, row: R) -> Option<&[Word]> {
854-
let slot = self.index.get(row.as_usize())?.as_ref()?;
854+
let slot = self.index.lookup(row)?;
855855
let start = slot.offset as usize;
856-
Some(&self.backing[start..start + self.words_per_row])
856+
Some(&self.backing[start..start + self.words_per_row()])
857857
}
858858

859859
/// Returns the mutable word slice for an allocated row, or `None`.
860860
#[inline]
861861
fn row_words_mut(&mut self, row: R) -> Option<&mut [Word]> {
862-
let slot = self.index.get(row.as_usize())?.as_ref()?;
862+
let slot = self.index.lookup(row)?;
863863
let start = slot.offset as usize;
864-
Some(&mut self.backing[start..start + self.words_per_row])
864+
let words_per_row = self.words_per_row();
865+
Some(&mut self.backing[start..start + words_per_row])
865866
}
866867

867868
/// Returns an immutable view of `row`, or `None` if the row is unallocated.
@@ -937,10 +938,9 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
937938
/// Clears all bits in `row` and returns its slot to the free-list.
938939
#[inline]
939940
pub fn clear_row(&mut self, row: R) {
940-
let row_idx = row.as_usize();
941-
if let Some(Some(slot)) = self.index.get(row_idx) {
941+
if let Some(slot) = self.index.lookup(row) {
942942
self.free_slots.push(slot.offset);
943-
self.index[row_idx] = None;
943+
self.index.remove(row);
944944
}
945945
}
946946

@@ -958,7 +958,8 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
958958
#[inline]
959959
pub fn insert_all_into_row(&mut self, row: R) {
960960
let offset = self.ensure_row(row) as usize;
961-
let words = &mut self.backing[offset..offset + self.words_per_row];
961+
let words_per_row = self.words_per_row();
962+
let words = &mut self.backing[offset..offset + words_per_row];
962963
words.fill(!0);
963964
clear_excess_bits_in_final_word(self.col_domain_size, words);
964965
}
@@ -974,18 +975,14 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
974975
return false;
975976
}
976977

977-
let Some(read_offset) = self
978-
.index
979-
.get(read.as_usize())
980-
.and_then(|slot| slot.map(|slot| slot.offset as usize))
981-
else {
978+
let Some(read_offset) = self.index.lookup(read).map(|slot| slot.offset as usize) else {
982979
return false;
983980
};
984981

985982
let write_offset = self.ensure_row(write) as usize;
986983

987984
let mut changed: Word = 0;
988-
for offset in 0..self.words_per_row {
985+
for offset in 0..self.words_per_row() {
989986
let old = self.backing[write_offset + offset];
990987
let new = old | self.backing[read_offset + offset];
991988
self.backing[write_offset + offset] = new;
@@ -1006,18 +1003,14 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10061003
}
10071004

10081005
let (Some(read_offset), Some(write_offset)) = (
1009-
self.index
1010-
.get(read.as_usize())
1011-
.and_then(|slot| slot.map(|slot| slot.offset as usize)),
1012-
self.index
1013-
.get(write.as_usize())
1014-
.and_then(|slot| slot.map(|slot| slot.offset as usize)),
1006+
self.index.lookup(read).map(|slot| slot.offset as usize),
1007+
self.index.lookup(write).map(|slot| slot.offset as usize),
10151008
) else {
10161009
return false;
10171010
};
10181011

10191012
let mut changed: Word = 0;
1020-
for offset in 0..self.words_per_row {
1013+
for offset in 0..self.words_per_row() {
10211014
let old = self.backing[write_offset + offset];
10221015
let new = old & !self.backing[read_offset + offset];
10231016
self.backing[write_offset + offset] = new;
@@ -1035,19 +1028,11 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10351028
return false;
10361029
}
10371030

1038-
let Some(write_offset) = self
1039-
.index
1040-
.get(write.as_usize())
1041-
.and_then(|slot| slot.map(|slot| slot.offset as usize))
1042-
else {
1031+
let Some(write_offset) = self.index.lookup(write).map(|slot| slot.offset as usize) else {
10431032
return false;
10441033
};
10451034

1046-
let Some(read_offset) = self
1047-
.index
1048-
.get(read.as_usize())
1049-
.and_then(|slot| slot.map(|slot| slot.offset as usize))
1050-
else {
1035+
let Some(read_offset) = self.index.lookup(read).map(|slot| slot.offset as usize) else {
10511036
// read is empty → write becomes empty
10521037
let was_nonempty = self.row(write).is_some_and(|row_ref| !row_ref.is_empty());
10531038
if was_nonempty {
@@ -1057,7 +1042,7 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10571042
};
10581043

10591044
let mut changed: Word = 0;
1060-
for offset in 0..self.words_per_row {
1045+
for offset in 0..self.words_per_row() {
10611046
let old = self.backing[write_offset + offset];
10621047
let new = old & self.backing[read_offset + offset];
10631048
self.backing[write_offset + offset] = new;
@@ -1073,8 +1058,10 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10731058
pub fn union_row_with(&mut self, row: R, other: &DenseBitSet<C>) -> bool {
10741059
debug_assert_eq!(other.domain_size(), self.col_domain_size);
10751060
let offset = self.ensure_row(row) as usize;
1061+
let words_per_row = self.words_per_row();
1062+
10761063
bitwise(
1077-
&mut self.backing[offset..offset + self.words_per_row],
1064+
&mut self.backing[offset..offset + words_per_row],
10781065
&other.words,
10791066
|lhs, rhs| lhs | rhs,
10801067
)

0 commit comments

Comments
 (0)