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