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 shares_allowance(&self, other: &Self) -> bool {
136 StrongArc::ptr_eq(&self.0, &other.0)
137 }
138
139 pub fn peak(&self) -> usize {
141 self.0.peak.load(Ordering::Relaxed)
142 }
143
144 pub fn reserve(&self, bytes: usize) -> Result<MemoryReservation, MemoryError> {
145 let mut reservation = self.empty_reservation();
146 reservation.grow(bytes)?;
147 Ok(reservation)
148 }
149
150 pub fn empty_reservation(&self) -> MemoryReservation {
151 MemoryReservation {
152 budget: self.clone(),
153 bytes: 0,
154 }
155 }
156}
157
158#[derive(Debug)]
160pub struct MemoryReservation {
161 budget: MemoryBudget,
162 bytes: usize,
163}
164
165impl MemoryReservation {
166 pub fn bytes(&self) -> usize {
167 self.bytes
168 }
169
170 pub fn budget(&self) -> &MemoryBudget {
171 &self.budget
172 }
173
174 pub fn split(&mut self, bytes: usize) -> Self {
178 self.bytes = self
179 .bytes
180 .checked_sub(bytes)
181 .expect("insufficient reserved bytes");
182 Self {
183 budget: self.budget.clone(),
184 bytes,
185 }
186 }
187
188 pub fn grow(&mut self, additional: usize) -> Result<(), MemoryError> {
190 if additional == 0 {
191 return Ok(());
192 }
193 for (claimed, allowance) in self.budget.allowances().enumerate() {
194 if let Err(error) = allowance.claim(additional) {
195 for previous in self.budget.allowances().take(claimed) {
196 previous.used.fetch_sub(additional, Ordering::Relaxed);
197 }
198 return Err(error);
199 }
200 }
201 self.bytes += additional;
202 Ok(())
203 }
204
205 pub fn absorb(&mut self, mut other: Self) {
209 assert!(
210 self.budget.shares_allowance(&other.budget),
211 "different memory allowances"
212 );
213 self.bytes += other.bytes;
214 other.bytes = 0;
215 }
216}
217
218impl Drop for MemoryReservation {
219 fn drop(&mut self) {
220 for allowance in self.budget.allowances() {
221 allowance.used.fetch_sub(self.bytes, Ordering::Relaxed);
222 }
223 }
224}
225
226#[derive(Debug)]
228pub struct Budgeted<T> {
229 value: T,
231 memory: MemoryReservation,
232}
233
234impl<T> Budgeted<T> {
235 pub fn new(value: T, memory: MemoryReservation) -> Self {
237 Self { value, memory }
238 }
239
240 pub fn reserved_bytes(&self) -> usize {
241 self.memory.bytes()
242 }
243
244 pub fn budget(&self) -> &MemoryBudget {
245 self.memory.budget()
246 }
247
248 pub fn into_shared(self) -> Result<Arc<Self>, MemoryError> {
250 let mut memory = self.memory.budget().reserve(std::mem::size_of::<Self>())?;
251 let (value, allocations) = self.into_parts();
252 memory.absorb(allocations);
253 Ok(Arc::new(Self::new(value, memory)))
254 }
255
256 pub fn into_parts(self) -> (T, MemoryReservation) {
258 (self.value, self.memory)
259 }
260}
261
262impl<T> std::ops::Deref for Budgeted<T> {
263 type Target = T;
264
265 fn deref(&self) -> &T {
266 &self.value
267 }
268}
269
270impl<T: PartialEq> PartialEq for Budgeted<T> {
271 fn eq(&self, other: &Self) -> bool {
272 self.value == other.value
273 }
274}
275
276impl<T: Eq> Eq for Budgeted<T> {}
277
278impl<T: AsRef<U>, U: ?Sized> AsRef<U> for Budgeted<T> {
279 fn as_ref(&self) -> &U {
280 self.value.as_ref()
281 }
282}
283
284fn buffer_bytes<T>(capacity: usize) -> Result<usize, MemoryError> {
285 capacity
286 .checked_mul(std::mem::size_of::<T>())
287 .filter(|bytes| isize::try_from(*bytes).is_ok())
288 .ok_or(MemoryError::SizeOverflow)
289}
290
291fn reconcile_buffer_capacity<T>(
292 memory: &mut MemoryReservation,
293 capacity: usize,
294) -> Result<(), MemoryError> {
295 memory.grow(buffer_bytes::<T>(capacity)?.saturating_sub(memory.bytes()))
296}
297
298fn replacement<T>(
299 budget: &MemoryBudget,
300 capacity: usize,
301 required: usize,
302) -> Result<(usize, MemoryReservation), MemoryError> {
303 let preferred = capacity.saturating_mul(2).max(required);
304 if let Ok(bytes) = buffer_bytes::<T>(preferred) {
305 if let Ok(memory) = budget.reserve(bytes) {
306 return Ok((preferred, memory));
307 }
308 }
309 Ok((required, budget.reserve(buffer_bytes::<T>(required)?)?))
310}
311
312#[cfg(test)]
313mod tests;