hibana 0.8.0

Const-projected Affine Multiparty Session Types for choreography-first Rust protocols
Documentation
//! Projected control-descriptor metadata helpers.

use crate::{
    control::cap::mint::ControlOp,
    eff::EffIndex,
    global::{
        ControlDesc,
        compiled::{images::DynamicPolicySite, lowering::CompiledProgramImage},
        const_dsl::{ControlScopeKind, PolicyMode},
    },
};

#[inline(always)]
pub(crate) const fn lane_open_tap_event_id() -> u16 {
    0x0100
}

#[inline(always)]
pub(crate) const fn control_op_tap_event_id(op: ControlOp) -> u16 {
    use crate::observe::ids;
    match op {
        ControlOp::RouteDecision => ids::ROUTE_DECISION,
        ControlOp::LoopContinue | ControlOp::LoopBreak => ids::LOOP_DECISION,
        ControlOp::StateSnapshot => ids::STATE_SNAPSHOT_REQ,
        ControlOp::StateRestore => ids::STATE_RESTORE_REQ,
        ControlOp::TopologyBegin => ids::TOPOLOGY_BEGIN,
        ControlOp::TopologyAck => ids::TOPOLOGY_ACK,
        ControlOp::TopologyCommit => ids::TOPOLOGY_COMMIT,
        ControlOp::AbortBegin => ids::ABORT_BEGIN,
        ControlOp::AbortAck => ids::ABORT_ACK,
        ControlOp::Fence => ids::POLICY_RA_OK,
        ControlOp::TxCommit => ids::POLICY_COMMIT,
        ControlOp::TxAbort => ids::POLICY_TX_ABORT,
    }
}

#[derive(Clone, Copy)]
struct ControlScopeIter {
    mask: u8,
    next: u8,
}

impl ControlScopeIter {
    #[inline(always)]
    const fn new(mask: u8) -> Self {
        Self { mask, next: 0 }
    }
}

impl Iterator for ControlScopeIter {
    type Item = ControlScopeKind;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        while self.next < 6 {
            let bit = 1u8 << self.next;
            let scope_kind = match self.next {
                0 => ControlScopeKind::Loop,
                1 => ControlScopeKind::State,
                2 => ControlScopeKind::Abort,
                3 => ControlScopeKind::Topology,
                4 => ControlScopeKind::Policy,
                5 => ControlScopeKind::Route,
                _ => unreachable!(),
            };
            self.next += 1;
            if self.mask & bit != 0 {
                return Some(scope_kind);
            }
        }
        None
    }
}

#[derive(Clone, Copy)]
pub(crate) struct EffectEnvelopeRef<'a> {
    image: &'a CompiledProgramImage,
}

impl<'a> EffectEnvelopeRef<'a> {
    #[inline(always)]
    pub(crate) const fn from_program_image(image: &'a CompiledProgramImage) -> Self {
        Self { image }
    }

    #[inline(always)]
    pub(crate) fn resources(&self) -> ResourceIter<'a> {
        ResourceIter {
            inner: ProgramImageResourceIter::new(self.image),
        }
    }

    #[inline(always)]
    pub(crate) fn resource_policy(&self, descriptor: &ResourceDescriptor) -> PolicyMode {
        let policy_site = descriptor.control.policy_site();
        if policy_site == ResourceDescriptor::STATIC_POLICY_SITE {
            PolicyMode::Static
        } else {
            ProgramImageDynamicPolicySiteIter::new(self.image)
                .nth(policy_site as usize)
                .map(|site| site.policy())
                .unwrap_or(PolicyMode::Static)
        }
    }

    #[inline(always)]
    pub(crate) fn control_scopes(&self) -> impl Iterator<Item = ControlScopeKind> {
        ControlScopeIter::new(self.image.compiled_program_control_scope_mask())
    }
}

pub(crate) struct ResourceIter<'a> {
    inner: ProgramImageResourceIter<'a>,
}

impl Iterator for ResourceIter<'_> {
    type Item = ResourceDescriptor;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }
}

pub(crate) struct ProgramImageDynamicPolicySiteIter<'a> {
    image: &'a CompiledProgramImage,
    offset: usize,
}

impl<'a> ProgramImageDynamicPolicySiteIter<'a> {
    #[inline(always)]
    pub(crate) const fn new(image: &'a CompiledProgramImage) -> Self {
        Self { image, offset: 0 }
    }
}

impl Iterator for ProgramImageDynamicPolicySiteIter<'_> {
    type Item = DynamicPolicySite;

    fn next(&mut self) -> Option<Self::Item> {
        let view = self.image.view();
        while self.offset < view.len() {
            let offset = self.offset;
            self.offset += 1;
            let Some(policy) = view.policy_at(offset) else {
                continue;
            };
            if !policy.is_dynamic() {
                continue;
            }
            let node = view.node_at(offset);
            if !matches!(node.kind, crate::eff::EffKind::Atom) {
                continue;
            }
            let atom = node.atom_data();
            let control = view.control_desc_at(offset);
            return Some(DynamicPolicySite::new(
                EffIndex::from_dense_ordinal(offset),
                atom.label,
                atom.resource,
                control.map(ControlDesc::op),
                policy,
            ));
        }
        None
    }
}

pub(crate) struct ProgramImageResourceIter<'a> {
    image: &'a CompiledProgramImage,
    offset: usize,
    dynamic_policy_site_len: u16,
}

impl<'a> ProgramImageResourceIter<'a> {
    #[inline(always)]
    const fn new(image: &'a CompiledProgramImage) -> Self {
        Self {
            image,
            offset: 0,
            dynamic_policy_site_len: 0,
        }
    }
}

impl Iterator for ProgramImageResourceIter<'_> {
    type Item = ResourceDescriptor;

    fn next(&mut self) -> Option<Self::Item> {
        let view = self.image.view();
        while self.offset < view.len() {
            let offset = self.offset;
            self.offset += 1;
            let node = view.node_at(offset);
            let policy = view.policy_at(offset).unwrap_or(PolicyMode::Static);
            let resource_policy_site = if policy.is_dynamic() {
                let site = self.dynamic_policy_site_len;
                self.dynamic_policy_site_len = self.dynamic_policy_site_len.saturating_add(1);
                site
            } else {
                ResourceDescriptor::STATIC_POLICY_SITE
            };
            if !matches!(node.kind, crate::eff::EffKind::Atom) {
                continue;
            }
            let atom = node.atom_data();
            if !atom.is_control {
                continue;
            }
            let resource_kind_tag = atom
                .resource
                .expect("control atom must carry a resource tag");
            let control_desc = view
                .control_desc_at(offset)
                .expect("control atom missing control descriptor");
            if control_desc.resource_tag() != resource_kind_tag {
                panic!("control atom/control descriptor mismatch");
            }
            return Some(ResourceDescriptor::new(control_desc.with_sites(
                EffIndex::from_dense_ordinal(offset),
                resource_policy_site,
            )));
        }
        None
    }
}

/// Metadata describing a projected control descriptor.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ResourceDescriptor {
    control: ControlDesc,
}

impl ResourceDescriptor {
    pub(crate) const STATIC_POLICY_SITE: u16 = ControlDesc::STATIC_POLICY_SITE;

    #[inline(always)]
    pub(crate) const fn new(control: ControlDesc) -> Self {
        Self { control }
    }

    #[inline(always)]
    pub(crate) const fn eff_index(&self) -> EffIndex {
        self.control.eff_index()
    }

    #[inline(always)]
    pub(crate) const fn tag(&self) -> u8 {
        self.control.resource_tag()
    }
}

#[cfg(all(test, hibana_repo_tests))]
#[path = "effects/tests.rs"]
mod tests;