use alloc::vec::Vec;
use core::fmt;
use crate::allocation::{AllocationContext, RequestedCapacity, try_push, try_reserve_total_exact};
use crate::bytes::{RuntimeByte, RuntimeInputByteCount, RuntimeStateByteCount};
use crate::error::{RunError, RuntimeInputError};
use crate::program::RuntimeInputByteLimit;
use super::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 byte_count.get() > limit.get() {
return Err(RuntimeInputError::limit(limit, byte_count));
}
for (zero_based_column, byte) in source.as_bytes().iter().copied().enumerate() {
RuntimeByte::validate_input_boundary(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
}
}
#[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<'_>,
limit: RuntimeInputByteLimit,
) -> Result<Self, RuntimeInputError> {
let input = ValidatedRuntimeInputSource::new(input, limit)?;
let mut bytes = Vec::new();
try_reserve_total_exact(
&mut bytes,
RequestedCapacity::new(input.byte_count().get()),
AllocationContext::RuntimeInputValidation,
)?;
for byte in input.bytes().iter().copied() {
try_push(
&mut bytes,
RuntimeByte::from_validated_input(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()
}
pub(crate) fn into_runtime_bytes(self) -> Vec<RuntimeByte> {
self.bytes
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct InitialStateBytes {
bytes: Vec<RuntimeByte>,
}
impl InitialStateBytes {
pub(crate) fn from_runtime_input(
input: RuntimeInput,
budget: RuntimeBudgetState,
) -> Result<Self, RunError> {
let byte_count = input.byte_count();
let state_len = RuntimeStateByteCount::from_runtime_input_count(byte_count);
budget.ensure_initial_state_len(state_len)?;
Ok(Self {
bytes: input.into_runtime_bytes(),
})
}
pub(crate) fn into_runtime_bytes(self) -> Vec<RuntimeByte> {
self.bytes
}
}