Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,258 @@
|
||||
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();
|
||||
}
|
||||
Reference in new issue
Block a user