use super::types::PostingEntry;
#[inline]
pub(super) fn merge_sorted_runs(
frozen_runs: Vec<Vec<PostingEntry>>,
mutable_run: Vec<PostingEntry>,
) -> Vec<PostingEntry> {
let mut runs: Vec<Vec<PostingEntry>> = frozen_runs;
if !mutable_run.is_empty() {
runs.push(mutable_run);
}
match runs.len() {
0 => Vec::new(),
1 => runs.into_iter().next().unwrap_or_default(),
_ => k_way_merge(&runs),
}
}
fn k_way_merge(runs: &[Vec<PostingEntry>]) -> Vec<PostingEntry> {
let total_len: usize = runs.iter().map(Vec::len).sum();
let mut result: Vec<PostingEntry> = Vec::with_capacity(total_len);
let mut cursors: Vec<usize> = vec![0; runs.len()];
while let Some(min_doc_id) = find_min_head_doc_id(runs, &cursors) {
if let Some(entry) = advance_matching_runs(runs, &mut cursors, min_doc_id) {
result.push(entry);
}
}
result
}
#[inline]
fn find_min_head_doc_id(runs: &[Vec<PostingEntry>], cursors: &[usize]) -> Option<u64> {
runs.iter()
.enumerate()
.filter_map(|(i, run)| run.get(cursors[i]).map(|entry| entry.doc_id))
.min()
}
#[inline]
fn advance_matching_runs(
runs: &[Vec<PostingEntry>],
cursors: &mut [usize],
target_doc_id: u64,
) -> Option<PostingEntry> {
let mut picked: Option<PostingEntry> = None;
for (i, run) in runs.iter().enumerate() {
if run
.get(cursors[i])
.is_some_and(|entry| entry.doc_id == target_doc_id)
{
picked = Some(run[cursors[i]]);
cursors[i] += 1;
}
}
picked
}