use super::{
CompiledProgramRef, CompiledRoleImage, ControlSemanticKind, DENSE_LANE_NONE, DenseLaneOrdinal,
EffIndex, EffKind, EndpointArenaLayout, LaneSetView, LaneSteps, LocalAtomFacts, LocalNode,
LocalNodeMeta, PhaseRouteGuard, PolicyMode, ScopeEvent, ScopeId, ScopeKind, ScopeRegion,
StateIndex, first_enter_for_scope, same_scope,
};
mod route_scope;
#[derive(Clone, Copy)]
pub(crate) struct RoleDescriptorRef {
program: CompiledProgramRef,
resident: &'static CompiledRoleImage,
}
impl core::fmt::Debug for RoleDescriptorRef {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RoleDescriptorRef")
.field("program", &self.program)
.field("role", &self.role())
.finish()
}
}
impl PartialEq for RoleDescriptorRef {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
if self.program != other.program || self.role() != other.role() {
return false;
}
core::ptr::eq(self.resident, other.resident)
}
}
impl Eq for RoleDescriptorRef {}
impl RoleDescriptorRef {
#[inline(always)]
pub(crate) const fn from_resident(compiled: &'static CompiledRoleImage) -> Self {
Self {
program: compiled.program(),
resident: compiled,
}
}
#[inline(always)]
pub(crate) const fn program(&self) -> CompiledProgramRef {
self.program
}
#[inline(always)]
const fn resident(&self) -> &'static CompiledRoleImage {
self.resident
}
#[inline(always)]
fn footprint(&self) -> crate::global::role_program::RoleFootprint {
self.resident.footprint()
}
#[inline(always)]
fn endpoint_layout_footprint(&self) -> crate::global::role_program::RoleFootprint {
self.footprint()
}
#[inline(always)]
pub(crate) fn role(&self) -> u8 {
self.resident.role()
}
#[inline(always)]
pub(crate) fn phase_lane_set(&self, idx: usize) -> Option<LaneSetView<'static>> {
self.resident().role_image().phase_lane_set(idx)
}
#[inline(always)]
pub(crate) fn phase_min_start(&self, idx: usize) -> Option<u16> {
self.resident().role_image().phase_min_start(idx)
}
#[inline(always)]
pub(crate) fn phase_route_guard(&self, idx: usize) -> Option<PhaseRouteGuard> {
let _ = idx;
None
}
#[inline(always)]
pub(crate) fn phase_lane_steps(&self, idx: usize, lane_idx: usize) -> Option<LaneSteps> {
if lane_idx >= self.logical_lane_count() {
return None;
}
self.resident().role_image().phase_lane_steps(idx, lane_idx)
}
#[inline(always)]
pub(crate) fn phase_lane_step_at(
&self,
idx: usize,
lane_idx: usize,
ordinal: usize,
) -> Option<u16> {
if lane_idx >= self.logical_lane_count() {
return None;
}
self.resident()
.role_image()
.phase_lane_step_at(idx, lane_idx, ordinal)
}
#[inline(always)]
pub(crate) fn phase_lane_step_ordinal(
&self,
idx: usize,
lane_idx: usize,
step_idx: usize,
) -> Option<u16> {
if lane_idx >= self.logical_lane_count() {
return None;
}
self.resident()
.role_image()
.phase_lane_step_ordinal(idx, lane_idx, step_idx)
}
#[inline(always)]
pub(crate) fn local_len(&self) -> usize {
self.resident.footprint().local_step_count
}
#[inline(always)]
pub(crate) fn node_len(&self) -> usize {
self.local_len().saturating_add(1)
}
#[inline(always)]
pub(crate) fn checked_node(&self, idx: usize) -> Option<LocalNode> {
if idx >= self.node_len() {
return None;
}
Some(self.node(idx))
}
#[inline(always)]
pub(crate) fn node(&self, idx: usize) -> LocalNode {
let compiled = self.resident();
let role = compiled.role();
self.resident_node(role, compiled, idx)
}
fn resident_node(&self, role: u8, compiled: &CompiledRoleImage, idx: usize) -> LocalNode {
let local_len = self.local_len();
if idx >= local_len {
return LocalNode::terminal(StateIndex::from_usize(local_len));
}
let (eff_idx, action_ordinal) = self
.resident_eff_for_step(role, compiled, idx)
.expect("resident local step index must resolve to an effect");
let view = compiled.program_image().view();
let eff = view.node_at(eff_idx);
let atom = eff.atom_data();
let scope = self.resident_scope_at(compiled, eff_idx);
let policy = match view.policy_at(eff_idx) {
Some(policy) => policy.with_scope(scope),
None => PolicyMode::Static,
};
let control_desc = if atom.is_control {
view.control_desc_at(eff_idx)
} else {
None
};
let semantic = ControlSemanticKind::from_control_desc(control_desc);
let shot = control_desc.map(|desc| desc.shot());
let resource = atom.resource;
let frame_label = self.resident_frame_label_at(compiled, eff_idx);
let route_scope_and_arm = self.resident_route_scope_and_arm_at(compiled, eff_idx);
let route_arm = route_scope_and_arm.map(|(_, arm)| arm);
let next = StateIndex::from_usize(action_ordinal.saturating_add(1));
let eff_index = EffIndex::from_dense_ordinal(eff_idx);
let facts = LocalAtomFacts {
eff_index,
label: atom.label,
frame_label,
resource,
is_control: atom.is_control,
shot,
policy,
lane: atom.lane,
};
let meta = |is_choice_determinant| LocalNodeMeta {
semantic,
next,
scope,
route_arm,
is_choice_determinant,
};
if atom.from == role && atom.to == role {
LocalNode::local(facts, meta(false))
} else if atom.from == role {
LocalNode::send(atom.to, facts, meta(false))
} else {
LocalNode::recv(
atom.from,
facts,
meta(route_scope_and_arm.is_some_and(|(route_scope, arm)| {
self.resident_first_recv_eff_for_route_arm(role, compiled, route_scope, arm)
== Some(eff_idx)
})),
)
}
}
fn resident_eff_for_step(
&self,
role: u8,
compiled: &CompiledRoleImage,
target_step: usize,
) -> Option<(usize, usize)> {
let view = compiled.program_image().view();
let mut step = 0usize;
let mut idx = 0usize;
while idx < view.len() {
let node = view.node_at(idx);
if matches!(node.kind, EffKind::Atom) {
let atom = node.atom_data();
if atom.from == role || atom.to == role {
if step == target_step {
return Some((idx, step));
}
step += 1;
}
}
idx += 1;
}
None
}
fn resident_step_for_eff(
&self,
role: u8,
compiled: &CompiledRoleImage,
target_eff: usize,
) -> Option<usize> {
let view = compiled.program_image().view();
let mut step = 0usize;
let mut idx = 0usize;
while idx < view.len() {
let node = view.node_at(idx);
if matches!(node.kind, EffKind::Atom) {
let atom = node.atom_data();
if atom.from == role || atom.to == role {
if idx == target_eff {
return Some(step);
}
step += 1;
}
}
idx += 1;
}
None
}
#[inline(always)]
pub(crate) fn has_active_lane(&self, lane_idx: usize) -> bool {
if lane_idx >= self.logical_lane_count() {
return false;
}
self.resident()
.role_image()
.active_lane_set()
.contains(lane_idx)
}
#[inline(always)]
pub(crate) fn first_active_lane(&self) -> Option<usize> {
self.resident().role_image().first_active_lane()
}
#[inline(always)]
pub(crate) fn endpoint_lane_slot_count(&self) -> usize {
self.footprint().endpoint_lane_slot_count.max(1)
}
#[inline(always)]
pub(crate) fn logical_lane_count(&self) -> usize {
self.footprint()
.logical_lane_count
.max(self.endpoint_lane_slot_count())
}
#[inline(always)]
pub(crate) fn route_table_frame_slots(&self) -> usize {
if self.route_scope_count() == 0 {
0
} else {
self.footprint()
.active_lane_count
.saturating_mul(self.max_route_stack_depth().max(1))
}
}
#[inline(always)]
pub(crate) fn route_table_lane_slots(&self) -> usize {
if self.route_scope_count() == 0 {
0
} else {
self.endpoint_lane_slot_count()
}
}
#[inline(always)]
pub(crate) fn loop_table_slots(&self) -> usize {
self.endpoint_lane_slot_count()
.saturating_mul(self.footprint().passive_linger_route_scope_count)
}
#[inline(always)]
pub(crate) fn resident_cap_entries(&self) -> usize {
self.footprint().active_lane_count.saturating_mul(4).max(4)
}
#[cfg(test)]
#[inline(always)]
pub(crate) fn active_lane_count(&self) -> usize {
self.footprint().active_lane_count
}
#[inline(always)]
pub(crate) fn max_route_stack_depth(&self) -> usize {
self.footprint().max_route_stack_depth
}
#[cfg(test)]
#[inline(always)]
pub(crate) fn max_loop_stack_depth(&self) -> usize {
self.footprint().passive_linger_route_scope_count
}
#[inline(always)]
pub(crate) fn route_scope_count(&self) -> usize {
self.footprint().route_scope_count
}
#[inline(always)]
pub(crate) fn fill_active_lane_dense_by_lane(&self, dst: &mut [DenseLaneOrdinal]) -> usize {
dst.fill(DENSE_LANE_NONE);
let active = self.resident().role_image().active_lane_set();
let mut dense = 0usize;
let mut next = active.first_set(dst.len());
while let Some(lane_idx) = next {
dst[lane_idx] =
DenseLaneOrdinal::new(dense).expect("dense active lane ordinal fits u16");
dense += 1;
next = active.next_set_from(lane_idx.saturating_add(1), dst.len());
}
dense
}
#[inline(always)]
pub(crate) fn fill_logical_lane_dense_by_lane(&self, dst: &mut [DenseLaneOrdinal]) -> usize {
let logical_lane_count = self.logical_lane_count();
let mut lane_idx = 0usize;
while lane_idx < dst.len() {
dst[lane_idx] = if lane_idx < logical_lane_count {
DenseLaneOrdinal::new(lane_idx).expect("logical lane ordinal fits u16")
} else {
DENSE_LANE_NONE
};
lane_idx += 1;
}
core::cmp::min(logical_lane_count, dst.len())
}
#[inline(always)]
pub(crate) fn endpoint_arena_layout_for_binding(
&self,
binding_enabled: bool,
) -> EndpointArenaLayout {
EndpointArenaLayout::from_footprint_with_binding(
self.endpoint_layout_footprint(),
binding_enabled,
)
}
}