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