use super::{Budgeted, MemoryBudget, MemoryReservation};
use crate::{CancellationToken, Value, ValueRetentionError};
mod string;
mod vec;
pub use string::ProductionString;
pub use vec::ProductionVec;
#[derive(Clone, Copy)]
pub struct ProductionControl<'a> {
budget: Option<&'a MemoryBudget>,
original: Option<&'a CancellationToken>,
invoking: Option<&'a CancellationToken>,
}
impl<'a> ProductionControl<'a> {
pub const fn uncontrolled() -> Self {
Self {
budget: None,
original: None,
invoking: None,
}
}
pub fn new(
budget: &'a MemoryBudget,
original: &'a CancellationToken,
invoking: &'a CancellationToken,
) -> Self {
Self {
budget: Some(budget),
original: Some(original),
invoking: Some(invoking),
}
}
pub fn budget(&self) -> Option<&'a MemoryBudget> {
self.budget
}
pub fn check_cancellation(&self) -> Result<(), crate::QueryCancelled> {
if let Some(original) = self.original {
original.check()?;
}
if let Some(invoking) = self.invoking {
invoking.check()?;
}
Ok(())
}
pub fn check(&self) -> Result<(), ValueRetentionError> {
self.check_cancellation().map_err(Into::into)
}
pub fn empty_reservation(&self) -> Option<MemoryReservation> {
self.budget.map(MemoryBudget::empty_reservation)
}
pub fn reserve(&self, bytes: usize) -> Result<Option<MemoryReservation>, ValueRetentionError> {
self.check()?;
self.budget
.map(|budget| budget.reserve(bytes).map_err(Into::into))
.transpose()
}
pub fn combine(
&self,
left: Option<MemoryReservation>,
right: Option<MemoryReservation>,
) -> Option<MemoryReservation> {
self.assert_owner(left.as_ref());
self.assert_owner(right.as_ref());
match (left, right) {
(Some(mut left), Some(right)) => {
left.absorb(right);
Some(left)
}
(None, None) => None,
_ => unreachable!("production ownership modes were checked"),
}
}
pub fn finish<T>(
&self,
value: T,
memory: Option<MemoryReservation>,
) -> Result<Produced<T>, ValueRetentionError> {
let output = Produced { value, memory };
self.assert_owner(output.memory.as_ref());
self.check()?;
Ok(output)
}
pub fn retain_external_value(
&self,
value: Value,
) -> Result<Produced<Value>, ValueRetentionError> {
let mut output = Produced {
value,
memory: self.empty_reservation(),
};
self.check()?;
if let Some(budget) = self.budget {
let memory = output
.memory
.as_mut()
.expect("controlled external value owner");
output.value.visit_retained_payload_with_check(
budget,
|| self.check_cancellation(),
|bytes| memory.grow(bytes),
)?;
}
self.check()?;
Ok(output)
}
pub fn copy_value(&self, value: &Value) -> Result<Produced<Value>, ValueRetentionError> {
self.check()?;
let result = match self.budget {
Some(budget) => Produced::from(value.clone_budgeted_with_check(budget, || {
if let Some(original) = self.original {
original.check()?;
}
if let Some(invoking) = self.invoking {
invoking.check()?;
}
Ok(())
})?),
None => Produced {
value: value.clone(),
memory: None,
},
};
self.check()?;
Ok(result)
}
pub fn copy_text(&self, text: &str) -> Result<Produced<String>, ValueRetentionError> {
self.check()?;
let mut output = ProductionString::new(*self);
output.reserve(text.len())?;
output.push_str(text)?;
output.finish()
}
pub fn format(
&self,
arguments: std::fmt::Arguments<'_>,
) -> Result<Produced<String>, ValueRetentionError> {
use std::fmt::Write;
self.check()?;
let mut output = ProductionString::new(*self);
let formatted = output.write_fmt(arguments);
let result = output.finish()?;
assert!(
formatted.is_ok(),
"formatter failed without a production error"
);
Ok(result)
}
fn assert_owner(&self, memory: Option<&MemoryReservation>) {
match (self.budget, memory) {
(Some(budget), Some(memory)) => assert!(
budget.shares_allowance(memory.budget()),
"different production allowance"
),
(None, None) => {}
_ => panic!("controlled and uncontrolled production ownership must not be mixed"),
}
}
}
#[derive(Debug)]
pub struct Produced<T> {
value: T,
memory: Option<MemoryReservation>,
}
impl<T> Produced<T> {
pub fn reserved_bytes(&self) -> usize {
self.memory.as_ref().map_or(0, MemoryReservation::bytes)
}
pub fn into_parts(self) -> (T, Option<MemoryReservation>) {
(self.value, self.memory)
}
pub fn into_uncontrolled(self) -> Result<T, Self> {
if self.memory.is_some() {
return Err(self);
}
Ok(self.value)
}
pub fn into_budgeted(self) -> Result<Budgeted<T>, Self> {
let Some(memory) = self.memory else {
return Err(self);
};
Ok(Budgeted::new(self.value, memory))
}
}
impl<T: Copy> Produced<Vec<T>> {
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.value.as_mut_slice()
}
}
impl<T> From<Budgeted<T>> for Produced<T> {
fn from(value: Budgeted<T>) -> Self {
let (value, memory) = value.into_parts();
Self {
value,
memory: Some(memory),
}
}
}
impl<T> std::ops::Deref for Produced<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
#[cfg(test)]
mod tests;