use std::collections::BTreeMap;
use kernel::UsdMicros;
use serde_json::Value;
use super::report::WINDOW_SLOT;
pub(crate) fn add(map: &mut BTreeMap<String, u64>, key: &str, amount: u64) {
let slot = map.entry(key.to_owned()).or_insert(0);
*slot = slot.saturating_add(amount);
}
pub(crate) fn quantify(map: &BTreeMap<String, u64>) -> Vec<(String, UsdMicros)> {
map.iter()
.map(|(k, v)| (k.clone(), UsdMicros::new(*v)))
.collect()
}
pub(crate) fn segment_weights(data: &serde_json::Map<String, Value>) -> Vec<(String, u64)> {
let mut weights = Vec::new();
if let Some(segments) = data.get("segments").and_then(Value::as_array) {
for segment in segments {
let slot = segment
.get("slot")
.and_then(Value::as_str)
.unwrap_or("unknown")
.to_owned();
let len = segment.get("len").and_then(Value::as_u64).unwrap_or(0);
weights.push((slot, len));
}
}
if let Some(window) = data.get("window_bytes").and_then(Value::as_u64) {
weights.push((WINDOW_SLOT.to_owned(), window));
}
weights
}
pub(crate) fn split(total: u64, weights: &[(String, u64)], fallback: &str) -> Vec<(String, u64)> {
let sum: u64 = weights.iter().map(|(_, w)| *w).fold(0, u64::saturating_add);
if weights.is_empty() || sum == 0 {
return vec![(fallback.to_owned(), total)];
}
let total_wide = u128::from(total);
let sum_wide = u128::from(sum);
let mut shares: Vec<(String, u64, u128)> = Vec::new();
let mut assigned: u64 = 0;
for (bucket, weight) in weights {
let scaled = total_wide.saturating_mul(u128::from(*weight));
let floor = scaled.checked_div(sum_wide).unwrap_or(0);
let remainder = scaled.checked_rem(sum_wide).unwrap_or(0);
let floor_u64 = u64::try_from(floor).unwrap_or(u64::MAX);
assigned = assigned.saturating_add(floor_u64);
shares.push((bucket.clone(), floor_u64, remainder));
}
let mut order: Vec<usize> = (0..shares.len()).collect();
order.sort_by(|a, b| {
let left = shares.get(*a);
let right = shares.get(*b);
match (left, right) {
(Some(l), Some(r)) => r.2.cmp(&l.2).then_with(|| l.0.cmp(&r.0)),
_ => std::cmp::Ordering::Equal,
}
});
let mut leftover = total.saturating_sub(assigned);
for index in order {
if leftover == 0 {
break;
}
if let Some(entry) = shares.get_mut(index) {
entry.1 = entry.1.saturating_add(1);
leftover = leftover.saturating_sub(1);
}
}
let mut merged: BTreeMap<String, u64> = BTreeMap::new();
for (bucket, share, _) in shares {
let slot = merged.entry(bucket).or_insert(0);
*slot = slot.saturating_add(share);
}
merged.into_iter().collect()
}