net-wire 0.1.0

Allocation-free no_std wire views and codecs for network protocols.
Documentation
use net_wire::http2::hpack::{
    HPACK_STATIC_TABLE_LEN, HpackDynamicTable, HpackDynamicTableEntry, HpackDynamicTableError,
    HpackDynamicTableInsertResult, HpackHeaderFieldRef, HpackStaticTable,
};

#[test]
fn hpack_static_table_has_rfc_boundaries_and_complete_index_space() {
    assert_eq!(HpackStaticTable::len(), HPACK_STATIC_TABLE_LEN);
    assert_eq!(HpackStaticTable::len(), 61);
    assert!(!HpackStaticTable::is_empty());
    assert_eq!(HpackStaticTable::get(0), None);
    assert_eq!(
        HpackStaticTable::get(1),
        Some(HpackHeaderFieldRef::new(b":authority", b""))
    );
    assert_eq!(
        HpackStaticTable::get(61),
        Some(HpackHeaderFieldRef::new(b"www-authenticate", b""))
    );
    assert_eq!(HpackStaticTable::get(62), None);
    assert!((1..=HpackStaticTable::len()).all(|index| HpackStaticTable::get(index).is_some()));
}

#[test]
fn hpack_static_table_preserves_rfc_representative_ordering_and_empty_values() {
    for (index, expected) in [
        (2, HpackHeaderFieldRef::new(b":method", b"GET")),
        (3, HpackHeaderFieldRef::new(b":method", b"POST")),
        (4, HpackHeaderFieldRef::new(b":path", b"/")),
        (5, HpackHeaderFieldRef::new(b":path", b"/index.html")),
        (8, HpackHeaderFieldRef::new(b":status", b"200")),
        (14, HpackHeaderFieldRef::new(b":status", b"500")),
        (
            16,
            HpackHeaderFieldRef::new(b"accept-encoding", b"gzip, deflate"),
        ),
        (15, HpackHeaderFieldRef::new(b"accept-charset", b"")),
        (28, HpackHeaderFieldRef::new(b"content-length", b"")),
        (61, HpackHeaderFieldRef::new(b"www-authenticate", b"")),
    ] {
        assert_eq!(HpackStaticTable::get(index), Some(expected));
    }
}

#[test]
fn hpack_header_field_ref_is_copyable_and_byte_oriented() {
    let field = HpackHeaderFieldRef::new(&[0x80, 0xff], &[0, 0xfe]);
    let copied = field;
    assert_eq!(field, copied);
    assert_eq!(copied.name(), [0x80, 0xff]);
    assert_eq!(copied.value(), [0, 0xfe]);
}

#[test]
fn hpack_dynamic_table_capacity_is_proven_without_mutating_rejected_stores() {
    let mut no_bytes = [];
    let mut no_entries = [];
    let table = HpackDynamicTable::new(&mut no_bytes, &mut no_entries, 31).unwrap();
    assert_eq!(table.capacity(), 31);

    let mut bytes = [0xa5; 1];
    let mut entries = [HpackDynamicTableEntry::EMPTY; 1];
    let before_bytes = bytes;
    let before_entries = entries;
    assert!(matches!(
        HpackDynamicTable::new(&mut bytes, &mut entries, 34),
        Err(HpackDynamicTableError::MaximumSizeExceedsCapacity {
            requested: 34,
            capacity: 33,
        })
    ));
    assert_eq!(bytes, before_bytes);
    assert_eq!(entries, before_entries);
}

#[test]
fn hpack_dynamic_table_copies_binary_fields_and_indexes_newest_first() {
    let mut bytes = [0; 36];
    let mut entries = [HpackDynamicTableEntry::EMPTY; 2];
    let mut table = HpackDynamicTable::new(&mut bytes, &mut entries, 68).unwrap();
    let mut name = [0xff];
    assert_eq!(
        table.insert(&name, &[0, 0x80]),
        Ok(HpackDynamicTableInsertResult::Inserted)
    );
    name[0] = 0;
    assert_eq!(name, [0]);
    assert_eq!(
        table.insert(&[], &[]),
        Ok(HpackDynamicTableInsertResult::Inserted)
    );
    assert_eq!(table.len(), 2);
    assert_eq!(table.size(), 67);
    assert_eq!(table.get(1), Some(HpackHeaderFieldRef::new(&[], &[])));
    assert_eq!(
        table.get(2),
        Some(HpackHeaderFieldRef::new(&[0xff], &[0, 0x80]))
    );
    assert_eq!(table.get(0), None);
    assert_eq!(table.get(3), None);
}

#[test]
fn hpack_dynamic_table_evicts_oldest_and_handles_oversized_entries() {
    let mut bytes = [0xcc; 36];
    let mut entries = [HpackDynamicTableEntry::EMPTY; 2];
    let mut table = HpackDynamicTable::new(&mut bytes, &mut entries, 68).unwrap();
    table.insert(b"a", b"1").unwrap();
    table.insert(b"b", b"2").unwrap();
    table.insert(b"c", b"3").unwrap();
    assert_eq!(table.get(1), Some(HpackHeaderFieldRef::new(b"c", b"3")));
    assert_eq!(table.get(2), Some(HpackHeaderFieldRef::new(b"b", b"2")));
    assert_eq!(table.get(3), None);
    assert_eq!(
        table.insert(&[0; 37], b""),
        Ok(HpackDynamicTableInsertResult::NotInsertedOversized)
    );
    assert!(table.is_empty());
    assert_eq!(table.get(1), None);
}

#[test]
fn hpack_dynamic_table_preserves_variable_length_entries_across_shift_and_eviction() {
    let mut bytes = [0; 96];
    let mut entries = [HpackDynamicTableEntry::EMPTY; 4];
    let mut table = HpackDynamicTable::new(&mut bytes, &mut entries, 128).unwrap();

    table.insert(b"a", b"111").unwrap();
    table.insert(b"bb", b"22").unwrap();
    table.insert(b"ccc", b"3").unwrap();
    table.insert(b"dddd", b"4444").unwrap();

    assert_eq!(table.size(), 112);
    assert_eq!(
        table.get(1),
        Some(HpackHeaderFieldRef::new(b"dddd", b"4444"))
    );
    assert_eq!(table.get(2), Some(HpackHeaderFieldRef::new(b"ccc", b"3")));
    assert_eq!(table.get(3), Some(HpackHeaderFieldRef::new(b"bb", b"22")));
    assert_eq!(table.get(4), None);

    table.set_maximum_size(76).unwrap();
    assert_eq!(table.size(), 76);
    assert_eq!(
        table.get(1),
        Some(HpackHeaderFieldRef::new(b"dddd", b"4444"))
    );
    assert_eq!(table.get(2), Some(HpackHeaderFieldRef::new(b"ccc", b"3")));
    assert_eq!(table.get(3), None);
}

#[test]
fn hpack_dynamic_table_resizes_and_rejects_excess_maximum_atomically() {
    let mut bytes = [0x5a; 36];
    let mut entries = [HpackDynamicTableEntry::EMPTY; 2];
    let mut table = HpackDynamicTable::new(&mut bytes, &mut entries, 68).unwrap();
    table.insert(b"a", b"1").unwrap();
    table.insert(b"b", b"2").unwrap();
    table.set_maximum_size(34).unwrap();
    assert_eq!(table.len(), 1);
    assert_eq!(table.get(1), Some(HpackHeaderFieldRef::new(b"b", b"2")));
    table.set_maximum_size(68).unwrap();
    assert_eq!(table.maximum_size(), 68);
    let before_size = table.size();
    assert_eq!(
        table.set_maximum_size(69),
        Err(HpackDynamicTableError::MaximumSizeExceedsCapacity {
            requested: 69,
            capacity: 68,
        })
    );
    assert_eq!(table.maximum_size(), 68);
    assert_eq!(table.size(), before_size);
    assert_eq!(table.get(1), Some(HpackHeaderFieldRef::new(b"b", b"2")));
}

#[test]
fn hpack_dynamic_table_exact_maximum_and_clear_do_not_expose_stale_storage() {
    let mut bytes = [0xaa; 1];
    let mut entries = [HpackDynamicTableEntry::EMPTY; 1];
    {
        let mut table = HpackDynamicTable::new(&mut bytes, &mut entries, 33).unwrap();
        assert_eq!(
            table.insert(b"x", b""),
            Ok(HpackDynamicTableInsertResult::Inserted)
        );
        assert_eq!(table.size(), 33);
        table.clear();
        assert!(table.is_empty());
        assert_eq!(table.get(1), None);
    }
    assert_eq!(bytes[0], b'x');
}