Truncate bodies so their combined length fits `budget_chars`. Start with an even allocation, then give unused capacity from short bodies to higher-ranked items instead of silently discarding available context.
(mut items: Vec<MemoryItem>, budget_chars: usize)
| 739 | /// even allocation, then give unused capacity from short bodies to higher-ranked |
| 740 | /// items instead of silently discarding available context. |
| 741 | fn apply_budget(mut items: Vec<MemoryItem>, budget_chars: usize) -> Vec<MemoryItem> { |
| 742 | if items.is_empty() { |
| 743 | return items; |
| 744 | } |
| 745 | let lengths: Vec<usize> = items.iter().map(|item| item.body.chars().count()).collect(); |
| 746 | let baseline = budget_chars / items.len(); |
| 747 | let mut allocations: Vec<usize> = lengths |
| 748 | .iter() |
| 749 | .map(|length| (*length).min(baseline)) |
| 750 | .collect(); |
| 751 | let mut remaining = budget_chars.saturating_sub(allocations.iter().sum()); |
| 752 | for (allocation, length) in allocations.iter_mut().zip(&lengths) { |
| 753 | let extra = length.saturating_sub(*allocation).min(remaining); |
| 754 | *allocation += extra; |
| 755 | remaining -= extra; |
| 756 | if remaining == 0 { |
| 757 | break; |
| 758 | } |
| 759 | } |
| 760 | |
| 761 | for ((item, allocation), length) in items.iter_mut().zip(allocations).zip(lengths) { |
| 762 | if allocation < length { |
| 763 | item.body = truncate_chars(&item.body, allocation); |
| 764 | item.truncated = true; |
| 765 | } |
| 766 | } |
| 767 | items |
| 768 | } |
| 769 | |
| 770 | /// Truncate to at most `max_chars` characters on a char boundary. |
| 771 | fn truncate_chars(s: &str, max_chars: usize) -> String { |