Skip to main content

uqa_core/memory/
production.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Ordinary and admitted producers share constructors while output and temporary leases stay separate.
8
9use super::{Budgeted, MemoryBudget, MemoryReservation};
10use crate::{CancellationToken, Value, ValueRetentionError};
11
12mod string;
13mod vec;
14pub use string::ProductionString;
15pub use vec::ProductionVec;
16
17/// Allocation and cancellation inputs borrowed for one production call. An ordinary call has no allowance or cancellation scope; a controlled call preserves both the original retained owner and the invoking reader.
18#[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    /// Admit an explicitly known allocation layout before its constructor runs.
69    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    /// Transfer two leases for allocations already owned by the same composite result. This does not produce, copy or replace a value.
77    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    /// Associate a value with leases acquired by its constructors. Every owned allocation must already belong to the supplied reservation. The explicit pair prevents a representation-changing closure from silently introducing new payloads; callers must construct and admit those payloads at their owner.
95    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    /// Accept a completed value from an external producer whose construction is outside this resource scope. This admission boundary retains its exposed owned capacities without copying; built-in constructors must instead reserve before allocating. The value precedes its accumulating lease on every error path.
107    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/// A produced value with its existing controlled lease, or an explicit ordinary result. There is no mutable dereference or generic map operation that could replace it with newly allocated, unadmitted payloads.
189#[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    /// Transfer the explicit value/lease pair to its next allocation owner. The caller must retain the lease until those allocations are freed or handed off under a documented legacy API boundary.
201    pub fn into_parts(self) -> (T, Option<MemoryReservation>) {
202        (self.value, self.memory)
203    }
204
205    /// Extract an ordinary result. A controlled result is returned unchanged instead of silently releasing its lease.
206    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    /// Preserve a controlled result in the existing budgeted carrier. An ordinary result is returned unchanged instead of being retroactively charged.
214    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    /// Mutate fixed-length scratch slots without changing capacity or introducing owned payloads. The Copy bound excludes allocation-owning values and the slice cannot grow the admitted container.
224    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;