259 lines
8.6 KiB
Rust
259 lines
8.6 KiB
Rust
use dever_runtime::wire::serde::{self, Deserialize, Serialize};
|
|
use dever_runtime::{
|
|
api::{Request, Response},
|
|
task::{Limits, Runtime, Scope},
|
|
};
|
|
use std::future::Future;
|
|
|
|
struct Input<'scope> {
|
|
request: Request,
|
|
peer: std::net::SocketAddr,
|
|
scope: &'scope Scope<'scope>,
|
|
}
|
|
|
|
fn fault(_: impl std::fmt::Display) -> Fault {
|
|
&FaultInfo {
|
|
message: "HTTP input failure",
|
|
source: "api-reference",
|
|
line: 1,
|
|
column: 1,
|
|
}
|
|
}
|
|
|
|
// 此样例不挂起;直接轮询实际生成入口,计时不含网络与调度器启动。
|
|
fn generated(values: &[Input<'_>]) -> Result<Vec<u8>, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(Vec::new());
|
|
};
|
|
let future = async {
|
|
if value.request.uri().path() == "/bench/wire/count" {
|
|
GENERATED_LIST(&value.request, value.peer, value.scope).await
|
|
} else {
|
|
GENERATED_HTTP(&value.request, value.peer, value.scope).await
|
|
}
|
|
};
|
|
let mut future = std::pin::pin!(future);
|
|
let mut context = std::task::Context::from_waker(std::task::Waker::noop());
|
|
let std::task::Poll::Ready(response) = future.as_mut().poll(&mut context) else {
|
|
panic!("pure HTTP fixture unexpectedly suspended");
|
|
};
|
|
if response.status() != 200 {
|
|
return Err(fault(response.status()));
|
|
}
|
|
Ok(response.into_body().into())
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
|
struct Payload {
|
|
text: String,
|
|
value: i64,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
|
struct Arguments {
|
|
payload: Object<Payload>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
|
struct ListArguments {
|
|
values: Integers,
|
|
}
|
|
|
|
struct Integers(Vec<i64>);
|
|
|
|
impl<'input> Deserialize<'input> for Integers {
|
|
fn deserialize<Decoder: serde::Deserializer<'input>>(
|
|
decoder: Decoder,
|
|
) -> Result<Self, Decoder::Error> {
|
|
struct Visitor;
|
|
impl<'input> serde::de::Visitor<'input> for Visitor {
|
|
type Value = Integers;
|
|
fn expecting(&self, output: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
output.write_str("an integer array")
|
|
}
|
|
fn visit_seq<Access: serde::de::SeqAccess<'input>>(
|
|
self,
|
|
mut access: Access,
|
|
) -> Result<Integers, Access::Error> {
|
|
let mut values = Vec::new();
|
|
while let Some(value) = access.next_element()? {
|
|
// 根对象和数组各占一个值;检查与 Serde 解码在同一次遍历中完成。
|
|
if values.len() == 65536 - 2 {
|
|
return Err(serde::de::Error::custom("value limit"));
|
|
}
|
|
values.push(value);
|
|
}
|
|
Ok(Integers(values))
|
|
}
|
|
}
|
|
decoder.deserialize_seq(Visitor)
|
|
}
|
|
}
|
|
|
|
struct Object<Value>(Value);
|
|
|
|
impl<'input, Value: Deserialize<'input>> Deserialize<'input> for Object<Value> {
|
|
fn deserialize<Decoder: serde::Deserializer<'input>>(
|
|
decoder: Decoder,
|
|
) -> Result<Self, Decoder::Error> {
|
|
struct Visitor<Value>(std::marker::PhantomData<Value>);
|
|
impl<'input, Value: Deserialize<'input>> serde::de::Visitor<'input> for Visitor<Value> {
|
|
type Value = Value;
|
|
fn expecting(&self, output: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
output.write_str("an object")
|
|
}
|
|
fn visit_map<Access: serde::de::MapAccess<'input>>(
|
|
self,
|
|
access: Access,
|
|
) -> Result<Value, Access::Error> {
|
|
Value::deserialize(serde::de::value::MapAccessDeserializer::new(access))
|
|
}
|
|
}
|
|
decoder
|
|
.deserialize_map(Visitor(std::marker::PhantomData))
|
|
.map(Self)
|
|
}
|
|
}
|
|
|
|
// 独立使用 Serde;固定记录保证结构预算,字节预算在唯一输入/输出边界处理。
|
|
fn reference(values: &[Input<'_>]) -> Result<Vec<u8>, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(Vec::new());
|
|
};
|
|
let request = &value.request;
|
|
let mut headers = request.headers().get_all("content-type").iter();
|
|
let content = headers.next().ok_or_else(|| fault("content type"))?;
|
|
let content = std::str::from_utf8(content.as_bytes()).map_err(fault)?;
|
|
if !content
|
|
.split(';')
|
|
.next()
|
|
.unwrap()
|
|
.trim()
|
|
.eq_ignore_ascii_case("application/json")
|
|
|| headers.next().is_some()
|
|
{
|
|
return Err(fault("content type"));
|
|
}
|
|
if request.body().len() > 16 * 1024 * 1024 {
|
|
return Err(fault("input size"));
|
|
}
|
|
let text = std::str::from_utf8(request.body()).map_err(fault)?;
|
|
#[derive(Serialize)]
|
|
#[serde(crate = "dever_runtime::wire::serde")]
|
|
struct Envelope<Value> {
|
|
code: u16,
|
|
message: &'static str,
|
|
data: Value,
|
|
}
|
|
struct Output(Vec<u8>);
|
|
impl std::io::Write for Output {
|
|
fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
|
|
if bytes.len() > 16 * 1024 * 1024 - self.0.len() {
|
|
return Err(std::io::Error::other("output size"));
|
|
}
|
|
self.0.extend_from_slice(bytes);
|
|
Ok(bytes.len())
|
|
}
|
|
fn flush(&mut self) -> std::io::Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
let mut output = Output(Vec::with_capacity(128));
|
|
if request.uri().path() == "/bench/wire/count" {
|
|
let arguments: Object<ListArguments> = serde_json::from_str(text).map_err(fault)?;
|
|
serde_json::to_writer(
|
|
&mut output,
|
|
&Envelope {
|
|
code: 0,
|
|
message: "ok",
|
|
data: arguments.0.values.0.len() as i64,
|
|
},
|
|
)
|
|
.map_err(fault)?;
|
|
} else {
|
|
let arguments: Object<Arguments> = serde_json::from_str(text).map_err(fault)?;
|
|
serde_json::to_writer(
|
|
&mut output,
|
|
&Envelope {
|
|
code: 0,
|
|
message: "ok",
|
|
data: arguments.0.payload.0,
|
|
},
|
|
)
|
|
.map_err(fault)?;
|
|
}
|
|
let mut response = Response::new(output.0.into());
|
|
response.headers_mut().insert(
|
|
hyper::header::CONTENT_TYPE,
|
|
hyper::header::HeaderValue::from_static("application/json; charset=utf-8"),
|
|
);
|
|
Ok(response.into_body().into())
|
|
}
|
|
|
|
fn main() {
|
|
println!("case,sample,implementation,elapsed_ns,checksum");
|
|
let runtime = Runtime::new(Limits {
|
|
threads: 1,
|
|
workers: 1,
|
|
tasks: 8,
|
|
})
|
|
.unwrap();
|
|
runtime
|
|
.run(async |scope| {
|
|
for size in [64, 4096, 65536] {
|
|
let payload = Payload {
|
|
text: "雪\\\"".repeat(size / 5),
|
|
value: 42,
|
|
};
|
|
let body = format!(
|
|
"{{\"payload\":{}}}",
|
|
serde_json::to_string(&payload).unwrap()
|
|
)
|
|
.into_bytes();
|
|
let values = [Input {
|
|
request: hyper::Request::builder()
|
|
.method("POST")
|
|
.uri("/bench/wire/echo")
|
|
.header("content-type", "application/json")
|
|
.body(body.into())
|
|
.unwrap(),
|
|
peer: ([127, 0, 0, 1], 12345).into(),
|
|
scope,
|
|
}];
|
|
compare(
|
|
&format!("http_fixed_record_{size}"),
|
|
&values,
|
|
200,
|
|
generated,
|
|
reference,
|
|
|body| body.len() as u64,
|
|
);
|
|
}
|
|
for size in [16, 1024, 8192, 32768] {
|
|
let body = format!("{{\"values\":[{}]}}", vec!["42"; size].join(",")).into_bytes();
|
|
let values = [Input {
|
|
request: hyper::Request::builder()
|
|
.method("POST")
|
|
.uri("/bench/wire/count")
|
|
.header("content-type", "application/json")
|
|
.body(body.into())
|
|
.unwrap(),
|
|
peer: ([127, 0, 0, 1], 12345).into(),
|
|
scope,
|
|
}];
|
|
compare(
|
|
&format!("http_integer_list_{size}"),
|
|
&values,
|
|
200,
|
|
generated,
|
|
reference,
|
|
|body| body.len() as u64,
|
|
);
|
|
}
|
|
})
|
|
.unwrap();
|
|
}
|