Complete first release candidate audit (#107)
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled
This commit is contained in:
167
crates/libremetaverse/src/packet_decoder.rs
Normal file
167
crates/libremetaverse/src/packet_decoder.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
//! Deterministic, reflection-free packet and compatibility-object diagnostics.
|
||||
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use libremetaverse_structured_data::OSD;
|
||||
use libremetaverse_types::Error;
|
||||
use libremetaverse_types::compat::Object;
|
||||
|
||||
use crate::packets::{Header, Packet};
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn interpret_options(header: &Header) -> String {
|
||||
format!(
|
||||
"[{} {} {} {}]",
|
||||
if header.appended_acks { "Ack" } else { " " },
|
||||
if header.resent { "Res" } else { " " },
|
||||
if header.reliable { "Rel" } else { " " },
|
||||
if header.zerocoded { "Zer" } else { " " },
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn packet_to_string(packet: &Packet) -> Result<String, Error> {
|
||||
let descriptor =
|
||||
crate::packet_catalog::descriptor_by_type(packet.type_).ok_or(Error::InvalidOperation)?;
|
||||
let mut result = String::new();
|
||||
writeln!(
|
||||
result,
|
||||
"Packet Type: {0} http://lib.openmetaverse.co/wiki/{0} http://wiki.secondlife.com/wiki/{0}",
|
||||
descriptor.name,
|
||||
)
|
||||
.map_err(|_| Error::InvalidOperation)?;
|
||||
writeln!(result, "[Packet Header]").map_err(|_| Error::InvalidOperation)?;
|
||||
writeln!(result, "Sequence: {}", packet.header.sequence)
|
||||
.map_err(|_| Error::InvalidOperation)?;
|
||||
writeln!(result, " Options: {}", interpret_options(&packet.header))
|
||||
.map_err(|_| Error::InvalidOperation)?;
|
||||
writeln!(result, "\n[Packet Payload]").map_err(|_| Error::InvalidOperation)?;
|
||||
let body = packet.packet_to_osd()?;
|
||||
format_osd_map(&body.snapshot(), 0, &mut result)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn message_to_string(message: &Object, recurse_level: i32) -> Result<String, Error> {
|
||||
if recurse_level < 0 || recurse_level > 64 {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let mut result = String::new();
|
||||
format_object(
|
||||
message,
|
||||
usize::try_from(recurse_level).map_err(|_| Error::Argument)?,
|
||||
&mut result,
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn format_object(value: &Object, depth: usize, output: &mut String) -> Result<(), Error> {
|
||||
let indent = " ".repeat(depth);
|
||||
match value {
|
||||
Object::Undefined => output.push_str(&format!("{indent}undefined\n")),
|
||||
Object::Boolean(value) => output.push_str(&format!("{indent}{value} [Boolean]\n")),
|
||||
Object::Integer(value) => output.push_str(&format!("{indent}{value} [Int32]\n")),
|
||||
Object::UInteger(value) => output.push_str(&format!("{indent}{value} [UInt32]\n")),
|
||||
Object::Long(value) => output.push_str(&format!("{indent}{value} [Int64]\n")),
|
||||
Object::ULong(value) => output.push_str(&format!("{indent}{value} [UInt64]\n")),
|
||||
Object::Real(value) => output.push_str(&format!("{indent}{value} [Double]\n")),
|
||||
Object::String(value) => output.push_str(&format!("{indent}{value} [String]\n")),
|
||||
Object::Bytes(value) => {
|
||||
output.push_str(&format!("{indent}[Byte[{0}]]\n", value.len()));
|
||||
format_hex(value, depth + 1, output)?;
|
||||
}
|
||||
Object::Array(values) => {
|
||||
output.push_str(&format!("{indent}[Array {0}]\n", values.len()));
|
||||
for (index, value) in values.iter().enumerate() {
|
||||
output.push_str(&format!("{indent} [{index}]\n"));
|
||||
format_object(value, depth + 2, output)?;
|
||||
}
|
||||
}
|
||||
Object::Map(values) => {
|
||||
output.push_str(&format!("{indent}[Map {0}]\n", values.len()));
|
||||
let mut entries = values.iter().collect::<Vec<_>>();
|
||||
entries.sort_by(|left, right| left.0.cmp(right.0));
|
||||
for (key, value) in entries {
|
||||
output.push_str(&format!("{indent} {key}:\n"));
|
||||
format_object(value, depth + 2, output)?;
|
||||
}
|
||||
}
|
||||
other => output.push_str(&format!("{indent}{other:?}\n")),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_osd_map(
|
||||
values: &std::collections::HashMap<String, OSD>,
|
||||
depth: usize,
|
||||
output: &mut String,
|
||||
) -> Result<(), Error> {
|
||||
let mut entries = values.iter().collect::<Vec<_>>();
|
||||
entries.sort_by(|left, right| left.0.cmp(right.0));
|
||||
for (key, value) in entries {
|
||||
writeln!(output, "{}{key}:", " ".repeat(depth)).map_err(|_| Error::InvalidOperation)?;
|
||||
format_osd(value, depth + 1, output)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_osd(value: &OSD, depth: usize, output: &mut String) -> Result<(), Error> {
|
||||
let indent = " ".repeat(depth);
|
||||
match value {
|
||||
OSD::Map(values) => format_osd_map(values, depth, output),
|
||||
OSD::Array(values) => {
|
||||
for (index, value) in values.iter().enumerate() {
|
||||
writeln!(output, "{indent}[{index}]").map_err(|_| Error::InvalidOperation)?;
|
||||
format_osd(value, depth + 1, output)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
OSD::Binary(bytes) => format_hex(bytes, depth, output),
|
||||
other => writeln!(output, "{indent}{other:?}").map_err(|_| Error::InvalidOperation),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_hex(bytes: &[u8], depth: usize, output: &mut String) -> Result<(), Error> {
|
||||
let indent = " ".repeat(depth);
|
||||
for chunk in bytes.chunks(16) {
|
||||
write!(output, "{indent}").map_err(|_| Error::InvalidOperation)?;
|
||||
for byte in chunk {
|
||||
write!(output, "{byte:02X} ").map_err(|_| Error::InvalidOperation)?;
|
||||
}
|
||||
output.push('\n');
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn options_match_reference_layout() {
|
||||
let mut header = crate::packet_catalog::new_header(crate::PacketFrequency::High, 1, false);
|
||||
header.reliable = true;
|
||||
header.resent = true;
|
||||
assert_eq!(interpret_options(&header), "[ Res Rel ]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packet_dump_contains_header_and_sorted_payload() {
|
||||
let packet =
|
||||
crate::packet_catalog::build_packet(crate::packet_catalog::PacketType::CloseCircuit)
|
||||
.unwrap();
|
||||
let dump = packet_to_string(&packet).unwrap();
|
||||
assert!(dump.contains("Packet Type: CloseCircuit"));
|
||||
assert!(dump.contains("[Packet Header]"));
|
||||
assert!(dump.contains("[Packet Payload]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_dump_formats_nested_values_and_bytes() {
|
||||
let value = Object::Map(std::collections::HashMap::from([
|
||||
("z".to_owned(), Object::Bytes(vec![0xab, 0xcd])),
|
||||
("a".to_owned(), Object::Integer(7)),
|
||||
]));
|
||||
let text = message_to_string(&value, 0).unwrap();
|
||||
assert!(text.find("a:").unwrap() < text.find("z:").unwrap());
|
||||
assert!(text.contains("AB CD"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user