net-wire 0.1.0

Allocation-free no_std wire views and codecs for network protocols.
Documentation
use std::error::Error;

use net_wire::kcp::{
    KCP_SEGMENT_HEADER_LEN, KcpCommand, KcpConversationId, KcpFragment, KcpSegment, KcpSegmentMut,
    KcpSegmentParseError, KcpSequenceNumber, KcpTimestamp, KcpUnacknowledged,
};

use super::fixtures::SEGMENT;

#[test]
fn segment_parsing_preserves_exact_fields_and_suffix() {
    let bytes = [
        0x44, 0x33, 0x22, 0x11, 0x51, 0x02, 0x66, 0x55, 0xaa, 0x99, 0x88, 0x77, 0xee, 0xdd, 0xcc,
        0xbb, 0x67, 0x45, 0x23, 0x01, 0x03, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xca,
    ];
    let (segment, suffix) = KcpSegment::parse(&bytes).unwrap();
    assert_eq!(segment.as_bytes(), SEGMENT);
    assert_eq!(suffix, [0xef, 0xca]);
    assert_eq!(segment.conversation_id().raw(), 0x1122_3344);
    assert_eq!(segment.command(), KcpCommand::PUSH);
    assert_eq!(segment.fragment().raw(), 2);
    assert_eq!(segment.window_size(), 0x5566);
    assert_eq!(segment.timestamp().raw(), 0x7788_99aa);
    assert_eq!(segment.sequence_number().raw(), 0xbbcc_ddee);
    assert_eq!(segment.unacknowledged().raw(), 0x0123_4567);
    assert_eq!(segment.payload_length(), 3);
    assert_eq!(segment.payload(), [0xde, 0xad, 0xbe]);
}

#[test]
fn segment_accepts_zero_payload_and_raw_unknown_fields() {
    let zero = [
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0,
    ];
    let (segment, suffix) = KcpSegment::parse(&zero).unwrap();
    assert!(suffix.is_empty());
    assert_eq!(segment.conversation_id().raw(), u32::MAX);
    assert_eq!(segment.command().raw(), u8::MAX);
    assert_eq!(segment.command().known(), None);
    assert_eq!(segment.fragment().raw(), u8::MAX);
    assert_eq!(segment.window_size(), u16::MAX);
    assert_eq!(segment.timestamp().raw(), u32::MAX);
    assert_eq!(segment.sequence_number().raw(), u32::MAX);
    assert_eq!(segment.unacknowledged().raw(), u32::MAX);
    assert_eq!(segment.payload_length(), 0);
    assert!(segment.payload().is_empty());
}

#[test]
fn segment_reports_all_truncation_boundaries() {
    for available in 0..KCP_SEGMENT_HEADER_LEN {
        assert_eq!(
            KcpSegment::parse(&SEGMENT[..available]),
            Err(KcpSegmentParseError::HeaderTruncated {
                required: KCP_SEGMENT_HEADER_LEN,
                available
            })
        );
    }
    assert_eq!(
        KcpSegment::parse(&SEGMENT[..26]),
        Err(KcpSegmentParseError::PayloadTruncated {
            required: SEGMENT.len(),
            available: 26
        })
    );
    let error = KcpSegment::parse(&SEGMENT[..26]).unwrap_err();
    assert!(Error::source(&error).is_none());
}

#[test]
fn mutable_segment_updates_fields_and_payload_without_changing_boundary() {
    let mut bytes = [
        0x44, 0x33, 0x22, 0x11, 0x51, 0x02, 0x66, 0x55, 0xaa, 0x99, 0x88, 0x77, 0xee, 0xdd, 0xcc,
        0xbb, 0x67, 0x45, 0x23, 0x01, 0x03, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xca,
    ];
    let (mut segment, suffix) = KcpSegmentMut::parse(&mut bytes).unwrap();
    assert_eq!(suffix, [0xef, 0xca]);
    segment.set_conversation_id(KcpConversationId::new(1));
    segment.set_command(KcpCommand::ACK);
    segment.set_fragment(KcpFragment::new(0));
    segment.set_window_size(2);
    segment.set_timestamp(KcpTimestamp::new(3));
    segment.set_sequence_number(KcpSequenceNumber::new(4));
    segment.set_unacknowledged(KcpUnacknowledged::new(5));
    segment.payload_mut().copy_from_slice(&[0xbe, 0xef, 0xca]);
    assert_eq!(segment.payload_length(), 3);
    assert_eq!(segment.payload(), [0xbe, 0xef, 0xca]);
    assert_eq!(segment.as_bytes().len(), SEGMENT.len());
    assert_eq!(suffix, [0xef, 0xca]);
    assert_eq!(
        segment.as_bytes(),
        [
            1, 0, 0, 0, 82, 0, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 0xbe, 0xef,
            0xca
        ]
    );
}