1use std::sync::atomic::{AtomicUsize, Ordering};
12use std::sync::Arc;
13
14mod deque;
15mod string;
16mod vec;
17
18pub use deque::BudgetedDeque;
19pub use string::BudgetedString;
20pub use vec::BudgetedVec;
21
22#[derive(Debug, thiserror::Error)]
23pub enum MemoryError {
24 #[error("memory requires {required} bytes, exceeding limit {limit}")]
25 Limit { required: usize, limit: usize },
26 #[error("memory allocation size overflow")]
27 SizeOverflow,
28 #[error("memory allocation failed: {0}")]
29 Allocation(#[from] std::collections::TryReserveError),
30}
31
32#[derive(Debug)]
33struct Allowance {
34 limit: usize,
35 used: AtomicUsize,
36 peak: AtomicUsize,
37}
38
39#[derive(Debug, Clone)]
41pub struct MemoryBudget(Arc<Allowance>);
42
43impl MemoryBudget {
44 pub fn new(limit: usize) -> Self {
45 Self(Arc::new(Allowance {
46 limit,
47 used: AtomicUsize::new(0),
48 peak: AtomicUsize::new(0),
49 }))
50 }
51
52 pub fn limit(&self) -> usize {
53 self.0.limit
54 }
55
56 pub fn used(&self) -> usize {
57 self.0.used.load(Ordering::Relaxed)
58 }
59
60 pub fn shares_allowance(&self, other: &Self) -> bool {
61 Arc::ptr_eq(&self.0, &other.0)
62 }
63
64 pub fn peak(&self) -> usize {
66 self.0.peak.load(Ordering::Relaxed)
67 }
68
69 pub fn reserve(&self, bytes: usize) -> Result<MemoryReservation, MemoryError> {
70 let mut reservation = self.empty_reservation();
71 reservation.grow(bytes)?;
72 Ok(reservation)
73 }
74
75 pub fn empty_reservation(&self) -> MemoryReservation {
76 MemoryReservation {
77 budget: self.clone(),
78 bytes: 0,
79 }
80 }
81}
82
83#[derive(Debug)]
85pub struct MemoryReservation {
86 budget: MemoryBudget,
87 bytes: usize,
88}
89
90impl MemoryReservation {
91 pub fn bytes(&self) -> usize {
92 self.bytes
93 }
94
95 pub fn budget(&self) -> &MemoryBudget {
96 &self.budget
97 }
98
99 pub fn split(&mut self, bytes: usize) -> Self {
103 self.bytes = self
104 .bytes
105 .checked_sub(bytes)
106 .expect("insufficient reserved bytes");
107 Self {
108 budget: self.budget.clone(),
109 bytes,
110 }
111 }
112
113 pub fn grow(&mut self, additional: usize) -> Result<(), MemoryError> {
115 if additional == 0 {
116 return Ok(());
117 }
118 let allowance = &self.budget.0;
119 let mut used = allowance.used.load(Ordering::Relaxed);
120 loop {
121 let required = used
122 .checked_add(additional)
123 .ok_or(MemoryError::SizeOverflow)?;
124 if required > allowance.limit {
125 return Err(MemoryError::Limit {
126 required,
127 limit: allowance.limit,
128 });
129 }
130 match allowance.used.compare_exchange_weak(
131 used,
132 required,
133 Ordering::Relaxed,
134 Ordering::Relaxed,
135 ) {
136 Ok(_) => {
137 self.bytes += additional;
138 allowance.peak.fetch_max(required, Ordering::Relaxed);
139 return Ok(());
140 }
141 Err(current) => used = current,
142 }
143 }
144 }
145
146 pub fn absorb(&mut self, mut other: Self) {
150 assert!(
151 Arc::ptr_eq(&self.budget.0, &other.budget.0),
152 "different memory allowances"
153 );
154 self.bytes += other.bytes;
155 other.bytes = 0;
156 }
157}
158
159impl Drop for MemoryReservation {
160 fn drop(&mut self) {
161 self.budget.0.used.fetch_sub(self.bytes, Ordering::Relaxed);
162 }
163}
164
165#[derive(Debug)]
167pub struct Budgeted<T> {
168 value: T,
170 memory: MemoryReservation,
171}
172
173impl<T> Budgeted<T> {
174 pub fn new(value: T, memory: MemoryReservation) -> Self {
176 Self { value, memory }
177 }
178
179 pub fn reserved_bytes(&self) -> usize {
180 self.memory.bytes()
181 }
182
183 pub fn into_shared(self) -> Result<Arc<Self>, MemoryError> {
185 let mut memory = self.memory.budget().reserve(std::mem::size_of::<Self>())?;
186 let (value, allocations) = self.into_parts();
187 memory.absorb(allocations);
188 Ok(Arc::new(Self::new(value, memory)))
189 }
190
191 pub fn into_parts(self) -> (T, MemoryReservation) {
193 (self.value, self.memory)
194 }
195}
196
197impl<T> std::ops::Deref for Budgeted<T> {
198 type Target = T;
199
200 fn deref(&self) -> &T {
201 &self.value
202 }
203}
204
205impl<T: PartialEq> PartialEq for Budgeted<T> {
206 fn eq(&self, other: &Self) -> bool {
207 self.value == other.value
208 }
209}
210
211impl<T: Eq> Eq for Budgeted<T> {}
212
213impl<T: AsRef<U>, U: ?Sized> AsRef<U> for Budgeted<T> {
214 fn as_ref(&self) -> &U {
215 self.value.as_ref()
216 }
217}
218
219fn buffer_bytes<T>(capacity: usize) -> Result<usize, MemoryError> {
220 capacity
221 .checked_mul(std::mem::size_of::<T>())
222 .filter(|bytes| isize::try_from(*bytes).is_ok())
223 .ok_or(MemoryError::SizeOverflow)
224}
225
226fn replacement<T>(
227 budget: &MemoryBudget,
228 capacity: usize,
229 required: usize,
230) -> Result<(usize, MemoryReservation), MemoryError> {
231 let preferred = capacity.saturating_mul(2).max(required);
232 if let Ok(bytes) = buffer_bytes::<T>(preferred) {
233 if let Ok(memory) = budget.reserve(bytes) {
234 return Ok((preferred, memory));
235 }
236 }
237 Ok((required, budget.reserve(buffer_bytes::<T>(required)?)?))
238}
239
240#[cfg(test)]
241mod tests;