use alloc::vec::Vec;
use core::fmt;
use crate::allocation::{AllocationContext, RequestedCapacity, try_push, try_reserve_total_exact};
use crate::bytes::{RuntimeByte, RuntimeInputByte, RuntimeInputByteCount, RuntimeStateByteCount};
use crate::error::{RunAdmissionError, RuntimeInputError};
use crate::limits::{ExecutionLimits, RuntimeInputLimits};
use crate::runtime::budget::RuntimeBudgetState;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RuntimeInputSource<'input> {
bytes: &'input [u8],
}
impl<'input> RuntimeInputSource<'input> {
#[must_use]
pub const fn from_bytes(bytes: &'input [u8]) -> Self {
Self { bytes }
}
#[must_use]
pub const fn as_bytes(self) -> &'input [u8] {
self.bytes
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.bytes.is_empty()
}
}
#[derive(PartialEq, Eq)]
pub struct RuntimeInput {
bytes: Vec<RuntimeByte>,
}
impl fmt::Debug for RuntimeInput {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("RuntimeInput")
.field("bytes", &RuntimeInputDebugBytes(self))
.finish()
}
}
struct RuntimeInputDebugBytes<'input>(&'input RuntimeInput);
impl fmt::Debug for RuntimeInputDebugBytes<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_list()
.entries(self.0.materialized_bytes())
.finish()
}
}
impl RuntimeInput {
pub fn validate(
input: RuntimeInputSource<'_>,
limits: RuntimeInputLimits,
) -> Result<Self, RuntimeInputError> {
let byte_count = RuntimeInputByteCount::new(input.as_bytes().len());
if !limits.input_byte_limit().accepts(byte_count) {
return Err(RuntimeInputError::input_limit(
limits.input_byte_limit(),
byte_count,
));
}
let mut bytes = Vec::new();
try_reserve_total_exact(
&mut bytes,
RequestedCapacity::from_runtime_input_count(byte_count),
AllocationContext::RuntimeInputValidation,
)?;
for (zero_based_column, byte) in input.as_bytes().iter().copied().enumerate() {
try_push(
&mut bytes,
RuntimeInputByte::validate(byte, zero_based_column)?.into_runtime_byte(),
AllocationContext::RuntimeInputValidation,
)?;
}
Ok(Self { bytes })
}
pub(crate) fn materialized_bytes(&self) -> impl Iterator<Item = u8> + '_ {
self.bytes.iter().copied().map(RuntimeByte::materialize)
}
#[must_use]
pub fn byte_count(&self) -> RuntimeInputByteCount {
RuntimeInputByteCount::new(self.bytes.len())
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
fn into_runtime_bytes(self) -> Vec<RuntimeByte> {
self.bytes
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct RunSeed {
initial_state: InitialStateBytes,
budget: RuntimeBudgetState,
}
impl RunSeed {
pub fn admit(input: RuntimeInput, limits: ExecutionLimits) -> Result<Self, RunAdmissionError> {
let initial_state_len = RuntimeStateByteCount::from_runtime_input_count(input.byte_count());
if !limits.state_byte_limit().accepts(initial_state_len) {
return Err(RunAdmissionError::initial_state_limit(
limits.state_byte_limit(),
initial_state_len,
));
}
Ok(Self {
initial_state: InitialStateBytes {
bytes: input.into_runtime_bytes(),
},
budget: RuntimeBudgetState::new(limits),
})
}
pub(crate) fn into_runtime_parts(self) -> (InitialStateBytes, RuntimeBudgetState) {
(self.initial_state, self.budget)
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct InitialStateBytes {
bytes: Vec<RuntimeByte>,
}
impl InitialStateBytes {
pub(crate) fn into_runtime_bytes(self) -> Vec<RuntimeByte> {
self.bytes
}
}