hibana 0.9.6

Choreography-derived runtime enforcement kernel for no_std Rust multiparty protocols
Documentation
use super::parallel_exit_for_enter;
use crate::global::{
    const_dsl::{EffList, ScopeKind},
    role_program::PackedLaneRange,
};

struct LocalStepCursor<'a, const E: usize> {
    eff_list: &'a EffList<E>,
    role: u8,
    eff_index: usize,
    local_step: usize,
}

impl<'a, const E: usize> LocalStepCursor<'a, E> {
    const fn new(eff_list: &'a EffList<E>, role: u8) -> Self {
        Self {
            eff_list,
            role,
            eff_index: 0,
            local_step: 0,
        }
    }

    const fn advance_to(&mut self, end: usize) {
        let limit = if end < self.eff_list.len() {
            end
        } else {
            self.eff_list.len()
        };
        while self.eff_index < limit {
            let atom = self.eff_list.atom_at(self.eff_index);
            if atom.from == self.role || atom.to == self.role {
                self.local_step += 1;
            }
            self.eff_index += 1;
        }
    }

    const fn range(&mut self, start: usize, end: usize) -> PackedLaneRange {
        if start >= end {
            return PackedLaneRange::new(0, 0);
        }
        if start < self.eff_index {
            crate::invariant();
        }
        self.advance_to(start);
        let local_start = self.local_step;
        self.advance_to(end);
        let local_len = self.local_step - local_start;
        if local_len == 0 {
            PackedLaneRange::new(0, 0)
        } else {
            PackedLaneRange::new(local_start, local_len)
        }
    }
}

pub(in crate::global::role_program::image_impl) struct ResidentRowCursor<'a, const E: usize> {
    eff_list: &'a EffList<E>,
    marker_index: usize,
    current_eff: usize,
    pending: Option<PackedLaneRange>,
    emitted_rows: usize,
    finished: bool,
    local: LocalStepCursor<'a, E>,
}

impl<'a, const E: usize> ResidentRowCursor<'a, E> {
    pub(in crate::global::role_program::image_impl) const fn new(
        eff_list: &'a EffList<E>,
        role: u8,
    ) -> Self {
        Self {
            eff_list,
            marker_index: 0,
            current_eff: 0,
            pending: None,
            emitted_rows: 0,
            finished: false,
            local: LocalStepCursor::new(eff_list, role),
        }
    }

    const fn emit(&mut self, row: PackedLaneRange) -> Option<PackedLaneRange> {
        self.emitted_rows += 1;
        Some(row)
    }

    pub(in crate::global::role_program::image_impl) const fn next(
        &mut self,
    ) -> Option<PackedLaneRange> {
        if let Some(row) = self.pending {
            self.pending = None;
            return self.emit(row);
        }
        let markers = self.eff_list.scope_markers();
        while self.marker_index < markers.len() {
            let marker_index = self.marker_index;
            let marker = markers.at(marker_index);
            self.marker_index += 1;
            if !marker.event.is_primary_enter()
                || !matches!(marker.scope_id.kind(), Some(ScopeKind::Parallel))
            {
                continue;
            }
            let exit_eff = parallel_exit_for_enter(markers, marker_index);
            let before = self.local.range(self.current_eff, marker.offset());
            let parallel_start = if marker.offset() > self.current_eff {
                marker.offset()
            } else {
                self.current_eff
            };
            let parallel = self.local.range(parallel_start, exit_eff);
            if exit_eff > self.current_eff {
                self.current_eff = exit_eff;
            }
            let before_present = !before.is_absent_or_zero_len();
            let parallel_present = !parallel.is_absent_or_zero_len();
            if before_present {
                if parallel_present {
                    self.pending = Some(parallel);
                }
                return self.emit(before);
            }
            if parallel_present {
                return self.emit(parallel);
            }
        }
        if !self.finished {
            self.finished = true;
            let trailing = self.local.range(self.current_eff, self.eff_list.len());
            if !trailing.is_absent_or_zero_len() {
                return self.emit(trailing);
            }
            if self.emitted_rows == 0 && self.local.local_step != 0 {
                crate::invariant();
            }
        }
        None
    }
}