1use super::{
10 buffer_bytes, reconcile_buffer_capacity, replacement, MemoryBudget, MemoryError,
11 MemoryReservation,
12};
13
14#[derive(Debug)]
15pub struct BudgetedVec<T> {
16 values: Vec<T>,
17 memory: MemoryReservation,
18}
19
20impl<T> BudgetedVec<T> {
21 pub fn new(budget: &MemoryBudget) -> Self {
22 Self {
23 values: Vec::new(),
24 memory: budget.empty_reservation(),
25 }
26 }
27
28 pub fn capacity(&self) -> usize {
29 self.values.capacity()
30 }
31
32 pub fn budget(&self) -> &MemoryBudget {
33 self.memory.budget()
34 }
35
36 pub fn reserve(&mut self, additional: usize) -> Result<(), MemoryError> {
37 let required = self
38 .values
39 .len()
40 .checked_add(additional)
41 .ok_or(MemoryError::SizeOverflow)?;
42 if required <= self.values.capacity() {
43 return Ok(());
44 }
45 let (capacity, memory) = replacement::<T>(self.memory.budget(), self.capacity(), required)?;
46 let mut buffer = Self {
47 values: Vec::new(),
48 memory,
49 };
50 buffer.values.try_reserve_exact(capacity)?;
51 self.replace_buffer(buffer)
52 }
53
54 fn replace_buffer(&mut self, mut buffer: Self) -> Result<(), MemoryError> {
55 reconcile_buffer_capacity::<T>(&mut buffer.memory, buffer.values.capacity())?;
56 buffer.values.append(&mut self.values);
57 *self = buffer;
59 Ok(())
60 }
61
62 pub fn push(&mut self, value: T) -> Result<(), MemoryError> {
63 self.reserve(1)?;
64 self.values.push(value);
65 Ok(())
66 }
67
68 pub fn clear(&mut self) {
69 self.values.clear();
70 }
71
72 pub fn truncate(&mut self, len: usize) {
73 self.values.truncate(len);
74 }
75
76 pub fn shrink_to_fit(&mut self) -> Result<(), MemoryError> {
78 let capacity = self.values.len();
79 if capacity == self.values.capacity() {
80 return Ok(());
81 }
82 let memory = self.memory.budget().reserve(buffer_bytes::<T>(capacity)?)?;
83 let mut buffer = Self {
84 values: Vec::new(),
85 memory,
86 };
87 buffer.values.try_reserve_exact(capacity)?;
88 self.replace_buffer(buffer)
89 }
90
91 pub fn pop(&mut self) -> Option<T> {
92 self.values.pop()
93 }
94
95 pub fn into_parts(self) -> (Vec<T>, MemoryReservation) {
97 (self.values, self.memory)
98 }
99}
100
101impl<T> std::ops::Deref for BudgetedVec<T> {
102 type Target = [T];
103
104 fn deref(&self) -> &[T] {
105 &self.values
106 }
107}
108
109impl<T: Copy> BudgetedVec<T> {
110 pub fn extend_from_slice(&mut self, values: &[T]) -> Result<(), MemoryError> {
112 self.reserve(values.len())?;
113 self.values.extend_from_slice(values);
114 Ok(())
115 }
116}
117
118impl<T> std::ops::DerefMut for BudgetedVec<T> {
119 fn deref_mut(&mut self) -> &mut [T] {
120 &mut self.values
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn extending_a_slice_reserves_before_mutating_and_preserves_failed_inputs() {
130 let budget = MemoryBudget::new(16);
131 let mut values = BudgetedVec::new(&budget);
132 values.extend_from_slice(&[1_u8, 2, 3, 4]).unwrap();
133 let retained = budget.used();
134 assert!(matches!(
135 values.extend_from_slice(&[9; 32]),
136 Err(MemoryError::Limit { .. })
137 ));
138 assert_eq!(&*values, &[1, 2, 3, 4]);
139 assert_eq!(budget.used(), retained);
140 values.extend_from_slice(&[5, 6]).unwrap();
141 assert_eq!(&*values, &[1, 2, 3, 4, 5, 6]);
142 assert_eq!(budget.used(), values.capacity());
143 drop(values);
144 assert_eq!(budget.used(), 0);
145 }
146
147 #[test]
148 fn shrinking_releases_excess_capacity_after_charging_both_buffers() {
149 let budget = MemoryBudget::new(32);
150 let mut values = BudgetedVec::new(&budget);
151 values
152 .extend_from_slice(&[1_u8, 2, 3, 4, 5, 6, 7, 8])
153 .unwrap();
154 values.truncate(2);
155 values.shrink_to_fit().unwrap();
156 assert_eq!(&*values, &[1, 2]);
157 assert_eq!(values.capacity(), 2);
158 assert_eq!(budget.used(), 2);
159 assert_eq!(budget.peak(), 10);
160 values.shrink_to_fit().unwrap();
161 assert_eq!(budget.used(), 2);
162 }
163
164 #[test]
165 fn failed_shrinking_preserves_the_buffer_and_empty_shrinking_needs_no_headroom() {
166 let budget = MemoryBudget::new(8);
167 let mut values = BudgetedVec::new(&budget);
168 values
169 .extend_from_slice(&[1_u8, 2, 3, 4, 5, 6, 7, 8])
170 .unwrap();
171 values.truncate(2);
172 assert!(matches!(
173 values.shrink_to_fit(),
174 Err(MemoryError::Limit { .. })
175 ));
176 assert_eq!(&*values, &[1, 2]);
177 assert_eq!(values.capacity(), 8);
178 assert_eq!(budget.used(), 8);
179 values.clear();
180 values.shrink_to_fit().unwrap();
181 assert_eq!(values.capacity(), 0);
182 assert_eq!(budget.used(), 0);
183 }
184
185 #[test]
186 fn oversized_replacement_is_charged_before_moving_the_original_values() {
187 for reject in [false, true] {
188 let mut original = vec![11_u32, 22];
189 original.reserve_exact(6);
190 let replacement = Vec::with_capacity(12);
191 let original_capacity = original.capacity();
192 let original_bytes = original_capacity * size_of::<u32>();
193 let replacement_bytes = replacement.capacity() * size_of::<u32>();
194 let budget =
195 MemoryBudget::new(original_bytes + replacement_bytes - usize::from(reject));
196 let mut values = BudgetedVec {
197 values: original,
198 memory: budget.reserve(original_bytes).unwrap(),
199 };
200 let buffer = BudgetedVec {
202 values: replacement,
203 memory: budget.reserve(values.len() * size_of::<u32>()).unwrap(),
204 };
205 let result = values.replace_buffer(buffer);
206 assert_eq!(&*values, &[11, 22]);
207 if reject {
208 assert!(matches!(result, Err(MemoryError::Limit { .. })));
209 assert_eq!(values.capacity(), original_capacity);
210 assert_eq!(budget.used(), original_bytes);
211 } else {
212 result.unwrap();
213 assert_eq!(budget.used(), replacement_bytes);
214 assert_eq!(budget.peak(), original_bytes + replacement_bytes);
215 }
216 drop(values);
217 assert_eq!(budget.used(), 0);
218 }
219 }
220
221 #[test]
222 fn shrinking_moves_owned_values_without_copying_or_dropping_survivors() {
223 use std::sync::{
224 atomic::{AtomicUsize, Ordering},
225 Arc,
226 };
227 struct Item(Arc<AtomicUsize>);
228 impl Drop for Item {
229 fn drop(&mut self) {
230 self.0.fetch_add(1, Ordering::Relaxed);
231 }
232 }
233 let budget = MemoryBudget::new(1024);
234 let dropped = Arc::new(AtomicUsize::new(0));
235 let mut values = BudgetedVec::new(&budget);
236 for _ in 0..4 {
237 values.push(Item(Arc::clone(&dropped))).unwrap();
238 }
239 values.truncate(1);
240 assert_eq!(dropped.load(Ordering::Relaxed), 3);
241 values.shrink_to_fit().unwrap();
242 assert_eq!(dropped.load(Ordering::Relaxed), 3);
243 drop(values);
244 assert_eq!(dropped.load(Ordering::Relaxed), 4);
245 assert_eq!(budget.used(), 0);
246 }
247}