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, RuntimeInputByteLimit, 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()
}
}
struct ValidatedRuntimeInputSource<'input> {
source: RuntimeInputSource<'input>,
byte_count: RuntimeInputByteCount,
}
impl<'input> ValidatedRuntimeInputSource<'input> {
fn new(
source: RuntimeInputSource<'input>,
limit: RuntimeInputByteLimit,
) -> Result<Self, RuntimeInputError> {
let byte_count = RuntimeInputByteCount::new(source.as_bytes().len());
if !limit.accepts(byte_count) {
return Err(RuntimeInputError::input_limit(limit, byte_count));
}
for (zero_based_column, byte) in source.as_bytes().iter().copied().enumerate() {
RuntimeInputByte::validate(byte, zero_based_column)?;
}
Ok(Self { source, byte_count })
}
const fn bytes(&self) -> &'input [u8] {
self.source.as_bytes()
}
const fn byte_count(&self) -> RuntimeInputByteCount {
self.byte_count
}
fn runtime_input_bytes(
&self,
) -> impl Iterator<Item = Result<RuntimeInputByte, RuntimeInputError>> + 'input {
self.bytes().iter().copied().map(|byte| {
RuntimeInputByte::from_validated_ascii(byte)
.map_err(RuntimeInputError::internal_invariant)
})
}
}
#[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 input = ValidatedRuntimeInputSource::new(input, limits.input_byte_limit())?;
let mut bytes = Vec::new();
try_reserve_total_exact(
&mut bytes,
RequestedCapacity::from_runtime_input_count(input.byte_count()),
AllocationContext::RuntimeInputValidation,
)?;
for byte in input.runtime_input_bytes() {
try_push(
&mut bytes,
byte?.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
}
}