1use std::sync::atomic::{AtomicUsize, Ordering};
12use std::sync::Arc;
13
14mod deque;
15mod hash_set;
16mod heap;
17mod map;
18mod production;
19mod string;
20mod vec;
21
22pub use deque::BudgetedDeque;
23pub use hash_set::BudgetedHashSet;
24pub use heap::BudgetedBinaryHeap;
25pub use map::{
26 BudgetedMap, BudgetedMapIter, BudgetedSharedMap, BudgetedSharedMapIter, OwnedMap,
27 OwnedMapIntoIter, OwnedSet, OwnedSetIntoIter, OwnedSetIter, PreparedMapEntry,
28};
29pub use production::{Produced, ProductionControl, ProductionString, ProductionVec};
30pub use string::BudgetedString;
31pub use vec::BudgetedVec;
32
33#[derive(Debug, thiserror::Error)]
34pub enum MemoryError {
35 #[error("memory requires {required} bytes, exceeding limit {limit}")]
36 Limit { required: usize, limit: usize },
37 #[error("memory allocation size overflow")]
38 SizeOverflow,
39 #[error("memory allocation failed: {0}")]
40 Allocation(#[from] std::collections::TryReserveError),
41}
42
43#[derive(Debug)]
44struct Allowance {
45 limit: usize,
46 used: AtomicUsize,
47 peak: AtomicUsize,
48 parent: Option<MemoryBudget>,
49}
50
51impl Allowance {
52 fn claim(&self, additional: usize) -> Result<(), MemoryError> {
53 let mut used = self.used.load(Ordering::Relaxed);
54 loop {
55 let required = used
56 .checked_add(additional)
57 .ok_or(MemoryError::SizeOverflow)?;
58 if required > self.limit {
59 return Err(MemoryError::Limit {
60 required,
61 limit: self.limit,
62 });
63 }
64 match self.used.compare_exchange_weak(
65 used,
66 required,
67 Ordering::Relaxed,
68 Ordering::Relaxed,
69 ) {
70 Ok(_) => {
71 self.peak.fetch_max(required, Ordering::Relaxed);
72 return Ok(());
73 }
74 Err(current) => used = current,
75 }
76 }
77 }
78}
79
80impl Drop for Allowance {
81 fn drop(&mut self) {
82 let mut parent = self.parent.take();
84 while let Some(budget) = parent {
85 match Arc::try_unwrap(budget.0) {
86 Ok(mut allowance) => parent = allowance.parent.take(),
87 Err(_) => break,
88 }
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct MemoryBudget(Arc<Allowance>);
96
97impl MemoryBudget {
98 pub fn new(limit: usize) -> Self {
99 Self(Arc::new(Allowance {
100 limit,
101 used: AtomicUsize::new(0),
102 peak: AtomicUsize::new(0),
103 parent: None,
104 }))
105 }
106
107 pub fn child(&self, limit: usize) -> Self {
109 Self(Arc::new(Allowance {
110 limit,
111 used: AtomicUsize::new(0),
112 peak: AtomicUsize::new(0),
113 parent: Some(self.clone()),
114 }))
115 }
116
117 fn allowances(&self) -> impl Iterator<Item = &Allowance> {
118 std::iter::successors(Some(self), |budget| budget.0.parent.as_ref())
119 .map(|budget| &*budget.0)
120 }
121
122 pub fn limit(&self) -> usize {
123 self.0.limit
124 }
125
126 pub fn used(&self) -> usize {
127 self.0.used.load(Ordering::Relaxed)
128 }
129
130 pub fn shares_allowance(&self, other: &Self) -> bool {
131 Arc::ptr_eq(&self.0, &other.0)
132 }
133
134 pub fn peak(&self) -> usize {
136 self.0.peak.load(Ordering::Relaxed)
137 }
138
139 pub fn reserve(&self, bytes: usize) -> Result<MemoryReservation, MemoryError> {
140 let mut reservation = self.empty_reservation();
141 reservation.grow(bytes)?;
142 Ok(reservation)
143 }
144
145 pub fn empty_reservation(&self) -> MemoryReservation {
146 MemoryReservation {
147 budget: self.clone(),
148 bytes: 0,
149 }
150 }
151}
152
153#[derive(Debug)]
155pub struct MemoryReservation {
156 budget: MemoryBudget,
157 bytes: usize,
158}
159
160impl MemoryReservation {
161 pub fn bytes(&self) -> usize {
162 self.bytes
163 }
164
165 pub fn budget(&self) -> &MemoryBudget {
166 &self.budget
167 }
168
169 pub fn split(&mut self, bytes: usize) -> Self {
173 self.bytes = self
174 .bytes
175 .checked_sub(bytes)
176 .expect("insufficient reserved bytes");
177 Self {
178 budget: self.budget.clone(),
179 bytes,
180 }
181 }
182
183 pub fn grow(&mut self, additional: usize) -> Result<(), MemoryError> {
185 if additional == 0 {
186 return Ok(());
187 }
188 for (claimed, allowance) in self.budget.allowances().enumerate() {
189 if let Err(error) = allowance.claim(additional) {
190 for previous in self.budget.allowances().take(claimed) {
191 previous.used.fetch_sub(additional, Ordering::Relaxed);
192 }
193 return Err(error);
194 }
195 }
196 self.bytes += additional;
197 Ok(())
198 }
199
200 pub fn absorb(&mut self, mut other: Self) {
204 assert!(
205 Arc::ptr_eq(&self.budget.0, &other.budget.0),
206 "different memory allowances"
207 );
208 self.bytes += other.bytes;
209 other.bytes = 0;
210 }
211}
212
213impl Drop for MemoryReservation {
214 fn drop(&mut self) {
215 for allowance in self.budget.allowances() {
216 allowance.used.fetch_sub(self.bytes, Ordering::Relaxed);
217 }
218 }
219}
220
221#[derive(Debug)]
223pub struct Budgeted<T> {
224 value: T,
226 memory: MemoryReservation,
227}
228
229impl<T> Budgeted<T> {
230 pub fn new(value: T, memory: MemoryReservation) -> Self {
232 Self { value, memory }
233 }
234
235 pub fn reserved_bytes(&self) -> usize {
236 self.memory.bytes()
237 }
238
239 pub fn budget(&self) -> &MemoryBudget {
240 self.memory.budget()
241 }
242
243 pub fn into_shared(self) -> Result<Arc<Self>, MemoryError> {
245 let mut memory = self.memory.budget().reserve(std::mem::size_of::<Self>())?;
246 let (value, allocations) = self.into_parts();
247 memory.absorb(allocations);
248 Ok(Arc::new(Self::new(value, memory)))
249 }
250
251 pub fn into_parts(self) -> (T, MemoryReservation) {
253 (self.value, self.memory)
254 }
255}
256
257impl<T> std::ops::Deref for Budgeted<T> {
258 type Target = T;
259
260 fn deref(&self) -> &T {
261 &self.value
262 }
263}
264
265impl<T: PartialEq> PartialEq for Budgeted<T> {
266 fn eq(&self, other: &Self) -> bool {
267 self.value == other.value
268 }
269}
270
271impl<T: Eq> Eq for Budgeted<T> {}
272
273impl<T: AsRef<U>, U: ?Sized> AsRef<U> for Budgeted<T> {
274 fn as_ref(&self) -> &U {
275 self.value.as_ref()
276 }
277}
278
279fn buffer_bytes<T>(capacity: usize) -> Result<usize, MemoryError> {
280 capacity
281 .checked_mul(std::mem::size_of::<T>())
282 .filter(|bytes| isize::try_from(*bytes).is_ok())
283 .ok_or(MemoryError::SizeOverflow)
284}
285
286fn reconcile_buffer_capacity<T>(
287 memory: &mut MemoryReservation,
288 capacity: usize,
289) -> Result<(), MemoryError> {
290 memory.grow(buffer_bytes::<T>(capacity)?.saturating_sub(memory.bytes()))
291}
292
293fn replacement<T>(
294 budget: &MemoryBudget,
295 capacity: usize,
296 required: usize,
297) -> Result<(usize, MemoryReservation), MemoryError> {
298 let preferred = capacity.saturating_mul(2).max(required);
299 if let Ok(bytes) = buffer_bytes::<T>(preferred) {
300 if let Ok(memory) = budget.reserve(bytes) {
301 return Ok((preferred, memory));
302 }
303 }
304 Ok((required, budget.reserve(buffer_bytes::<T>(required)?)?))
305}
306
307#[cfg(test)]
308mod tests;