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