use super::{
EffList, EffStruct, MAX_CAPACITY, MAX_ROUTE_RESOLVER_MARKERS, MAX_SEGMENT_EFFS, MAX_SEGMENTS,
Message, ReentryMark, RouteResolver, RouteResolverMarker, ScopeEvent, ScopeId, ScopeKind,
ScopeMarker, eff,
};
#[derive(Clone, Copy)]
pub(crate) enum ScopeRebase {
Preserve,
MarkRouteEnters,
}
impl ScopeRebase {
const fn apply(
self,
current: ReentryMark,
scope_id: ScopeId,
event: ScopeEvent,
) -> ReentryMark {
match self {
Self::Preserve => current,
Self::MarkRouteEnters => {
if matches!(scope_id.kind(), Some(ScopeKind::Route))
&& matches!(event, ScopeEvent::Enter)
{
ReentryMark::Reentrant
} else {
current
}
}
}
}
}
impl EffList {
pub(crate) const fn new() -> Self {
Self {
segments: [[EffStruct::pure(); MAX_SEGMENT_EFFS]; MAX_SEGMENTS],
len: 0,
scope_budget: 0,
scope_markers: [ScopeMarker::empty(); MAX_CAPACITY],
scope_marker_len: 0,
resolver_markers: [RouteResolverMarker::empty(); MAX_ROUTE_RESOLVER_MARKERS],
resolver_marker_len: 0,
}
}
pub(crate) const fn len(&self) -> usize {
self.len
}
pub(crate) const fn scope_budget(&self) -> u16 {
self.scope_budget
}
pub(crate) const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
const fn segment_slot(offset: usize) -> (usize, usize) {
(offset / MAX_SEGMENT_EFFS, offset % MAX_SEGMENT_EFFS)
}
#[inline(always)]
pub(crate) const fn node_at(&self, offset: usize) -> EffStruct {
if offset >= self.len {
panic!("EffList node offset out of bounds");
}
let (segment, local) = Self::segment_slot(offset);
self.segments[segment][local]
}
pub(crate) const fn push(mut self, node: EffStruct) -> Self {
self.push_mut(node);
self
}
const fn push_mut(&mut self, node: EffStruct) {
if self.len >= MAX_CAPACITY {
panic!("EffList capacity exceeded");
}
let (segment, local) = Self::segment_slot(self.len);
self.segments[segment][local] = node;
self.len += 1;
}
pub(crate) const fn append_rebased_from(
&mut self,
other: &EffList,
lane_offset: u16,
scope_offset: u16,
scope_rebase: ScopeRebase,
) {
let mut idx = 0;
let base = self.len;
while idx < other.len {
let mut node = other.node_at(idx);
if lane_offset != 0 && matches!(node.kind, eff::EffKind::Atom) {
let mut atom = node.atom_data();
let lane = atom.lane as u16 + lane_offset;
if lane > u8::MAX as u16 {
panic!("projection internal lane overflow");
}
atom.lane = lane as u8;
node = EffStruct::atom(atom);
}
self.push_mut(node);
idx += 1;
}
let mut scope_idx = 0;
while scope_idx < other.scope_marker_len {
let marker = other.scope_markers[scope_idx];
let scope_id = marker.scope_id.add_ordinal(scope_offset);
let reentry = scope_rebase.apply(marker.reentry, scope_id, marker.event);
self.push_scope_marker_full_mut(
base + marker.offset(),
scope_id,
marker.event,
reentry,
);
scope_idx += 1;
}
let mut resolver_idx = 0;
while resolver_idx < other.resolver_marker_len {
let marker = other.resolver_markers[resolver_idx];
self.push_route_resolver_mut(
marker.scope.add_ordinal(scope_offset),
marker.resolver_id,
);
resolver_idx += 1;
}
}
pub(crate) const fn rebase_scopes_mut(&mut self, scope_offset: u16, scope_rebase: ScopeRebase) {
if scope_offset == 0 && matches!(scope_rebase, ScopeRebase::Preserve) {
return;
}
let mut scope_idx = 0usize;
while scope_idx < self.scope_marker_len {
let marker = self.scope_markers[scope_idx];
let scope_id = marker.scope_id.add_ordinal(scope_offset);
let reentry = scope_rebase.apply(marker.reentry, scope_id, marker.event);
self.scope_markers[scope_idx] =
ScopeMarker::new(marker.offset(), scope_id, marker.event, reentry);
scope_idx += 1;
}
let mut resolver_idx = 0usize;
while resolver_idx < self.resolver_marker_len {
let marker = self.resolver_markers[resolver_idx];
self.resolver_markers[resolver_idx] = RouteResolverMarker::new(
marker.scope.add_ordinal(scope_offset),
marker.resolver_id,
);
resolver_idx += 1;
}
if scope_offset != 0 {
let scope_budget = self.scope_budget as u32 + scope_offset as u32;
if scope_budget > ScopeId::LOCAL_CAPACITY as u32 {
panic!("scope ordinal overflow");
}
self.scope_budget = scope_budget as u16;
}
}
const fn push_scope_marker_full_mut(
&mut self,
offset: usize,
scope_id: ScopeId,
event: ScopeEvent,
reentry: ReentryMark,
) {
if self.scope_marker_len >= MAX_CAPACITY {
panic!("EffList scope marker capacity exceeded");
}
let ordinal = scope_id.local_ordinal();
let next = ordinal + 1;
if next > ScopeId::LOCAL_CAPACITY {
panic!("scope ordinal overflow");
}
if next > self.scope_budget {
self.scope_budget = next;
}
let mut idx = self.scope_marker_len;
while idx > 0 {
let prev = self.scope_markers[idx - 1];
if prev.offset() > offset {
self.scope_markers[idx] = prev;
idx -= 1;
} else {
break;
}
}
self.scope_markers[idx] = ScopeMarker::new(offset, scope_id, event, reentry);
self.scope_marker_len += 1;
}
const fn push_scope_marker_outer_enter_reentry_mut(
&mut self,
offset: usize,
scope: ScopeId,
reentry: ReentryMark,
) {
if self.scope_marker_len >= MAX_CAPACITY {
panic!("EffList scope marker capacity exceeded");
}
let ordinal = scope.local_ordinal();
let next = ordinal + 1;
if next > ScopeId::LOCAL_CAPACITY {
panic!("scope ordinal overflow");
}
if next > self.scope_budget {
self.scope_budget = next;
}
let mut idx = self.scope_marker_len;
while idx > 0 {
let prev = self.scope_markers[idx - 1];
if prev.offset() >= offset {
self.scope_markers[idx] = prev;
idx -= 1;
} else {
break;
}
}
self.scope_markers[idx] = ScopeMarker::new(offset, scope, ScopeEvent::Enter, reentry);
self.scope_marker_len += 1;
}
pub(crate) const fn push_scope_around(&mut self, start: usize, end: usize, scope: ScopeId) {
self.push_scope_marker_outer_enter_reentry_mut(start, scope, ReentryMark::SinglePass);
self.push_scope_marker_full_mut(end, scope, ScopeEvent::Exit, ReentryMark::SinglePass);
}
pub(crate) const fn push_scope_enter_at_boundary(&mut self, offset: usize, scope: ScopeId) {
self.push_scope_marker_full_mut(offset, scope, ScopeEvent::Enter, ReentryMark::SinglePass);
}
pub(crate) const fn push_scope_exit_at_boundary(&mut self, offset: usize, scope: ScopeId) {
self.push_scope_marker_full_mut(offset, scope, ScopeEvent::Exit, ReentryMark::SinglePass);
}
pub(crate) const fn push_parallel_scope_split(&mut self, scope: ScopeId, split: usize) {
if !matches!(scope.kind(), Some(ScopeKind::Parallel)) {
panic!("parallel split scope");
}
if split == 0 || split >= self.len {
panic!("parallel split must separate non-empty arms");
}
let len = self.len;
self.push_scope_marker_outer_enter_reentry_mut(0, scope, ReentryMark::SinglePass);
self.push_scope_marker_full_mut(split, scope, ScopeEvent::Split, ReentryMark::SinglePass);
self.push_scope_marker_full_mut(len, scope, ScopeEvent::Exit, ReentryMark::SinglePass);
}
pub(crate) const fn push_route_resolver_mut(&mut self, scope: ScopeId, resolver_id: u16) {
if !matches!(scope.kind(), Some(ScopeKind::Route)) {
panic!("EffList route resolver scope");
}
let mut idx = 0usize;
while idx < self.resolver_marker_len {
if self.resolver_markers[idx].scope.same(scope) {
self.resolver_markers[idx] = RouteResolverMarker::new(scope, resolver_id);
return;
}
idx += 1;
}
if self.resolver_marker_len >= MAX_ROUTE_RESOLVER_MARKERS {
panic!("EffList resolver marker capacity exceeded");
}
self.resolver_markers[self.resolver_marker_len] =
RouteResolverMarker::new(scope, resolver_id);
self.resolver_marker_len += 1;
}
pub(crate) const fn resolver_for_scope(&self, scope: ScopeId) -> Option<RouteResolver> {
if scope.is_none() {
return None;
}
let mut idx = 0usize;
while idx < self.resolver_marker_len {
let marker = self.resolver_markers[idx];
if marker.scope.same(scope) {
return Some(marker.resolver());
}
idx += 1;
}
None
}
pub(crate) const fn scope_marker_count(&self) -> usize {
self.scope_marker_len
}
pub(crate) const fn scope_marker_at(&self, idx: usize) -> ScopeMarker {
if idx >= self.scope_marker_len {
panic!("EffList scope marker index out of bounds");
}
self.scope_markers[idx]
}
pub(crate) const fn scope_markers(&self) -> &[ScopeMarker] {
unsafe { core::slice::from_raw_parts(self.scope_markers.as_ptr(), self.scope_marker_len) }
}
}
pub(crate) const fn const_send_typed<const FROM: u8, const TO: u8, M, const LANE: u8>() -> EffList
where
M: Message,
{
if let Some(message) = crate::g::role_pair_contract_error::<FROM, TO>() {
panic!("{}", message);
}
let atom = eff::EffAtom {
from: FROM,
to: TO,
label: <M as Message>::LOGICAL_LABEL,
origin: eff::EventOrigin::User,
lane: LANE,
};
EffList::new().push(EffStruct::atom(atom))
}