Implement generated packet wire codecs (#47)
Some checks failed
Native code generation / deterministic (push) Successful in 4m29s
Imaging and meshing gate / native (push) Failing after 16s
JPEG 2000 feature / linux (push) Failing after 59s
Skia feature / linux (push) Failing after 1m37s

This commit is contained in:
2026-08-09 08:16:32 +00:00
parent 5bc22bdd83
commit 74801c3488
16 changed files with 87505 additions and 25470 deletions

View File

@@ -0,0 +1,178 @@
use libremetaverse::packets::{
AgentThrottlePacket, Header, Packet, PacketType, UseCircuitCodePacket,
};
use libremetaverse::{Error, Helpers, PacketFrequency};
use libremetaverse_types::UUID;
fn header(frequency: PacketFrequency, id: u16) -> Header {
Header {
ack_list: None,
appended_acks: false,
frequency,
id,
reliable: true,
resent: false,
sequence: 0x0102_0304,
zerocoded: false,
}
}
fn header_bytes(header: &Header) -> Vec<u8> {
let length = match header.frequency {
PacketFrequency::High => 7,
PacketFrequency::Medium => 8,
PacketFrequency::Low => 10,
};
let mut bytes = vec![0_u8; length];
let mut position = 0;
header
.to_bytes(&mut bytes, &mut position)
.expect("encode header");
assert_eq!(usize::try_from(position).unwrap(), bytes.len());
bytes
}
#[test]
fn header_frequencies_match_the_reference_wire_bytes() {
// Golden source: codegen/inputs/message_template.msg frequency rules and
// LibreMetaverse.Tests/PacketTests.cs::HeaderFlags (translated in
// tests/compat/tests/wire_semantics.rs::packet_header_flags).
assert_eq!(
header_bytes(&header(PacketFrequency::High, 0x2a)),
[0x40, 1, 2, 3, 4, 0, 0x2a]
);
assert_eq!(
header_bytes(&header(PacketFrequency::Medium, 0x2a)),
[0x40, 1, 2, 3, 4, 0, 0xff, 0x2a]
);
assert_eq!(
header_bytes(&header(PacketFrequency::Low, 0x1234)),
[0x40, 1, 2, 3, 4, 0, 0xff, 0xff, 0x12, 0x34]
);
}
#[test]
fn appended_acks_are_big_endian_and_removed_from_the_payload_boundary() {
let mut value = header(PacketFrequency::High, 0x2a);
value.appended_acks = true;
value.ack_list = Some(vec![0x0102_0304, 0xa0b0_c0d0]);
let mut bytes = header_bytes(&value);
let mut ack_bytes = [0_u8; 9];
let mut ack_position = 0;
value
.acks_to_bytes(&mut ack_bytes, &mut ack_position)
.expect("encode ACKs");
assert_eq!(ack_bytes, [1, 2, 3, 4, 0xa0, 0xb0, 0xc0, 0xd0, 2]);
bytes.extend_from_slice(&ack_bytes);
let mut position = 0;
let mut packet_end = i32::try_from(bytes.len()).unwrap() - 1;
let decoded = Header::build_header(bytes, &mut position, &mut packet_end).expect("decode");
assert!(decoded.appended_acks);
assert_eq!(decoded.ack_list, value.ack_list);
assert_eq!(position, 7);
assert_eq!(packet_end, 6);
}
#[test]
fn use_circuit_code_matches_the_reference_golden_packet() {
// Golden schema: codegen/inputs/message_template.msg::UseCircuitCode
// (Low 3, CircuitCode/U32 + SessionID/LLUUID + ID/LLUUID). Header behavior
// is linked to the translated HeaderFlags test above.
let session = (0_u8..16).collect::<Vec<_>>();
let agent = (16_u8..32).collect::<Vec<_>>();
let mut packet = UseCircuitCodePacket::new_with_constructor().expect("packet constructor");
packet.circuit_code.code = 0x1122_3344;
packet.circuit_code.session_id =
UUID::new_with_bytes_int32(session.clone(), 0).expect("session UUID");
packet.circuit_code.id = UUID::new_with_bytes_int32(agent.clone(), 0).expect("agent UUID");
let encoded = packet.to_bytes_with_method().expect("encode packet");
let mut expected = vec![
0x40, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 3, 0x44, 0x33, 0x22, 0x11,
];
expected.extend_from_slice(&session);
expected.extend_from_slice(&agent);
assert_eq!(encoded, expected);
let mut position = 0;
let decoded =
UseCircuitCodePacket::new_with_bytes_int32(encoded.clone(), &mut position).expect("decode");
assert_eq!(usize::try_from(position).unwrap(), encoded.len());
assert_eq!(decoded.circuit_code.code, 0x1122_3344);
assert_eq!(
decoded.circuit_code.session_id.get_bytes().unwrap(),
session
);
assert_eq!(decoded.circuit_code.id.get_bytes().unwrap(), agent);
assert_eq!(decoded.to_bytes_with_method().unwrap(), encoded);
}
#[test]
fn malformed_and_unknown_packets_return_errors_without_panicking() {
let valid = [0x40, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 3];
for length in 0..valid.len() {
let bytes = valid[..length].to_vec();
let outcome = std::panic::catch_unwind(|| {
let mut end = i32::try_from(bytes.len()).unwrap() - 1;
Packet::build_packet_with_bytes_int32_bytes(bytes.clone(), &mut end, vec![0; 64])
});
assert!(outcome.is_ok(), "truncation at {length} panicked");
assert!(
outcome.unwrap().is_err(),
"truncation at {length} was accepted"
);
}
let missing_payload = valid.to_vec();
let mut end = i32::try_from(missing_payload.len()).unwrap() - 1;
assert!(
Packet::build_packet_with_bytes_int32_bytes(missing_payload, &mut end, vec![0; 64])
.is_err()
);
let unknown = vec![0x40, 0, 0, 0, 0, 0, 0xfe];
let mut end = i32::try_from(unknown.len()).unwrap() - 1;
let Err(error) = Packet::build_packet_with_bytes_int32_bytes(unknown, &mut end, vec![0; 64])
else {
panic!("unknown packet ID was accepted");
};
assert!(matches!(error, Error::Parse { .. }));
}
#[test]
fn packet_factory_decodes_zerocoded_payloads() {
let mut packet = AgentThrottlePacket::new_with_constructor().expect("packet constructor");
packet.agent_data.circuit_code = 0x1122_3344;
packet.throttle.gen_counter = 0x5566_7788;
packet.throttle.throttles = vec![1, 0, 0, 2, 3];
let raw = packet.to_bytes_with_method().expect("raw packet");
let mut encoded = vec![0_u8; raw.len() * 2 + 2];
let encoded_length = Helpers::zero_encode(
Some(&raw),
i32::try_from(raw.len()).unwrap(),
Some(&mut encoded),
)
.expect("zero encode");
encoded.truncate(usize::try_from(encoded_length).unwrap());
let mut end = encoded_length - 1;
let built =
Packet::build_packet_with_bytes_int32_bytes(encoded, &mut end, vec![0_u8; raw.len()])
.expect("packet factory");
assert_eq!(built.type_, PacketType::AgentThrottle);
assert_eq!(end, i32::try_from(raw.len()).unwrap() - 1);
}
#[test]
fn packet_id_dispatch_covers_known_and_unknown_ids() {
assert_eq!(
Packet::get_type(3, PacketFrequency::Low).expect("packet type"),
PacketType::UseCircuitCode
);
assert_eq!(
Packet::get_type(u16::MAX, PacketFrequency::High).expect("unknown type"),
PacketType::Default
);
assert_eq!(Helpers::MSG_RELIABLE, 0x40);
}