msrt 0.1.2

Portable MSRT protocol implementation.
Documentation
//! Tick-driven retransmission.

use crate::engine::{
    Engine, EngineOutput, MAX_IN_FLIGHT_PACKETS, SendFailedEvent, SendFailureReason, WriteEvent,
};

pub(crate) fn tick(engine: &mut Engine, now_ms: u64) {
    engine.now_ms = now_ms;
    engine
        .reassembly
        .expire(now_ms, engine.reassembly_timeout_ms);

    let mut retransmits = [None; MAX_IN_FLIGHT_PACKETS];
    let mut retransmit_len = 0;
    let mut failures = [None; MAX_IN_FLIGHT_PACKETS];
    let mut failure_len = 0;
    let mut failed_messages = [None; MAX_IN_FLIGHT_PACKETS];
    let mut failed_message_len = 0;

    for packet in engine.in_flight.packets() {
        if now_ms.saturating_sub(packet.last_sent_ms) < engine.retransmit_timeout_ms {
            continue;
        }

        if packet.attempts >= engine.max_retransmit_attempts {
            let already_failed = failed_messages[..failed_message_len]
                .iter()
                .flatten()
                .any(|key| *key == (packet.channel_id, packet.message_id));

            if !already_failed && failure_len < failures.len() {
                failures[failure_len] = Some(*packet);
                failure_len += 1;
                failed_messages[failed_message_len] = Some((packet.channel_id, packet.message_id));
                failed_message_len += 1;
            }
        } else if retransmit_len < retransmits.len() {
            retransmits[retransmit_len] = Some(*packet);
            retransmit_len += 1;
        }
    }

    for packet in failures[..failure_len].iter().flatten() {
        engine
            .in_flight
            .remove_message(packet.channel_id, packet.message_id);
        let _ = engine
            .events
            .push(EngineOutput::SendFailed(SendFailedEvent {
                channel_id: packet.channel_id,
                message_id: packet.message_id,
                reason: SendFailureReason::RetryLimitReached,
            }));
    }

    for packet in retransmits[..retransmit_len].iter().flatten() {
        let message_failed = failed_messages[..failed_message_len]
            .iter()
            .flatten()
            .any(|key| *key == (packet.channel_id, packet.message_id));

        if message_failed {
            continue;
        }

        engine
            .in_flight
            .note_retransmit(packet.packet_number, now_ms);
        let _ = engine.events.push(EngineOutput::Write(WriteEvent {
            packet_number: packet.packet_number,
            bytes: packet.bytes,
            len: packet.len,
        }));
    }
}