frame-core 0.3.0

Component model, lifecycle, process isolation — hosts components as supervised BEAM process trees
Documentation
//! Deriving mailbox acknowledgement integers from identifier bytes.

use beamr::term::Term;

use super::error::RuntimeError;

/// FNV-1a 64-bit offset basis.
const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;

/// FNV-1a 64-bit prime.
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;

/// Folds identifier bytes into a valid mailbox integer above a reserved
/// range.
///
/// A component's mailbox speaks integers, and its message protocol usually
/// claims the low ones (liveness, stop, news). When the host must send an
/// acknowledgement derived from an identifier — a stored entity id, for
/// example — the derivation has to land inside the BEAM small-integer term
/// payload AND above the protocol's reserved values. This fold is that
/// derivation, absorbed from the generated composition host's hand-rolled
/// `Term::SMALL_INT_MAX` arithmetic (finding B6): deterministic FNV-1a over
/// EVERY input byte (the hand-rolled version truncated to the first eight),
/// reduced into `reserved_below ..= Term::SMALL_INT_MAX`.
///
/// `reserved_below` states the caller's protocol reservation: every integer
/// strictly below it is reserved and will never be returned. The returned
/// value is deterministic for identical inputs.
///
/// # Errors
///
/// Refuses a `reserved_below` outside `0..=Term::SMALL_INT_MAX` — a negative
/// reservation is meaningless for a derived acknowledgement, and a bound past
/// the small-integer maximum leaves no valid value to fold into. Never a
/// clamp, never a silent wrap.
pub fn mailbox_integer(bytes: &[u8], reserved_below: i64) -> Result<i64, RuntimeError> {
    let max = Term::SMALL_INT_MAX;
    let refusal = RuntimeError::MailboxReservedRangeInvalid {
        reserved_below,
        max,
    };
    if !(0..=max).contains(&reserved_below) {
        return Err(refusal);
    }
    let mut fold = FNV_OFFSET_BASIS;
    for byte in bytes {
        fold ^= u64::from(*byte);
        fold = fold.wrapping_mul(FNV_PRIME);
    }
    // With 0 <= reserved_below <= max proven above, every conversion below is
    // infallible by construction; each arm still surfaces the typed refusal
    // rather than assuming, so no failure path is ever silent.
    let Ok(span) = u64::try_from(max - reserved_below) else {
        return Err(refusal);
    };
    let Some(size) = span.checked_add(1) else {
        return Err(refusal);
    };
    let Ok(offset) = i64::try_from(fold % size) else {
        return Err(refusal);
    };
    let Some(value) = reserved_below.checked_add(offset) else {
        return Err(refusal);
    };
    Ok(value)
}

#[cfg(test)]
mod tests {
    use super::{FNV_OFFSET_BASIS, FNV_PRIME, mailbox_integer};
    use beamr::term::Term;

    #[test]
    fn empty_input_folds_the_offset_basis_into_the_full_domain()
    -> Result<(), Box<dyn std::error::Error>> {
        let size = u64::try_from(Term::SMALL_INT_MAX)?
            .checked_add(1)
            .ok_or("size overflow")?;
        assert_eq!(
            mailbox_integer(b"", 0)?,
            i64::try_from(FNV_OFFSET_BASIS % size)?
        );
        Ok(())
    }

    #[test]
    fn single_byte_matches_the_fnv1a_step() -> Result<(), Box<dyn std::error::Error>> {
        let folded = (FNV_OFFSET_BASIS ^ u64::from(b'x')).wrapping_mul(FNV_PRIME);
        let size = u64::try_from(Term::SMALL_INT_MAX)?
            .checked_add(1)
            .ok_or("size overflow")?;
        assert_eq!(mailbox_integer(b"x", 0)?, i64::try_from(folded % size)?);
        Ok(())
    }
}