hibana 0.9.2

Session-typed choreographic programming for no_std Rust protocols, inspired by affine MPST
Documentation
pub(crate) mod meta {
    /// Maximum number of effect rows stored in one lowering segment.
    pub(crate) const MAX_SEGMENT_EFFS: usize = 96;

    /// Maximum number of fixed lowering segments in one program image.
    pub(crate) const MAX_SEGMENTS: usize = 32;

    /// Total effect row capacity across all lowering segments.
    pub(crate) const MAX_EFF_NODES: usize = MAX_SEGMENTS * MAX_SEGMENT_EFFS;
}

/// Segmented effect position inside a projected local descriptor image.
///
/// The upper 16 bits identify the segment and the lower 16 bits identify the
/// segment-local effect offset. Application code never constructs this value;
/// runtime crates may inspect it as a compact stable descriptor id.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct EffIndex(u32);

impl EffIndex {
    pub(crate) const ZERO: Self = Self(0);

    #[inline(always)]
    pub(crate) const fn from_segment_offset(segment: u16, offset: u16) -> Self {
        if segment as usize >= meta::MAX_SEGMENTS || offset as usize >= meta::MAX_SEGMENT_EFFS {
            crate::invariant();
        }
        Self(((segment as u32) << 16) | offset as u32)
    }

    #[inline(always)]
    pub(crate) const fn from_dense_ordinal(idx: usize) -> Self {
        if idx >= meta::MAX_EFF_NODES {
            crate::invariant();
        }
        let segment = idx / meta::MAX_SEGMENT_EFFS;
        let offset = idx % meta::MAX_SEGMENT_EFFS;
        Self::from_segment_offset(segment as u16, offset as u16)
    }

    #[inline(always)]
    pub(crate) const fn segment(self) -> u16 {
        (self.0 >> 16) as u16
    }

    #[inline(always)]
    pub(crate) const fn offset(self) -> u16 {
        self.0 as u16
    }

    #[inline(always)]
    pub(crate) const fn dense_ordinal(self) -> usize {
        let segment = self.segment() as usize;
        let offset = self.offset() as usize;
        if segment >= meta::MAX_SEGMENTS || offset >= meta::MAX_SEGMENT_EFFS {
            crate::invariant();
        }
        segment * meta::MAX_SEGMENT_EFFS + offset
    }
}

impl core::fmt::Display for EffIndex {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}:{}", self.segment(), self.offset())
    }
}

#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum EffKind {
    Pure = 0,
    Atom = 1,
}

#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum EventOrigin {
    User = 0,
    Session = 1,
}

impl EventOrigin {
    #[inline(always)]
    pub(crate) const fn from_packed_bits(bits: u8) -> Self {
        match bits {
            0 => Self::User,
            1 => Self::Session,
            _ => crate::invariant(),
        }
    }

    #[inline(always)]
    pub(crate) const fn packed_bits(self) -> u8 {
        match self {
            Self::User => 0,
            Self::Session => 1,
        }
    }

    #[inline(always)]
    pub(crate) const fn is_session(self) -> bool {
        matches!(self, Self::Session)
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct EffAtom {
    pub(crate) from: u8,
    pub(crate) to: u8,
    pub(crate) label: u8,
    pub(crate) origin: EventOrigin,
    /// Type-level lane for parallel composition; lane 0 is the primary lane.
    pub(crate) lane: u8,
}

impl EffAtom {
    pub(crate) const ZERO: Self = Self {
        from: 0,
        to: 0,
        label: 0,
        origin: EventOrigin::User,
        lane: 0,
    };
}

#[repr(transparent)]
#[derive(Clone, Copy)]
pub(crate) struct EffData {
    atom: EffAtom,
}

impl EffData {
    pub(crate) const fn empty() -> Self {
        Self {
            atom: EffAtom::ZERO,
        }
    }

    pub(crate) const fn from_atom(atom: EffAtom) -> Self {
        Self { atom }
    }

    #[inline(always)]
    pub(crate) const fn atom(&self) -> EffAtom {
        self.atom
    }
}

#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct EffStruct {
    pub(crate) kind: EffKind,
    pub(crate) data: EffData,
}

impl EffStruct {
    pub(crate) const fn pure() -> Self {
        Self {
            kind: EffKind::Pure,
            data: EffData::empty(),
        }
    }

    pub(crate) const fn atom(atom: EffAtom) -> Self {
        Self {
            kind: EffKind::Atom,
            data: EffData::from_atom(atom),
        }
    }

    #[inline(always)]
    pub(crate) const fn atom_data(&self) -> EffAtom {
        match self.kind {
            EffKind::Pure => crate::invariant(),
            EffKind::Atom => self.data.atom(),
        }
    }
}

impl core::fmt::Debug for EffStruct {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self.kind {
            EffKind::Pure => f.debug_struct("EffStruct::Pure").finish(),
            EffKind::Atom => f
                .debug_struct("EffStruct::Atom")
                .field("atom", &self.atom_data())
                .finish(),
        }
    }
}

impl PartialEq for EffStruct {
    fn eq(&self, other: &Self) -> bool {
        if self.kind != other.kind {
            return false;
        }
        match self.kind {
            EffKind::Pure => true,
            EffKind::Atom => self.atom_data() == other.atom_data(),
        }
    }
}

impl Eq for EffStruct {}

#[cfg(test)]
mod tests {
    use super::{EffIndex, EffStruct};

    #[test]
    fn eff_index_checked_constructor_packs_valid_segment_and_offset() {
        let idx = EffIndex::from_segment_offset(2, 42);

        assert_eq!(idx.segment(), 2);
        assert_eq!(idx.offset(), 42);
        assert_eq!(idx, EffIndex((2u32 << 16) | 42));
    }

    #[test]
    #[should_panic]
    fn invalid_eff_index_constructor_panics_at_construction() {
        let _ = EffIndex::from_segment_offset(super::meta::MAX_SEGMENTS as u16, 0);
    }

    #[test]
    #[should_panic]
    fn pure_effect_atom_data_fails_fast() {
        let _ = EffStruct::pure().atom_data();
    }

    #[test]
    fn eff_index_from_dense_ordinal_stays_segment_zero() {
        let idx = EffIndex::from_dense_ordinal(42);

        assert_eq!(idx.segment(), 0);
        assert_eq!(idx.offset(), 42);
        assert_eq!(idx.dense_ordinal(), 42);
    }

    #[test]
    fn eff_index_display_is_always_segmented() {
        let first = EffIndex::from_dense_ordinal(42);
        let second = EffIndex::from_dense_ordinal(super::meta::MAX_SEGMENT_EFFS + 7);

        assert_eq!(std::format!("{first}"), "0:42");
        assert_eq!(std::format!("{second}"), "1:7");
    }

    #[test]
    fn eff_index_from_dense_ordinal_crosses_segment_boundary() {
        let idx = EffIndex::from_dense_ordinal(super::meta::MAX_SEGMENT_EFFS + 7);

        assert_eq!(idx.segment(), 1);
        assert_eq!(idx.offset(), 7);
        assert_eq!(idx.dense_ordinal(), super::meta::MAX_SEGMENT_EFFS + 7);
    }
}