use std::hint::black_box;
use std::time::Instant;
use serde_json::json;
use unb_core::{Envelope, Kind, DEFAULT_HOPS, PROTOCOL_VERSION};
mod legacy {
use bytes::{BufMut, Bytes, BytesMut};
use unb_core::Envelope;
pub fn decode(frame: Bytes) -> Envelope {
let header_len =
u32::from_le_bytes(frame[0..4].try_into().expect("length prefix")) as usize;
let header_end = 4 + header_len;
let mut envelope: Envelope =
serde_json::from_slice(&frame[4..header_end]).expect("legacy header json");
envelope.payload = frame.slice(header_end..);
envelope
}
pub fn encode(envelope: &Envelope) -> Bytes {
let header = serde_json::to_vec(envelope).expect("envelope header is plain json data");
let header_len = header.len();
let mut frame = BytesMut::with_capacity(4 + header_len + envelope.payload.len());
frame.put_u32_le(header_len as u32);
frame.put_slice(&header);
frame.put_slice(&envelope.payload);
frame.freeze()
}
}
fn request_envelope() -> Envelope {
let mut headers = serde_json::Map::new();
headers.insert("authorization".into(), json!("Bearer jwt-abc"));
headers.insert("x-player".into(), json!("alice"));
Envelope {
v: PROTOCOL_VERSION,
id: "f42".into(),
target: "games".into(),
subject: "chess.move".into(),
kind: Kind::Request,
corr: Some("s7".into()),
seq: None,
hops: Some(DEFAULT_HOPS),
body_token: None,
payload: Envelope::encode_payload(&json!({"from": "e2", "to": "e4"})),
path: Vec::new(),
headers,
}
}
fn response_envelope() -> Envelope {
Envelope {
v: PROTOCOL_VERSION,
id: "f43".into(),
target: String::new(),
subject: String::new(),
kind: Kind::Response,
corr: Some("s7".into()),
seq: None,
hops: None,
body_token: None,
payload: Envelope::encode_payload(&json!({"ok": true, "fen": "rnbqkbnr/pppppppp"})),
path: Vec::new(),
headers: serde_json::Map::new(),
}
}
fn measure(label: &str, iterations: u32, mut run: impl FnMut()) {
for _ in 0..iterations / 10 {
run();
}
let started = Instant::now();
for _ in 0..iterations {
run();
}
let nanos = started.elapsed().as_nanos() / iterations as u128;
println!("{label:<32} {nanos:>6} ns/op");
}
fn main() {
const ITERATIONS: u32 = 200_000;
for (name, envelope) in [
("request", request_envelope()),
("response", response_envelope()),
] {
let http_frame = envelope.encode();
let legacy_frame = legacy::encode(&envelope);
println!(
"{name}: http frame {} bytes, legacy frame {} bytes",
http_frame.len(),
legacy_frame.len()
);
measure(&format!("{name} encode http"), ITERATIONS, || {
black_box(black_box(&envelope).encode());
});
measure(&format!("{name} encode legacy"), ITERATIONS, || {
black_box(legacy::encode(black_box(&envelope)));
});
measure(&format!("{name} decode http"), ITERATIONS, || {
black_box(Envelope::decode(black_box(http_frame.clone())).expect("http frame"));
});
measure(&format!("{name} decode legacy"), ITERATIONS, || {
black_box(legacy::decode(black_box(legacy_frame.clone())));
});
}
}