refactor: rename batch retrieval methods and update common_traits
Renames `get_batch`, `fill_batch`, and `fill_batch_sorted` to `collect_slots_values`, `fill_slots_values`, and `fill_slots_values_sorted` to align with updated `common_traits 0.13` APIs. Introduces optimized batch retrieval that sorts input indices for sequential mmap access before reordering outputs to match the original query order. Updates test suites to reflect the new method signatures without altering validation logic or coverage.
This commit is contained in:
Generated
+62
-6
@@ -300,7 +300,7 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af737c6c59cb018ecbe6472cbdf86d39c59d78252febfe311953a991b6e4ed85"
|
||||
dependencies = [
|
||||
"common_traits",
|
||||
"common_traits 0.11.4",
|
||||
"epserde",
|
||||
"mem_dbg",
|
||||
]
|
||||
@@ -475,7 +475,18 @@ checksum = "fda9ae1f26adcae83adb2e92f69cf59421f2a277a942f49f8e59f2fcbd7cf062"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"half",
|
||||
"impl-tools",
|
||||
"impl-tools 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "common_traits"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8e0d7d0c5977b1e87f759d406740c38227125f374f54d81bc197e877f2c5f26"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"half",
|
||||
"impl-tools 0.12.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -783,7 +794,7 @@ checksum = "c40d342ff20a2ce62d9a85ce406e672dfa137f902ac9670034533184f1533976"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.11.1",
|
||||
"common_traits",
|
||||
"common_traits 0.11.4",
|
||||
"epserde-derive",
|
||||
"maligned",
|
||||
"mem_dbg",
|
||||
@@ -1083,11 +1094,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ae95c9095c2f1126d7db785955c73cdc5fc33e7c3fa911bd4a42931672029a7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"impl-tools-lib",
|
||||
"impl-tools-lib 0.11.4",
|
||||
"proc-macro-error2",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-tools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06c8567c75b167cdda5ecee024da9cc9581d41bd52460e2e0e768967972aeef1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"impl-tools-lib 0.12.0",
|
||||
"proc-macro-error3",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-tools-lib"
|
||||
version = "0.11.4"
|
||||
@@ -1100,6 +1123,18 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-tools-lib"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c81eb99ba7c6145aeccb8857ddc0c611cd52440091dcfddd605b6a930c9afbf4"
|
||||
dependencies = [
|
||||
"proc-macro-error3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
@@ -1519,7 +1554,7 @@ dependencies = [
|
||||
name = "obicompactvec"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common_traits",
|
||||
"common_traits 0.13.0",
|
||||
"memmap2",
|
||||
"ndarray",
|
||||
"rayon",
|
||||
@@ -2009,6 +2044,16 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr3"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0084e6206a967a2dad822180626b2f6b07a3b379325e8f1ec0438e33a469ba7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
@@ -2020,6 +2065,17 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error3"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cf066225f2373bc711684792b69bdeac0356019b007e721090c24d92d5d5a50"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -2093,7 +2149,7 @@ dependencies = [
|
||||
"cacheline-ef",
|
||||
"clap",
|
||||
"colored",
|
||||
"common_traits",
|
||||
"common_traits 0.11.4",
|
||||
"epserde",
|
||||
"epserde-derive",
|
||||
"fastrand",
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
common_traits = "0.11"
|
||||
common_traits = "0.13"
|
||||
memmap2 = "0.9"
|
||||
ndarray = "0.17"
|
||||
rayon = "1"
|
||||
|
||||
@@ -200,7 +200,7 @@ impl PersistentBitMatrix {
|
||||
/// buffer, ever — the whole point, see
|
||||
/// `PersistentSparseBitMatrix::nonzero_iter`); `Columnar`/`Packed`
|
||||
/// reuse the same sorted-slot batching `fill_sub_matrix` always used
|
||||
/// (`BitSliceView::nonzero_among_sorted`), just eagerly collected here
|
||||
/// (`BitSliceView::enumerate_nonzero_slots`), just eagerly collected here
|
||||
/// rather than filled into a dense buffer — same `get` calls, same
|
||||
/// mmap locality, no per-format duplication of that logic elsewhere.
|
||||
/// `Implicit` is trivial (one column, always present).
|
||||
@@ -228,7 +228,7 @@ impl PersistentBitMatrix {
|
||||
for c in 0..n_cols {
|
||||
let view = self.col_view(c);
|
||||
hits.extend(
|
||||
view.nonzero_among_sorted(&sorted_slots)
|
||||
view.enumerate_nonzero_slots(&sorted_slots)
|
||||
.map(|pos| (perm[pos], c, 1u32)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,36 +75,15 @@ impl PersistentBitVec {
|
||||
/// The slots are sorted internally before reading so that accesses to
|
||||
/// the underlying mmap are as sequential as possible, then the results
|
||||
/// are reordered to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<bool> {
|
||||
let mut out = vec![false; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
out
|
||||
#[inline]
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<bool> {
|
||||
self.view().collect_slots_values(slots)
|
||||
}
|
||||
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
let mut perm: Vec<usize> = (0..n).collect();
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![false; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
#[inline]
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [bool]) {
|
||||
self.view().fill_slots_values(slots, out)
|
||||
}
|
||||
|
||||
// SAFETY: mmap is page-aligned, HEADER_SIZE=16 divisible by 8 → u64-aligned.
|
||||
|
||||
@@ -372,7 +372,7 @@ impl PersistentCompactIntMatrix {
|
||||
let mut out: Vec<Vec<u32>> = Vec::with_capacity(n_cols);
|
||||
for c in 0..n_cols {
|
||||
let mut col_buf = vec![0u32; slots.len()];
|
||||
self.col_view(c).fill_batch(slots, &mut col_buf);
|
||||
self.col_view(c).fill_slots_values(slots, &mut col_buf);
|
||||
out.push(col_buf);
|
||||
}
|
||||
out
|
||||
@@ -397,7 +397,7 @@ impl PersistentCompactIntMatrix {
|
||||
for (c, col) in out.iter_mut().enumerate() {
|
||||
col.resize(n, 0);
|
||||
let mut tmp = vec![0u32; n];
|
||||
self.col_view(c).fill_batch_sorted(&sorted_slots, &mut tmp);
|
||||
self.col_view(c).fill_slots_values_sorted(&sorted_slots, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
col[orig_idx] = tmp[i];
|
||||
}
|
||||
@@ -411,7 +411,7 @@ impl PersistentCompactIntMatrix {
|
||||
/// design even though no sparse count format exists yet). No native
|
||||
/// low-effort case here the way `PersistentSparseBitMatrix` has one —
|
||||
/// both variants reuse the same sorted-slot batching `fill_sub_matrix`
|
||||
/// already used, via `IntSliceView::nonzero_among_sorted`.
|
||||
/// already used, via `IntSliceView::enumerate_nonzero_slots`.
|
||||
pub fn nonzero_iter<'a>(&'a self, slots: &'a [usize]) -> impl Iterator<Item = (usize, usize, u32)> + 'a {
|
||||
let n = slots.len();
|
||||
let mut perm: Vec<usize> = (0..n).collect();
|
||||
@@ -422,7 +422,7 @@ impl PersistentCompactIntMatrix {
|
||||
for c in 0..n_cols {
|
||||
hits.extend(
|
||||
self.col_view(c)
|
||||
.nonzero_among_sorted(&sorted_slots)
|
||||
.enumerate_nonzero_slots(&sorted_slots)
|
||||
.map(|(pos, v)| (perm[pos], c, v)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,34 +67,15 @@ impl PersistentCompactIntVec {
|
||||
/// The slots are sorted internally before reading so that accesses to
|
||||
/// the underlying mmap are as sequential as possible, then the results
|
||||
/// are reordered to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<u32> {
|
||||
let mut out = vec![0u32; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
out
|
||||
#[inline]
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<u32> {
|
||||
self.view().collect_slots_values(slots)
|
||||
}
|
||||
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 { return; }
|
||||
let mut perm: Vec<usize> = (0..n).collect();
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![0u32; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
#[inline]
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [u32]) {
|
||||
self.view().fill_slots_values(slots, out)
|
||||
}
|
||||
|
||||
fn overflow_get(&self, slot: usize) -> u32 {
|
||||
|
||||
@@ -215,65 +215,65 @@ fn hamming_dist_basic() {
|
||||
assert_eq!(ra.hamming_dist(&rb), 2);
|
||||
}
|
||||
|
||||
// ── get_batch tests ────────────────────────────────────────────────────────────
|
||||
// ── collect_slots_values tests ────────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_in_order() {
|
||||
fn bitvec_collect_slots_values_in_order() {
|
||||
let bits = vec![true, false, true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let got = r.get_batch(&[0, 1, 2, 3, 4]);
|
||||
let got = r.collect_slots_values(&[0, 1, 2, 3, 4]);
|
||||
assert_eq!(got, bits);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_out_of_order() {
|
||||
fn bitvec_collect_slots_values_out_of_order() {
|
||||
let bits = vec![true, false, true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let got = r.get_batch(&[3, 0, 4, 1]);
|
||||
let got = r.collect_slots_values(&[3, 0, 4, 1]);
|
||||
assert_eq!(got, vec![false, true, true, false]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_with_duplicates() {
|
||||
fn bitvec_collect_slots_values_with_duplicates() {
|
||||
let bits = vec![true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let got = r.get_batch(&[0, 2, 0, 1]);
|
||||
let got = r.collect_slots_values(&[0, 2, 0, 1]);
|
||||
assert_eq!(got, vec![true, true, true, false]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_empty() {
|
||||
fn bitvec_collect_slots_values_empty() {
|
||||
let (_dir, r) = make_bv(&[true, false]);
|
||||
let got = r.get_batch(&[]);
|
||||
let got = r.collect_slots_values(&[]);
|
||||
assert!(got.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_out_of_bounds_returns_false() {
|
||||
fn bitvec_collect_slots_values_out_of_bounds_returns_false() {
|
||||
// PersistentBitVec::get does NOT bounds-check — out-of-range slots read
|
||||
// whatever bit happens to be in the mmap (zero-initialised, so false).
|
||||
// This is a known gap; get_batch inherits it.
|
||||
// This is a known gap; collect_slots_values inherits it.
|
||||
let (_dir, r) = make_bv(&[true, false]);
|
||||
let got = r.get_batch(&[0, 2]);
|
||||
let got = r.collect_slots_values(&[0, 2]);
|
||||
assert_eq!(got, vec![true, false]);
|
||||
}
|
||||
|
||||
// BitSliceView get_batch (same logic, exercised through the view)
|
||||
// BitSliceView collect_slots_values (same logic, exercised through the view)
|
||||
#[test]
|
||||
fn bitslice_view_get_batch() {
|
||||
fn bitslice_view_collect_slots_values() {
|
||||
let bits = vec![true, false, true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let view = r.view();
|
||||
assert_eq!(view.get_batch(&[0, 1, 2]), vec![true, false, true]);
|
||||
assert_eq!(view.get_batch(&[4, 3]), vec![true, false]);
|
||||
assert_eq!(view.collect_slots_values(&[0, 1, 2]), vec![true, false, true]);
|
||||
assert_eq!(view.collect_slots_values(&[4, 3]), vec![true, false]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitslice_view_get_batch_out_of_bounds_returns_false() {
|
||||
fn bitslice_view_collect_slots_values_out_of_bounds_returns_false() {
|
||||
// BitSliceView::get does NOT bounds-check either.
|
||||
let bits = vec![true, false];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let view = r.view();
|
||||
let got = view.get_batch(&[0, 2]);
|
||||
let got = view.collect_slots_values(&[0, 2]);
|
||||
assert_eq!(got, vec![true, false]);
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ fn partial_relfreq_bray_additive_across_split() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── get_batch tests ────────────────────────────────────────────────────────────
|
||||
// ── collect_slots_values tests ────────────────────────────────────────────────────────────
|
||||
|
||||
fn make_pciv(counts: &[u32]) -> (tempfile::TempDir, PersistentCompactIntVec) {
|
||||
let dir = tempdir().unwrap();
|
||||
@@ -413,49 +413,49 @@ fn make_pciv(counts: &[u32]) -> (tempfile::TempDir, PersistentCompactIntVec) {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_in_order() {
|
||||
fn pciv_collect_slots_values_in_order() {
|
||||
let counts = vec![10u32, 255, 300, 1000];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let got = v.get_batch(&[0, 1, 2, 3]);
|
||||
let got = v.collect_slots_values(&[0, 1, 2, 3]);
|
||||
assert_eq!(got, counts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_out_of_order() {
|
||||
fn pciv_collect_slots_values_out_of_order() {
|
||||
let counts = vec![10u32, 255, 300, 1000];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let got = v.get_batch(&[3, 0, 2, 1]);
|
||||
let got = v.collect_slots_values(&[3, 0, 2, 1]);
|
||||
assert_eq!(got, vec![1000, 10, 300, 255]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_with_duplicates() {
|
||||
fn pciv_collect_slots_values_with_duplicates() {
|
||||
let counts = vec![10u32, 255, 300];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let got = v.get_batch(&[0, 2, 0, 1]);
|
||||
let got = v.collect_slots_values(&[0, 2, 0, 1]);
|
||||
assert_eq!(got, vec![10, 300, 10, 255]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_empty() {
|
||||
fn pciv_collect_slots_values_empty() {
|
||||
let (_dir, v) = make_pciv(&[10u32, 20]);
|
||||
let got: Vec<u32> = v.get_batch(&[]);
|
||||
let got: Vec<u32> = v.collect_slots_values(&[]);
|
||||
assert!(got.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_out_of_bounds_panics() {
|
||||
fn pciv_collect_slots_values_out_of_bounds_panics() {
|
||||
let (_dir, v) = make_pciv(&[10u32, 20]);
|
||||
let result = std::panic::catch_unwind(|| v.get_batch(&[0, 2]));
|
||||
assert!(result.is_err(), "get_batch should panic on out-of-bounds slot");
|
||||
let result = std::panic::catch_unwind(|| v.collect_slots_values(&[0, 2]));
|
||||
assert!(result.is_err(), "collect_slots_values should panic on out-of-bounds slot");
|
||||
}
|
||||
|
||||
// IntSliceView get_batch (same logic, exercised through the view)
|
||||
// IntSliceView collect_slots_values (same logic, exercised through the view)
|
||||
#[test]
|
||||
fn intslice_view_get_batch() {
|
||||
fn intslice_view_collect_slots_values() {
|
||||
let counts = vec![10u32, 255, 300, 1000];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let view = v.view();
|
||||
assert_eq!(view.get_batch(&[0, 1, 2]), vec![10, 255, 300]);
|
||||
assert_eq!(view.get_batch(&[3, 1]), vec![1000, 255]);
|
||||
assert_eq!(view.collect_slots_values(&[0, 1, 2]), vec![10, 255, 300]);
|
||||
assert_eq!(view.collect_slots_values(&[3, 1]), vec![1000, 255]);
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ impl<'a> BitSliceView<'a> {
|
||||
///
|
||||
/// Slots are sorted internally before reading, then results are reordered
|
||||
/// to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<bool> {
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<bool> {
|
||||
let mut out = vec![false; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
self.fill_slots_values(slots, &mut out);
|
||||
out
|
||||
}
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [bool]) {
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 { return; }
|
||||
@@ -44,32 +44,43 @@ impl<'a> BitSliceView<'a> {
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![false; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
self.fill_slots_values_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/// Positions in `sorted_slots` (not the slots themselves) whose bit is
|
||||
/// set — a `filter` over `get`, not a new traversal. `sorted_slots`
|
||||
/// need not actually be sorted for correctness, but callers batching
|
||||
/// several columns over the same slot set sort once and reuse it for
|
||||
/// cache-friendlier `get` access (see `PersistentBitMatrix::nonzero_iter`).
|
||||
pub(crate) fn nonzero_among_sorted<'b>(self, sorted_slots: &'b [usize]) -> impl Iterator<Item = usize> + 'b
|
||||
/// `(position in slots, value)` for every slot. Plain `enumerate` +
|
||||
/// `get`: order never affects correctness here, only the caller's cache
|
||||
/// locality (see `enumerate_nonzero_slots`), so this takes `slots` as
|
||||
/// given.
|
||||
#[inline]
|
||||
pub fn enumerate_slots_values<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = (usize, bool)> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
sorted_slots.iter().enumerate()
|
||||
.filter_map(move |(pos, &slot)| self.get(slot).then_some(pos))
|
||||
slots.iter().enumerate().map(move |(pos, &slot)| (pos, self.get(slot)))
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_slots_values_sorted(&self, sorted_slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (pos, v) in self.enumerate_slots_values(sorted_slots) {
|
||||
out[pos] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// Positions in `slots` (not the slots themselves) whose bit is set.
|
||||
/// Order of `slots` never matters for correctness; callers batching
|
||||
/// several columns over the same slot set sort it once beforehand
|
||||
/// purely for cache-friendlier `get` access (see
|
||||
/// `PersistentBitMatrix::nonzero_iter`).
|
||||
pub fn enumerate_nonzero_slots<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = usize> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
self.enumerate_slots_values(slots).filter_map(|(pos, v)| v.then_some(pos))
|
||||
}
|
||||
|
||||
pub fn count_ones(&self) -> u64 {
|
||||
@@ -184,14 +195,14 @@ impl<'a> IntSliceView<'a> {
|
||||
///
|
||||
/// Slots are sorted internally before reading, then results are reordered
|
||||
/// to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<u32> {
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<u32> {
|
||||
let mut out = vec![0u32; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
self.fill_slots_values(slots, &mut out);
|
||||
out
|
||||
}
|
||||
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [u32]) {
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 { return; }
|
||||
@@ -199,32 +210,40 @@ impl<'a> IntSliceView<'a> {
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![0u32; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
self.fill_slots_values_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`BitSliceView::nonzero_among_sorted`]: `(position in
|
||||
/// sorted_slots, value)` for every nonzero value — a `filter_map` over
|
||||
/// `get`, not a new traversal.
|
||||
pub(crate) fn nonzero_among_sorted<'b>(self, sorted_slots: &'b [usize]) -> impl Iterator<Item = (usize, u32)> + 'b
|
||||
/// `(position in slots, value)` for every slot. See
|
||||
/// [`BitSliceView::enumerate_slots_values`]: order never affects
|
||||
/// correctness here.
|
||||
#[inline]
|
||||
pub fn enumerate_slots_values<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = (usize, u32)> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
sorted_slots.iter().enumerate().filter_map(move |(pos, &slot)| {
|
||||
let v = self.get(slot);
|
||||
(v != 0).then_some((pos, v))
|
||||
})
|
||||
slots.iter().enumerate().map(move |(pos, &slot)| (pos, self.get(slot)))
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_slots_values_sorted(&self, sorted_slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (pos, v) in self.enumerate_slots_values(sorted_slots) {
|
||||
out[pos] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`BitSliceView::enumerate_nonzero_slots`]: `(position in slots,
|
||||
/// value)` for every nonzero value — a `filter_map` over `get`, not a
|
||||
/// new traversal.
|
||||
pub fn enumerate_nonzero_slots<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = (usize, u32)> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
self.enumerate_slots_values(slots).filter_map(|(pos, v)| (v != 0).then_some((pos, v)))
|
||||
}
|
||||
|
||||
/// Sequential merge scan: yields all n values in slot order.
|
||||
|
||||
Reference in New Issue
Block a user