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)
| 700 | /// even allocation, then give unused capacity from short bodies to higher-ranked |
| 701 | /// items instead of silently discarding available context. |
| 702 | fn apply_budget(mut items: Vec<MemoryItem>, budget_chars: usize) -> Vec<MemoryItem> { |
| 703 | if items.is_empty() { |
| 704 | return items; |
| 705 | } |
| 706 | let lengths: Vec<usize> = items.iter().map(|item| item.body.chars().count()).collect(); |
| 707 | let baseline = budget_chars / items.len(); |
| 708 | let mut allocations: Vec<usize> = lengths |
| 709 | .iter() |
| 710 | .map(|length| (*length).min(baseline)) |
| 711 | .collect(); |
| 712 | let mut remaining = budget_chars.saturating_sub(allocations.iter().sum()); |
| 713 | for (allocation, length) in allocations.iter_mut().zip(&lengths) { |
| 714 | let extra = length.saturating_sub(*allocation).min(remaining); |
| 715 | *allocation += extra; |
| 716 | remaining -= extra; |
| 717 | if remaining == 0 { |
| 718 | break; |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | for ((item, allocation), length) in items.iter_mut().zip(allocations).zip(lengths) { |
| 723 | if allocation < length { |
| 724 | item.body = truncate_chars(&item.body, allocation); |
| 725 | item.truncated = true; |
| 726 | } |
| 727 | } |
| 728 | items |
| 729 | } |
| 730 | |
| 731 | /// Truncate to at most `max_chars` characters on a char boundary. |
| 732 | fn truncate_chars(s: &str, max_chars: usize) -> String { |