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}
49
50#[derive(Debug, Clone)]
52pub struct MemoryBudget(Arc<Allowance>);
53
54impl MemoryBudget {
55 pub fn new(limit: usize) -> Self {
56 Self(Arc::new(Allowance {
57 limit,
58 used: AtomicUsize::new(0),
59 peak: AtomicUsize::new(0),
60 }))
61 }
62
63 pub fn limit(&self) -> usize {
64 self.0.limit
65 }
66
67 pub fn used(&self) -> usize {
68 self.0.used.load(Ordering::Relaxed)
69 }
70
71 pub fn shares_allowance(&self, other: &Self) -> bool {
72 Arc::ptr_eq(&self.0, &other.0)
73 }
74
75 pub fn peak(&self) -> usize {
77 self.0.peak.load(Ordering::Relaxed)
78 }
79
80 pub fn reserve(&self, bytes: usize) -> Result<MemoryReservation, MemoryError> {
81 let mut reservation = self.empty_reservation();
82 reservation.grow(bytes)?;
83 Ok(reservation)
84 }
85
86 pub fn empty_reservation(&self) -> MemoryReservation {
87 MemoryReservation {
88 budget: self.clone(),
89 bytes: 0,
90 }
91 }
92}
93
94#[derive(Debug)]
96pub struct MemoryReservation {
97 budget: MemoryBudget,
98 bytes: usize,
99}
100
101impl MemoryReservation {
102 pub fn bytes(&self) -> usize {
103 self.bytes
104 }
105
106 pub fn budget(&self) -> &MemoryBudget {
107 &self.budget
108 }
109
110 pub fn split(&mut self, bytes: usize) -> Self {
114 self.bytes = self
115 .bytes
116 .checked_sub(bytes)
117 .expect("insufficient reserved bytes");
118 Self {
119 budget: self.budget.clone(),
120 bytes,
121 }
122 }
123
124 pub fn grow(&mut self, additional: usize) -> Result<(), MemoryError> {
126 if additional == 0 {
127 return Ok(());
128 }
129 let allowance = &self.budget.0;
130 let mut used = allowance.used.load(Ordering::Relaxed);
131 loop {
132 let required = used
133 .checked_add(additional)
134 .ok_or(MemoryError::SizeOverflow)?;
135 if required > allowance.limit {
136 return Err(MemoryError::Limit {
137 required,
138 limit: allowance.limit,
139 });
140 }
141 match allowance.used.compare_exchange_weak(
142 used,
143 required,
144 Ordering::Relaxed,
145 Ordering::Relaxed,
146 ) {
147 Ok(_) => {
148 self.bytes += additional;
149 allowance.peak.fetch_max(required, Ordering::Relaxed);
150 return Ok(());
151 }
152 Err(current) => used = current,
153 }
154 }
155 }
156
157 pub fn absorb(&mut self, mut other: Self) {
161 assert!(
162 Arc::ptr_eq(&self.budget.0, &other.budget.0),
163 "different memory allowances"
164 );
165 self.bytes += other.bytes;
166 other.bytes = 0;
167 }
168}
169
170impl Drop for MemoryReservation {
171 fn drop(&mut self) {
172 self.budget.0.used.fetch_sub(self.bytes, Ordering::Relaxed);
173 }
174}
175
176#[derive(Debug)]
178pub struct Budgeted<T> {
179 value: T,
181 memory: MemoryReservation,
182}
183
184impl<T> Budgeted<T> {
185 pub fn new(value: T, memory: MemoryReservation) -> Self {
187 Self { value, memory }
188 }
189
190 pub fn reserved_bytes(&self) -> usize {
191 self.memory.bytes()
192 }
193
194 pub fn budget(&self) -> &MemoryBudget {
195 self.memory.budget()
196 }
197
198 pub fn into_shared(self) -> Result<Arc<Self>, MemoryError> {
200 let mut memory = self.memory.budget().reserve(std::mem::size_of::<Self>())?;
201 let (value, allocations) = self.into_parts();
202 memory.absorb(allocations);
203 Ok(Arc::new(Self::new(value, memory)))
204 }
205
206 pub fn into_parts(self) -> (T, MemoryReservation) {
208 (self.value, self.memory)
209 }
210}
211
212impl<T> std::ops::Deref for Budgeted<T> {
213 type Target = T;
214
215 fn deref(&self) -> &T {
216 &self.value
217 }
218}
219
220impl<T: PartialEq> PartialEq for Budgeted<T> {
221 fn eq(&self, other: &Self) -> bool {
222 self.value == other.value
223 }
224}
225
226impl<T: Eq> Eq for Budgeted<T> {}
227
228impl<T: AsRef<U>, U: ?Sized> AsRef<U> for Budgeted<T> {
229 fn as_ref(&self) -> &U {
230 self.value.as_ref()
231 }
232}
233
234fn buffer_bytes<T>(capacity: usize) -> Result<usize, MemoryError> {
235 capacity
236 .checked_mul(std::mem::size_of::<T>())
237 .filter(|bytes| isize::try_from(*bytes).is_ok())
238 .ok_or(MemoryError::SizeOverflow)
239}
240
241fn reconcile_buffer_capacity<T>(
242 memory: &mut MemoryReservation,
243 capacity: usize,
244) -> Result<(), MemoryError> {
245 memory.grow(buffer_bytes::<T>(capacity)?.saturating_sub(memory.bytes()))
246}
247
248fn replacement<T>(
249 budget: &MemoryBudget,
250 capacity: usize,
251 required: usize,
252) -> Result<(usize, MemoryReservation), MemoryError> {
253 let preferred = capacity.saturating_mul(2).max(required);
254 if let Ok(bytes) = buffer_bytes::<T>(preferred) {
255 if let Ok(memory) = budget.reserve(bytes) {
256 return Ok((preferred, memory));
257 }
258 }
259 Ok((required, budget.reserve(buffer_bytes::<T>(required)?)?))
260}
261
262#[cfg(test)]
263mod tests;