net-wire 0.1.0

Allocation-free no_std wire views and codecs for network protocols.
Documentation
use net_wire::qpack::{
    QPACK_INTEGER_MAX, QpackInteger, QpackIntegerBuildError, QpackIntegerBuilder,
    QpackIntegerParseError,
};

#[test]
fn rfc_integer_examples_are_exact_and_bounded() {
    let ten = [0x0a, 0xaa];
    assert_eq!(
        QpackInteger::parse(&ten, 5).map(|integer| (integer.value(), integer.as_bytes())),
        Ok((10, &ten[..1]))
    );
    let thirteen_thirty_seven = [0x1f, 0x9a, 0x0a, 0xaa];
    assert_eq!(
        QpackInteger::parse(&thirteen_thirty_seven, 5)
            .map(|integer| (integer.value(), integer.as_bytes())),
        Ok((1337, &thirteen_thirty_seven[..3]))
    );
    let forty_two = [42, 0xaa];
    assert_eq!(
        QpackInteger::parse(&forty_two, 8).map(|integer| (integer.value(), integer.as_bytes())),
        Ok((42, &forty_two[..1]))
    );
    let bytes = [0];
    assert_eq!(
        net_wire::qpack::QpackInteger::parse(&bytes, 8).map(net_wire::qpack::QpackInteger::value),
        Ok(0)
    );
}

#[test]
fn every_prefix_width_preserves_legal_high_bits() {
    for prefix_bits in 1..=8 {
        let mask = if prefix_bits == 8 {
            0xff
        } else {
            (1u8 << prefix_bits) - 1
        };
        let high_bits = !mask;
        let mut destination = [0xaa; 2];
        assert_eq!(
            QpackIntegerBuilder::new(&mut destination, prefix_bits, high_bits, 0)
                .build()
                .map(|integer| {
                    (
                        integer.as_bytes(),
                        integer.high_bits(),
                        integer.prefix_bits(),
                    )
                }),
            Ok((&[high_bits][..], high_bits, prefix_bits))
        );
    }
}

#[test]
fn saturated_prefix_and_continuation_boundaries_are_canonical() {
    for (value, expected) in [
        (31, &[0x1f, 0][..]),
        (158, &[0x1f, 127][..]),
        (159, &[0x1f, 128, 1][..]),
    ] {
        let mut destination = [0xaa; 4];
        assert_eq!(
            QpackIntegerBuilder::new(&mut destination, 5, 0, value)
                .build()
                .map(|integer| integer.as_bytes()),
            Ok(expected)
        );
    }
}

#[test]
fn noncanonical_integer_preserves_its_exact_bytes() {
    let wire = [0x1f, 0x80, 0, 0xaa];
    assert_eq!(
        QpackInteger::parse(&wire, 5).map(|integer| (integer.value(), integer.as_bytes())),
        Ok((31, &wire[..3]))
    );
}

#[test]
fn incomplete_integer_reports_exact_boundaries() {
    assert_eq!(
        QpackInteger::parse(&[], 5),
        Err(QpackIntegerParseError::Incomplete {
            required: 1,
            available: 0,
        })
    );
    assert_eq!(
        QpackInteger::parse(&[0x1f], 5),
        Err(QpackIntegerParseError::Incomplete {
            required: 2,
            available: 1,
        })
    );
    assert_eq!(
        QpackInteger::parse(&[0x1f, 0x80], 5),
        Err(QpackIntegerParseError::Incomplete {
            required: 3,
            available: 2,
        })
    );
    assert_eq!(
        QpackInteger::parse(&[0x1f, 0x80, 0x80], 5),
        Err(QpackIntegerParseError::Incomplete {
            required: 4,
            available: 3,
        })
    );
}

#[test]
fn parse_distinguishes_overflow_from_qpack_value_limit() {
    let over_limit = [0xff, 0x81, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f];
    let overflow = [
        0xff, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0,
    ];
    assert!(matches!(
        QpackInteger::parse(&over_limit, 8),
        Err(QpackIntegerParseError::ValueTooLarge { .. })
    ));
    assert_eq!(
        QpackInteger::parse(&overflow, 8),
        Err(QpackIntegerParseError::Overflow)
    );
}

#[test]
fn qpack_integer_limit_roundtrips_and_rejects_the_next_value() {
    let mut destination = [0xaa; 16];
    assert_eq!(
        QpackIntegerBuilder::new(&mut destination, 8, 0, QPACK_INTEGER_MAX)
            .build()
            .map(|integer| integer.value()),
        Ok(QPACK_INTEGER_MAX)
    );
    assert_eq!(
        QpackInteger::parse(&destination, 8).map(|integer| integer.value()),
        Ok(QPACK_INTEGER_MAX)
    );
    let mut too_large_destination = [0xaa; 16];
    assert_eq!(
        QpackIntegerBuilder::new(&mut too_large_destination, 8, 0, QPACK_INTEGER_MAX + 1).build(),
        Err(QpackIntegerBuildError::ValueTooLarge {
            value: QPACK_INTEGER_MAX + 1,
        })
    );
    let over_limit = [0xff, 0x81, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f];
    assert!(matches!(
        QpackInteger::parse(&over_limit, 8),
        Err(QpackIntegerParseError::ValueTooLarge { .. })
    ));
}

#[test]
fn canonical_builder_uses_exact_capacity_and_preserves_suffix() {
    let mut exact = [0xaa; 3];
    assert_eq!(
        QpackIntegerBuilder::new(&mut exact, 5, 0, 1337)
            .build()
            .map(|integer| integer.as_bytes()),
        Ok(&[0x1f, 0x9a, 0x0a][..])
    );
    let mut destination = [0xaa; 4];
    assert_eq!(
        QpackIntegerBuilder::new(&mut destination, 5, 0, 1337)
            .build()
            .map(|integer| integer.as_bytes()),
        Ok(&[0x1f, 0x9a, 0x0a][..])
    );
    assert_eq!(destination[3], 0xaa);
}

#[test]
fn builder_errors_leave_the_whole_destination_unchanged() {
    let cases = [
        (
            0,
            0,
            0,
            QpackIntegerBuildError::InvalidPrefixBits { prefix_bits: 0 },
        ),
        (
            5,
            1,
            0,
            QpackIntegerBuildError::HighBitsOverlap {
                high_bits: 1,
                prefix_bits: 5,
            },
        ),
        (
            5,
            0,
            QPACK_INTEGER_MAX + 1,
            QpackIntegerBuildError::ValueTooLarge {
                value: QPACK_INTEGER_MAX + 1,
            },
        ),
    ];
    for (prefix_bits, high_bits, value, error) in cases {
        let mut destination = [0xaa; 2];
        assert_eq!(
            QpackIntegerBuilder::new(&mut destination, prefix_bits, high_bits, value).build(),
            Err(error)
        );
        assert_eq!(destination, [0xaa; 2]);
    }
    let mut destination = [0xaa; 2];
    assert_eq!(
        QpackIntegerBuilder::new(&mut destination, 5, 0, 1337).build(),
        Err(QpackIntegerBuildError::BufferTooShort {
            required: 3,
            available: 2,
        })
    );
    assert_eq!(destination, [0xaa; 2]);
}