uqa_core/memory/
production.rs1use super::{Budgeted, MemoryBudget, MemoryReservation};
10use crate::{CancellationToken, Value, ValueRetentionError};
11
12mod string;
13mod vec;
14pub use string::ProductionString;
15pub use vec::ProductionVec;
16
17#[derive(Clone, Copy)]
19pub struct ProductionControl<'a> {
20 budget: Option<&'a MemoryBudget>,
21 original: Option<&'a CancellationToken>,
22 invoking: Option<&'a CancellationToken>,
23}
24
25impl<'a> ProductionControl<'a> {
26 pub const fn uncontrolled() -> Self {
27 Self {
28 budget: None,
29 original: None,
30 invoking: None,
31 }
32 }
33
34 pub fn new(
35 budget: &'a MemoryBudget,
36 original: &'a CancellationToken,
37 invoking: &'a CancellationToken,
38 ) -> Self {
39 Self {
40 budget: Some(budget),
41 original: Some(original),
42 invoking: Some(invoking),
43 }
44 }
45
46 pub fn budget(&self) -> Option<&'a MemoryBudget> {
47 self.budget
48 }
49
50 pub fn check_cancellation(&self) -> Result<(), crate::QueryCancelled> {
51 if let Some(original) = self.original {
52 original.check()?;
53 }
54 if let Some(invoking) = self.invoking {
55 invoking.check()?;
56 }
57 Ok(())
58 }
59
60 pub fn check(&self) -> Result<(), ValueRetentionError> {
61 self.check_cancellation().map_err(Into::into)
62 }
63
64 pub fn empty_reservation(&self) -> Option<MemoryReservation> {
65 self.budget.map(MemoryBudget::empty_reservation)
66 }
67
68 pub fn reserve(&self, bytes: usize) -> Result<Option<MemoryReservation>, ValueRetentionError> {
70 self.check()?;
71 self.budget
72 .map(|budget| budget.reserve(bytes).map_err(Into::into))
73 .transpose()
74 }
75
76 pub fn combine(
78 &self,
79 left: Option<MemoryReservation>,
80 right: Option<MemoryReservation>,
81 ) -> Option<MemoryReservation> {
82 self.assert_owner(left.as_ref());
83 self.assert_owner(right.as_ref());
84 match (left, right) {
85 (Some(mut left), Some(right)) => {
86 left.absorb(right);
87 Some(left)
88 }
89 (None, None) => None,
90 _ => unreachable!("production ownership modes were checked"),
91 }
92 }
93
94 pub fn finish<T>(
96 &self,
97 value: T,
98 memory: Option<MemoryReservation>,
99 ) -> Result<Produced<T>, ValueRetentionError> {
100 let output = Produced { value, memory };
101 self.assert_owner(output.memory.as_ref());
102 self.check()?;
103 Ok(output)
104 }
105
106 pub fn retain_external_value(
108 &self,
109 value: Value,
110 ) -> Result<Produced<Value>, ValueRetentionError> {
111 let mut output = Produced {
112 value,
113 memory: self.empty_reservation(),
114 };
115 self.check()?;
116 if let Some(budget) = self.budget {
117 let memory = output
118 .memory
119 .as_mut()
120 .expect("controlled external value owner");
121 output.value.visit_retained_payload_with_check(
122 budget,
123 || self.check_cancellation(),
124 |bytes| memory.grow(bytes),
125 )?;
126 }
127 self.check()?;
128 Ok(output)
129 }
130
131 pub fn copy_value(&self, value: &Value) -> Result<Produced<Value>, ValueRetentionError> {
132 self.check()?;
133 let result = match self.budget {
134 Some(budget) => Produced::from(value.clone_budgeted_with_check(budget, || {
135 if let Some(original) = self.original {
136 original.check()?;
137 }
138 if let Some(invoking) = self.invoking {
139 invoking.check()?;
140 }
141 Ok(())
142 })?),
143 None => Produced {
144 value: value.clone(),
145 memory: None,
146 },
147 };
148 self.check()?;
149 Ok(result)
150 }
151
152 pub fn copy_text(&self, text: &str) -> Result<Produced<String>, ValueRetentionError> {
153 self.check()?;
154 let mut output = ProductionString::new(*self);
155 output.reserve(text.len())?;
156 output.push_str(text)?;
157 output.finish()
158 }
159
160 pub fn format(
161 &self,
162 arguments: std::fmt::Arguments<'_>,
163 ) -> Result<Produced<String>, ValueRetentionError> {
164 use std::fmt::Write;
165 self.check()?;
166 let mut output = ProductionString::new(*self);
167 let formatted = output.write_fmt(arguments);
168 let result = output.finish()?;
169 assert!(
170 formatted.is_ok(),
171 "formatter failed without a production error"
172 );
173 Ok(result)
174 }
175
176 fn assert_owner(&self, memory: Option<&MemoryReservation>) {
177 match (self.budget, memory) {
178 (Some(budget), Some(memory)) => assert!(
179 budget.shares_allowance(memory.budget()),
180 "different production allowance"
181 ),
182 (None, None) => {}
183 _ => panic!("controlled and uncontrolled production ownership must not be mixed"),
184 }
185 }
186}
187
188#[derive(Debug)]
190pub struct Produced<T> {
191 value: T,
192 memory: Option<MemoryReservation>,
193}
194
195impl<T> Produced<T> {
196 pub fn reserved_bytes(&self) -> usize {
197 self.memory.as_ref().map_or(0, MemoryReservation::bytes)
198 }
199
200 pub fn into_parts(self) -> (T, Option<MemoryReservation>) {
202 (self.value, self.memory)
203 }
204
205 pub fn into_uncontrolled(self) -> Result<T, Self> {
207 if self.memory.is_some() {
208 return Err(self);
209 }
210 Ok(self.value)
211 }
212
213 pub fn into_budgeted(self) -> Result<Budgeted<T>, Self> {
215 let Some(memory) = self.memory else {
216 return Err(self);
217 };
218 Ok(Budgeted::new(self.value, memory))
219 }
220}
221
222impl<T: Copy> Produced<Vec<T>> {
223 pub fn as_mut_slice(&mut self) -> &mut [T] {
225 self.value.as_mut_slice()
226 }
227}
228
229impl<T> From<Budgeted<T>> for Produced<T> {
230 fn from(value: Budgeted<T>) -> Self {
231 let (value, memory) = value.into_parts();
232 Self {
233 value,
234 memory: Some(memory),
235 }
236 }
237}
238
239impl<T> std::ops::Deref for Produced<T> {
240 type Target = T;
241 fn deref(&self) -> &T {
242 &self.value
243 }
244}
245
246#[cfg(test)]
247mod tests;