use crate::{cdk::candid::Principal, view::placement::sharding::ShardPlacement};
use std::collections::{BTreeMap, BTreeSet};
pub(super) struct SlotBackfillPlan {
pub slots: BTreeMap<Principal, u32>,
pub occupied: BTreeSet<u32>,
}
pub(super) fn plan_slot_backfill(
pool: &str,
view: &[(Principal, ShardPlacement)],
max_slots: u32,
) -> SlotBackfillPlan {
let mut entries: Vec<(Principal, ShardPlacement)> = view
.iter()
.filter(|(_, entry)| entry.pool.as_str() == pool)
.map(|(pid, entry)| (*pid, entry.clone()))
.collect();
entries.sort_by_key(|(pid, _)| *pid);
let mut slots = BTreeMap::<Principal, u32>::new();
let mut occupied = BTreeSet::<u32>::new();
for (pid, entry) in &entries {
if entry_has_assigned_slot(entry) {
slots.insert(*pid, entry.slot);
occupied.insert(entry.slot);
}
}
if max_slots == 0 {
return SlotBackfillPlan { slots, occupied };
}
let available: Vec<u32> = (0..max_slots)
.filter(|slot| !occupied.contains(slot))
.collect();
if available.is_empty() {
return SlotBackfillPlan { slots, occupied };
}
let mut idx = 0usize;
for (pid, entry) in &entries {
if entry_has_assigned_slot(entry) {
continue;
}
if idx >= available.len() {
break;
}
let slot = available[idx];
idx += 1;
slots.insert(*pid, slot);
occupied.insert(slot);
}
SlotBackfillPlan { slots, occupied }
}
const fn entry_has_assigned_slot(entry: &ShardPlacement) -> bool {
entry.slot != ShardPlacement::UNASSIGNED_SLOT
}