use super::package::Package; use super::BLOCK_VSIZE; use crate::types::SelectedTx; /// How many packages to look ahead when current doesn't fit. const LOOK_AHEAD: usize = 100; /// Partition packages into blocks by fee rate. /// /// Packages are sorted by fee rate descending, then placed into blocks. /// When a package doesn't fit, we look ahead for smaller packages that do. /// Atomic packages are never split across blocks. pub fn partition_into_blocks(mut packages: Vec, num_blocks: usize) -> Vec> { packages.sort_unstable_by(|a, b| b.fee_rate.cmp(&a.fee_rate)); let mut blocks: Vec> = Vec::with_capacity(num_blocks); let mut current_block: Vec = Vec::new(); let mut current_vsize: u64 = 0; let mut used = vec![false; packages.len()]; let mut idx = 0; while idx < packages.len() && blocks.len() < num_blocks { if used[idx] { idx += 1; continue; } let remaining_space = BLOCK_VSIZE.saturating_sub(current_vsize); let package = &packages[idx]; if package.vsize <= remaining_space { current_block.extend(package.txs.iter().copied()); current_vsize += package.vsize; used[idx] = true; idx += 1; continue; } // Package doesn't fit if current_block.is_empty() { // Empty block: add oversized package anyway current_block.extend(package.txs.iter().copied()); current_vsize += package.vsize; used[idx] = true; idx += 1; continue; } // Look ahead for a smaller package that fits let found_smaller = try_fill_with_smaller( &packages, &mut used, idx, remaining_space, &mut current_block, &mut current_vsize, ); if !found_smaller { // No package fits, finalize current block blocks.push(std::mem::take(&mut current_block)); current_vsize = 0; } } if !current_block.is_empty() && blocks.len() < num_blocks { blocks.push(current_block); } blocks } /// Try to find a smaller package in the look-ahead window that fits. fn try_fill_with_smaller( packages: &[Package], used: &mut [bool], start: usize, remaining_space: u64, block: &mut Vec, block_vsize: &mut u64, ) -> bool { let end = (start + LOOK_AHEAD).min(packages.len()); for idx in (start + 1)..end { if used[idx] { continue; } let package = &packages[idx]; if package.vsize <= remaining_space { block.extend(package.txs.iter().copied()); *block_vsize += package.vsize; used[idx] = true; return true; } } false }