hibana 0.9.6

Choreography-derived runtime enforcement kernel for no_std Rust multiparty protocols
Documentation
use crate::{
    eff::EffAtom,
    global::const_dsl::{
        EffList, ReentryMark, ScopeId, ScopeKind, color_roll_frame_labels, merge_parallel_lanes,
        merge_route_frame_labels,
    },
};

#[derive(Clone, Copy)]
pub(crate) enum SourceRouteResolver {
    Intrinsic,
    Dynamic(u16),
}

#[derive(Clone, Copy)]
pub(crate) enum ProgramSourceNode {
    Send(EffAtom),
    Seq {
        left: &'static Self,
        right: &'static Self,
    },
    Route {
        left: &'static Self,
        right: &'static Self,
        resolver: SourceRouteResolver,
    },
    Parallel {
        left: &'static Self,
        right: &'static Self,
    },
    Roll(&'static Self),
}

pub(crate) trait ProgramShape {
    const SOURCE_NODE: ProgramSourceNode;
    const EVENT_COUNT: usize;
    const SCOPE_MARKER_COUNT: usize;
    const RESOLVER_MARKER_COUNT: usize;

    const SOURCE_ROW_COUNT: usize = checked_source_count(
        Self::EVENT_COUNT,
        Self::SCOPE_MARKER_COUNT,
        Self::RESOLVER_MARKER_COUNT,
    );
}

pub(crate) struct ProgramSourceData<const CAPACITY: usize> {
    eff: EffList<CAPACITY>,
}

struct SourceLowering<const CAPACITY: usize> {
    eff: EffList<CAPACITY>,
    next_scope_ordinal: u16,
}

impl<const CAPACITY: usize> SourceLowering<CAPACITY> {
    const fn new(event_count: usize, scope_count: usize, resolver_count: usize) -> Self {
        Self {
            eff: EffList::new_partitioned(event_count, scope_count, resolver_count),
            next_scope_ordinal: 0,
        }
    }

    const fn allocate_scope(&mut self, kind: ScopeKind) -> ScopeId {
        if self.next_scope_ordinal >= ScopeId::LOCAL_CAPACITY {
            panic!("structured scope domain exceeded");
        }
        let ordinal = self.next_scope_ordinal;
        self.next_scope_ordinal += 1;
        ScopeId::new(kind, ordinal)
    }

    const fn emit(&mut self, node: &ProgramSourceNode, route_reentry: ReentryMark) -> u16 {
        match node {
            ProgramSourceNode::Send(atom) => {
                self.eff.push_event_mut(*atom);
                1
            }
            ProgramSourceNode::Seq { left, right } => {
                let left_span = self.emit(left, route_reentry);
                let right_span = self.emit(right, route_reentry);
                max_lane_span(left_span, right_span)
            }
            ProgramSourceNode::Route {
                left,
                right,
                resolver,
            } => {
                let scope = self.allocate_scope(ScopeKind::Route);
                let left_start = self.eff.len();
                let left_span = self.emit(left, route_reentry);
                let right_start = self.eff.len();
                let right_span = self.emit(right, route_reentry);
                let right_end = self.eff.len();
                self.eff.push_route_scope_mut(
                    scope,
                    left_start,
                    right_start,
                    right_end,
                    route_reentry,
                );
                merge_route_frame_labels(&mut self.eff, left_start, right_start, right_end);
                if let SourceRouteResolver::Dynamic(resolver_id) = *resolver {
                    self.eff.push_route_resolver_mut(scope, resolver_id);
                }
                max_lane_span(left_span, right_span)
            }
            ProgramSourceNode::Parallel { left, right } => {
                let scope = self.allocate_scope(ScopeKind::Parallel);
                let left_start = self.eff.len();
                let left_span = self.emit(left, route_reentry);
                let right_start = self.eff.len();
                let right_span = self.emit(right, route_reentry);
                let right_end = self.eff.len();
                self.eff
                    .push_parallel_scope_mut(scope, left_start, right_start, right_end);
                merge_parallel_lanes(
                    &mut self.eff,
                    left_start,
                    right_start,
                    right_end,
                    left_span,
                    right_span,
                )
            }
            ProgramSourceNode::Roll(inner) => {
                let scope = self.allocate_scope(ScopeKind::Roll);
                let start = self.eff.len();
                let lane_span = self.emit(inner, ReentryMark::Reentrant);
                let end = self.eff.len();
                self.eff.push_roll_scope_mut(scope, start, end);
                color_roll_frame_labels(&mut self.eff, start, end);
                lane_span
            }
        }
    }
}

impl<const CAPACITY: usize> ProgramSourceData<CAPACITY> {
    pub(crate) const fn lower<Steps>() -> Self
    where
        Steps: ProgramShape,
    {
        if Steps::SOURCE_ROW_COUNT == 0 || Steps::SOURCE_ROW_COUNT > CAPACITY {
            panic!("source bucket selection");
        }
        let mut lowering = SourceLowering::new(
            Steps::EVENT_COUNT,
            Steps::SCOPE_MARKER_COUNT,
            Steps::RESOLVER_MARKER_COUNT,
        );
        let _ = lowering.emit(&Steps::SOURCE_NODE, ReentryMark::SinglePass);
        if lowering.eff.len() != Steps::EVENT_COUNT
            || lowering.eff.scope_marker_count() != Steps::SCOPE_MARKER_COUNT
            || lowering.eff.resolver_marker_count() != Steps::RESOLVER_MARKER_COUNT
        {
            panic!("type tree and lowered source disagree");
        }
        Self { eff: lowering.eff }
    }

    #[inline(always)]
    pub(crate) const fn eff_list(&self) -> &EffList<CAPACITY> {
        &self.eff
    }
}

const fn max_lane_span(lhs: u16, rhs: u16) -> u16 {
    if lhs >= rhs { lhs } else { rhs }
}

pub(crate) const fn checked_source_count(lhs: usize, rhs: usize, added: usize) -> usize {
    let Some(partial) = lhs.checked_add(rhs) else {
        panic!("choreography source count overflow");
    };
    let Some(sum) = partial.checked_add(added) else {
        panic!("choreography source count overflow");
    };
    sum
}