use alloc::vec::Vec;
use crate::allocation::{
AllocationContext, AllocationError, RequestedCapacity, try_push, try_reserve_total_exact,
};
use crate::bytes::{RuntimeByte, RuntimeStateByteCount, TraceSnapshotByteCount};
use crate::error::TraceSnapshotError;
use crate::inspect::RuleView;
use crate::limits::{StepCount, TraceSnapshotByteLimit};
use crate::program::{ReturnOutput, ReturnOutputView, RuntimeStateSnapshot};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct RuntimeStateView<'run> {
bytes: &'run [RuntimeByte],
}
impl<'run> RuntimeStateView<'run> {
pub(crate) const fn new(bytes: &'run [RuntimeByte]) -> Self {
Self { bytes }
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.bytes.is_empty()
}
pub(crate) fn materialized_bytes(self) -> impl Iterator<Item = u8> + 'run {
self.bytes.iter().copied().map(RuntimeByte::materialize)
}
#[must_use]
pub const fn byte_count(self) -> RuntimeStateByteCount {
RuntimeStateByteCount::new(self.bytes.len())
}
pub(crate) fn to_vec_with_context(
self,
context: AllocationContext,
) -> Result<Vec<u8>, AllocationError> {
let mut output = Vec::new();
try_reserve_total_exact(
&mut output,
RequestedCapacity::from_runtime_state_count(self.byte_count()),
context,
)?;
for byte in self.materialized_bytes() {
try_push(&mut output, byte, context)?;
}
Ok(output)
}
pub fn materialize(self) -> Result<RuntimeStateSnapshot, AllocationError> {
Ok(RuntimeStateSnapshot::from_materialized(
self.to_vec_with_context(AllocationContext::RuntimeStateView)?,
))
}
}
impl core::fmt::Debug for RuntimeStateView<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list()
.entries((*self).materialized_bytes())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraceEffect<State, Output> {
Continue {
state: State,
},
Return {
output: Output,
},
}
pub type BorrowedTraceEffect<'program, 'run> =
TraceEffect<RuntimeStateView<'run>, ReturnOutputView<'program>>;
pub type TraceSnapshotEffect = TraceEffect<RuntimeStateSnapshot, ReturnOutput>;
impl TraceEffect<RuntimeStateView<'_>, ReturnOutputView<'_>> {
#[must_use]
pub fn byte_count(self) -> TraceSnapshotByteCount {
match self {
Self::Continue { state } => {
TraceSnapshotByteCount::from_runtime_state_count(state.byte_count())
}
Self::Return { output } => {
TraceSnapshotByteCount::from_return_output_count(output.byte_count())
}
}
}
#[must_use]
pub fn is_empty(self) -> bool {
match self {
Self::Continue { state } => state.is_empty(),
Self::Return { output } => output.is_empty(),
}
}
fn to_snapshot(
self,
limit: TraceSnapshotByteLimit,
) -> Result<TraceSnapshotEffect, TraceSnapshotError> {
ensure_trace_len(self.byte_count(), limit)?;
match self {
Self::Continue { state } => Ok(TraceSnapshotEffect::Continue {
state: RuntimeStateSnapshot::from_materialized(
state.to_vec_with_context(AllocationContext::TraceSnapshot)?,
),
}),
Self::Return { output } => Ok(TraceSnapshotEffect::Return {
output: ReturnOutput::from_materialized(
output.to_vec_with_context(AllocationContext::TraceSnapshot)?,
),
}),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraceEvent<'program, State, Effect> {
Initial {
state: State,
},
Step {
step: StepCount,
rule: RuleView<'program>,
effect: Effect,
},
}
pub type BorrowedTraceEvent<'program, 'run> =
TraceEvent<'program, RuntimeStateView<'run>, BorrowedTraceEffect<'program, 'run>>;
pub type TraceSnapshotEvent<'program> =
TraceEvent<'program, RuntimeStateSnapshot, TraceSnapshotEffect>;
impl<'program> TraceEvent<'program, RuntimeStateView<'_>, BorrowedTraceEffect<'program, '_>> {
#[must_use]
pub fn byte_count(self) -> TraceSnapshotByteCount {
match self {
Self::Initial { state } => {
TraceSnapshotByteCount::from_runtime_state_count(state.byte_count())
}
Self::Step { effect, .. } => effect.byte_count(),
}
}
#[must_use]
pub fn is_empty(self) -> bool {
match self {
Self::Initial { state } => state.is_empty(),
Self::Step { effect, .. } => effect.is_empty(),
}
}
pub fn to_snapshot(
self,
limit: TraceSnapshotByteLimit,
) -> Result<TraceSnapshotEvent<'program>, TraceSnapshotError> {
match self {
Self::Initial { state } => {
ensure_trace_len(
TraceSnapshotByteCount::from_runtime_state_count(state.byte_count()),
limit,
)?;
Ok(TraceSnapshotEvent::Initial {
state: RuntimeStateSnapshot::from_materialized(
state.to_vec_with_context(AllocationContext::TraceSnapshot)?,
),
})
}
Self::Step { step, rule, effect } => Ok(TraceSnapshotEvent::Step {
step,
rule,
effect: effect.to_snapshot(limit)?,
}),
}
}
}
fn ensure_trace_len(
len: TraceSnapshotByteCount,
limit: TraceSnapshotByteLimit,
) -> Result<(), TraceSnapshotError> {
if !limit.accepts(len) {
return Err(TraceSnapshotError::Limit {
limit,
attempted_len: len,
});
}
Ok(())
}