172 lines
5.4 KiB
Rust
172 lines
5.4 KiB
Rust
//! 出站帧保留 JSON 原文,并在写出长度前验证合并后的预算。
|
|
use super::*;
|
|
use crate::wire::json::Json;
|
|
use std::{hint::black_box, process::Stdio, time::Instant};
|
|
|
|
#[derive(serde::Serialize)]
|
|
struct Call<'input> {
|
|
kind: &'static str,
|
|
id: u64,
|
|
operation: &'input str,
|
|
}
|
|
|
|
#[test]
|
|
fn outgoing_payload_preserves_spelling_and_combined_limits() {
|
|
let header = Call {
|
|
kind: "call",
|
|
id: 1,
|
|
operation: "雪\"\\",
|
|
};
|
|
for source in ["null", " \n{\"雪\":[1e9999,-0,\"\\u96ea\"]}\t "] {
|
|
let value = Json::parse(source).unwrap();
|
|
let head = payload_header(
|
|
serde_json::to_string(&header).unwrap(),
|
|
4,
|
|
",\"payload\":",
|
|
value.shape().unwrap(),
|
|
)
|
|
.unwrap();
|
|
let frame = format!("{head}{}}}", source.trim_ascii());
|
|
#[derive(serde::Serialize)]
|
|
struct Reference<'input> {
|
|
#[serde(flatten)]
|
|
header: &'input Call<'input>,
|
|
payload: &'input RawValue,
|
|
}
|
|
assert_eq!(
|
|
frame,
|
|
serde_json::to_string(&Reference {
|
|
header: &header,
|
|
payload: serde_json::from_str(source).unwrap(),
|
|
})
|
|
.unwrap()
|
|
);
|
|
crate::wire::raw::validate(&frame).unwrap();
|
|
}
|
|
for count in [document::MAX_VALUES - 5, document::MAX_VALUES - 4] {
|
|
let value = Json::encode(&vec![0; count]).unwrap();
|
|
let encoded = payload_header(
|
|
serde_json::to_string(&header).unwrap(),
|
|
4,
|
|
",\"payload\":",
|
|
value.shape().unwrap(),
|
|
);
|
|
assert_eq!(encoded.is_ok(), count == document::MAX_VALUES - 5);
|
|
}
|
|
for depth in [document::MAX_DEPTH - 1, document::MAX_DEPTH] {
|
|
let value = Json::parse(&("[".repeat(depth) + &"]".repeat(depth))).unwrap();
|
|
assert_eq!(
|
|
payload_header(
|
|
serde_json::to_string(&header).unwrap(),
|
|
4,
|
|
",\"payload\":",
|
|
value.shape().unwrap()
|
|
)
|
|
.is_ok(),
|
|
depth < document::MAX_DEPTH
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn oversized_frame_does_not_write_a_partial_prefix() {
|
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
runtime.block_on(async {
|
|
let mut child = tokio::process::Command::new("cat")
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
let mut channel = Channel::new(child.stdin.take().unwrap(), child.stdout.take().unwrap());
|
|
let payload = Json::string(&"a".repeat(LIMIT - 2)).unwrap();
|
|
let header = Call {
|
|
kind: "call",
|
|
id: 1,
|
|
operation: "measure",
|
|
};
|
|
assert!(
|
|
channel
|
|
.send_payload(
|
|
serde_json::to_string(&header).unwrap(),
|
|
4,
|
|
",\"payload\":",
|
|
payload.as_str(),
|
|
payload.shape().unwrap()
|
|
)
|
|
.await
|
|
.is_err()
|
|
);
|
|
let control = serde_json::json!({"kind":"health","id":0});
|
|
channel.send(&control).await.unwrap();
|
|
assert_eq!(channel.read().await.unwrap().as_str(), control.to_string());
|
|
drop(channel);
|
|
assert!(child.wait().await.unwrap().success());
|
|
});
|
|
}
|
|
|
|
fn reference_header(header: &Call<'_>, values: usize, depth: usize) -> Result<String, String> {
|
|
if values + 4 > document::MAX_VALUES || depth + 1 > document::MAX_DEPTH {
|
|
return Err("JSON budget".into());
|
|
}
|
|
let mut text = serde_json::to_string(header).map_err(|error| error.to_string())?;
|
|
// 与正式通道一样,整帧字节预算在写出长度前检查,不属于帧头编码计时。
|
|
text.pop();
|
|
text.push_str(",\"payload\":");
|
|
Ok(text)
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "explicit release performance probe; header only, not complete IPC"]
|
|
fn outgoing_header_cost() {
|
|
let header = Call {
|
|
kind: "call",
|
|
id: 42,
|
|
operation: "measure",
|
|
};
|
|
let shape = document::Shape::new(2, 1);
|
|
let expected = reference_header(&header, 2, 1).unwrap();
|
|
assert_eq!(
|
|
payload_header(
|
|
serde_json::to_string(&header).unwrap(),
|
|
4,
|
|
",\"payload\":",
|
|
shape
|
|
)
|
|
.unwrap(),
|
|
expected
|
|
);
|
|
for sample in 0..15 {
|
|
for native in if sample % 2 == 0 {
|
|
[false, true]
|
|
} else {
|
|
[true, false]
|
|
} {
|
|
let started = Instant::now();
|
|
let mut checksum = 0;
|
|
for _ in 0..100_000 {
|
|
let text = if native {
|
|
reference_header(black_box(&header), black_box(2), black_box(1))
|
|
} else {
|
|
payload_header(
|
|
serde_json::to_string(black_box(&header)).unwrap(),
|
|
4,
|
|
",\"payload\":",
|
|
black_box(shape),
|
|
)
|
|
}
|
|
.unwrap();
|
|
checksum += black_box(text).len();
|
|
}
|
|
assert_eq!(checksum, 100_000 * expected.len());
|
|
println!(
|
|
"worker_header,{sample},{},{},{checksum}",
|
|
if native { "rust" } else { "dever" },
|
|
started.elapsed().as_nanos()
|
|
);
|
|
}
|
|
}
|
|
}
|