hibana 0.9.6

Choreography-derived runtime enforcement kernel for no_std Rust multiparty protocols
Documentation
use super::{ScopeId, ScopeKind, ScopeMarkerView};

mod nesting;
pub(crate) use nesting::{StructuredScopeRange, innermost_scope_range, outermost_scope_range};

mod route;
pub(crate) use route::{
    closed_route_arm_ranges_from_first_enter, passive_route_child_scope,
    route_arm_event_ranges_for_scope, route_arm_ranges_from_first_enter,
    route_parent_arm_for_scope, route_scope_slot_for_scope, structured_scope_event_range,
};

#[cfg(kani)]
mod kani;

#[cfg(all(test, hibana_repo_tests))]
mod tests;

pub(crate) const fn route_enter_at(
    markers: ScopeMarkerView<'_>,
    start: usize,
    end: usize,
    marker_floor: usize,
) -> Option<usize> {
    let mut idx = marker_floor;
    while idx < markers.len() {
        let marker = markers.at(idx);
        if marker.offset() > start {
            break;
        }
        if marker.offset() == start
            && marker.event.is_primary_enter()
            && matches!(marker.scope_id.kind(), Some(ScopeKind::Route))
            && markers.is_first_enter(idx)
            && route_exits_within(markers, idx, end)
        {
            return Some(idx);
        }
        idx += 1;
    }
    None
}

const fn route_exits_within(markers: ScopeMarkerView<'_>, enter_idx: usize, end: usize) -> bool {
    match closed_route_arm_ranges_from_first_enter(markers, enter_idx) {
        Some([_, (_, arm1_end)]) => arm1_end <= end,
        None => false,
    }
}

pub(crate) const fn parallel_enter_at(
    markers: ScopeMarkerView<'_>,
    start: usize,
    end: usize,
    marker_floor: usize,
) -> Option<usize> {
    let mut idx = marker_floor;
    while idx < markers.len() {
        let marker = markers.at(idx);
        if marker.offset() > start {
            break;
        }
        if marker.offset() == start
            && marker.event.is_primary_enter()
            && matches!(marker.scope_id.kind(), Some(ScopeKind::Parallel))
            && parallel_exits_within(markers, idx, end)
        {
            return Some(idx);
        }
        idx += 1;
    }
    None
}

const fn parallel_exits_within(markers: ScopeMarkerView<'_>, enter_idx: usize, end: usize) -> bool {
    let Some((_, _, _, right_end)) = parallel_arm_ranges_from_enter(markers, enter_idx) else {
        return false;
    };
    right_end <= end
}

pub(crate) const fn roll_body_range_from_enter(
    scope_markers: ScopeMarkerView<'_>,
    enter_idx: usize,
) -> Option<(usize, usize)> {
    if enter_idx >= scope_markers.len() {
        return None;
    }
    let marker = scope_markers.at(enter_idx);
    if !marker.event.is_enter() || !matches!(marker.scope_id.kind(), Some(ScopeKind::Roll)) {
        return None;
    }
    let end = marker.segment_end();
    if end <= marker.offset() {
        None
    } else {
        Some((marker.offset(), end))
    }
}

pub(crate) const fn scope_segment_end_from_enter(
    scope_markers: ScopeMarkerView<'_>,
    enter_idx: usize,
    segment_limit: Option<usize>,
) -> usize {
    if enter_idx >= scope_markers.len() {
        panic!("scope enter marker index out of bounds");
    }
    let marker = scope_markers.at(enter_idx);
    if !marker.event.is_enter() {
        panic!("scope segment boundary requires an enter marker");
    }
    let end = marker.segment_end();
    if end <= marker.offset() || matches!(segment_limit, Some(limit) if end > limit) {
        crate::invariant();
    }
    end
}

pub(crate) const fn roll_continuation_end(
    scope_markers: ScopeMarkerView<'_>,
    roll_enter_idx: usize,
    roll_end: usize,
    eff_len: usize,
) -> usize {
    let roll_start = scope_markers.at(roll_enter_idx).offset();
    let mut boundary = eff_len;
    let mut idx = 0usize;
    while idx < roll_enter_idx {
        let marker = scope_markers.at(idx);
        if marker.event.is_enter()
            && marker.offset() <= roll_start
            && let Some(candidate) = scope_boundary_for_enter(scope_markers, idx, roll_start)
            && candidate > roll_end
            && candidate < boundary
        {
            boundary = candidate;
        }
        idx += 1;
    }
    boundary
}

const fn scope_boundary_for_enter(
    scope_markers: ScopeMarkerView<'_>,
    enter_idx: usize,
    contained_offset: usize,
) -> Option<usize> {
    let marker = scope_markers.at(enter_idx);
    match marker.scope_id.kind() {
        Some(ScopeKind::Parallel) => {
            let Some((_, left_end, _, right_end)) =
                parallel_arm_ranges_from_enter(scope_markers, enter_idx)
            else {
                return None;
            };
            if contained_offset < left_end {
                Some(left_end)
            } else {
                Some(right_end)
            }
        }
        Some(ScopeKind::Route) => route_arm_exit_from_enter(scope_markers, enter_idx),
        Some(ScopeKind::Roll) => match roll_body_range_from_enter(scope_markers, enter_idx) {
            Some((_, end)) => Some(end),
            None => None,
        },
        None => None,
    }
}

const fn route_arm_exit_from_enter(
    scope_markers: ScopeMarkerView<'_>,
    enter_idx: usize,
) -> Option<usize> {
    if enter_idx >= scope_markers.len() {
        return None;
    }
    let marker = scope_markers.at(enter_idx);
    if !marker.event.is_enter() || !matches!(marker.scope_id.kind(), Some(ScopeKind::Route)) {
        return None;
    }
    let end = marker.segment_end();
    if end <= marker.offset() {
        None
    } else {
        Some(end)
    }
}

pub(crate) const fn parallel_arm_ranges_from_enter(
    scope_markers: ScopeMarkerView<'_>,
    enter_idx: usize,
) -> Option<(usize, usize, usize, usize)> {
    if enter_idx >= scope_markers.len() {
        return None;
    }
    let marker = scope_markers.at(enter_idx);
    if !marker.event.is_primary_enter()
        || !matches!(marker.scope_id.kind(), Some(ScopeKind::Parallel))
    {
        return None;
    }
    let Some(split) = marker.event.parallel_split() else {
        return None;
    };
    let exit = marker.segment_end();
    if marker.offset() >= split || split >= exit {
        None
    } else {
        Some((marker.offset(), split, split, exit))
    }
}