Fix panic with zero-weighted segments

This commit is contained in:
Joscha 2023-02-19 02:02:27 +01:00
parent a8876e94f3
commit b1c276ec38

View file

@ -51,6 +51,8 @@ use super::{Either2, Either3, Either4, Either5, Either6, Either7};
// removes all segments that are at least as small as their allotment. It then // removes all segments that are at least as small as their allotment. It then
// resizes the remaining segments to their allotments. // resizes the remaining segments to their allotments.
// TODO Handle overflows and other sizing issues correctly
struct Segment { struct Segment {
size: u16, size: u16,
weight: f32, weight: f32,
@ -164,30 +166,29 @@ fn shrink(segments: &mut [Segment], mut available: u16) {
total_weight = segments.len() as f32; total_weight = segments.len() as f32;
} }
let mut changed = false; let mut removed = 0;
segments.retain(|s| { segments.retain(|s| {
let allotment = s.weight / total_weight * available as f32; let allotment = s.weight / total_weight * available as f32;
if (s.size as f32) > allotment { if (s.size as f32) > allotment {
return true; // May need to shrink return true; // May need to shrink
} }
// The size subtracted from `available` is always smaller than or // The segment size subtracted from `available` is always smaller
// equal to its allotment. It must be smaller in at least one case, // than or equal to its allotment. Since `available` is the sum of
// or we wouldn't be shrinking. Since `available` is the sum of all // all allotments, it can never go below 0.
// allotments, it never reaches 0. assert!(s.size <= available);
assert!(available > s.size);
available -= s.size; removed += s.size;
changed = true;
false false
}); });
available -= removed;
// If all segments were smaller or the same size as their allotments, we // If all segments were smaller or the same size as their allotments, we
// would be trying to grow, not shrink them. Hence, there must be at // would be trying to grow, not shrink them. Hence, there must be at
// least one segment bigger than its allotment. // least one segment bigger than its allotment.
assert!(!segments.is_empty()); assert!(!segments.is_empty());
if !changed { if removed == 0 {
break; // All segments want more than their weight allows. break; // All segments want more than their weight allows.
} }
} }