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();
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
use dever_runtime::wire::{
|
||||
self,
|
||||
serde::{self, Deserialize, Serialize},
|
||||
};
|
||||
|
||||
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> {
|
||||
wire::object(decoder).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
||||
struct Greeting {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
||||
struct Integer {
|
||||
value: i64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
||||
struct Profile {
|
||||
id: Id,
|
||||
name: String,
|
||||
scores: Vec<i64>,
|
||||
ratio: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
||||
struct Echo {
|
||||
value: Object<Profile>,
|
||||
}
|
||||
|
||||
fn fault(_: String) -> Fault {
|
||||
&FaultInfo {
|
||||
message: "command failure",
|
||||
source: "reference.dever",
|
||||
line: 1,
|
||||
column: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn greeting_generated(values: &[&str]) -> Result<String, Fault> {
|
||||
GENERATED_GREETING(values.first().copied().unwrap_or(r#"{"name":""}"#)).map_err(fault)
|
||||
}
|
||||
|
||||
fn greeting_reference(values: &[&str]) -> Result<String, Fault> {
|
||||
let input: Object<Greeting> =
|
||||
wire::decode(values.first().copied().unwrap_or(r#"{"name":""}"#)).map_err(fault)?;
|
||||
let mut result = String::with_capacity(input.0.name.len() + 9);
|
||||
result.push_str("[Hello, ");
|
||||
result.push_str(&input.0.name);
|
||||
result.push(']');
|
||||
wire::response(&result).map_err(fault)
|
||||
}
|
||||
|
||||
fn echo_generated(values: &[&str]) -> Result<String, Fault> {
|
||||
GENERATED_ECHO(
|
||||
values
|
||||
.first()
|
||||
.copied()
|
||||
.unwrap_or(r#"{"value":{"id":"","name":"","scores":[]}}"#),
|
||||
)
|
||||
.map_err(fault)
|
||||
}
|
||||
|
||||
fn echo_reference(values: &[&str]) -> Result<String, Fault> {
|
||||
let input: Object<Echo> = wire::decode(
|
||||
values
|
||||
.first()
|
||||
.copied()
|
||||
.unwrap_or(r#"{"value":{"id":"","name":"","scores":[]}}"#),
|
||||
)
|
||||
.map_err(fault)?;
|
||||
let result = input.0.value.0;
|
||||
if result.ratio.is_some_and(|value| !value.is_finite()) {
|
||||
return Err(fault("non-finite Float".into()));
|
||||
}
|
||||
wire::response(&result).map_err(fault)
|
||||
}
|
||||
|
||||
fn double_generated(values: &[&str]) -> Result<String, Fault> {
|
||||
GENERATED_DOUBLE(values.first().copied().unwrap_or(r#"{"value":0}"#)).map_err(fault)
|
||||
}
|
||||
|
||||
fn double_reference(values: &[&str]) -> Result<String, Fault> {
|
||||
let input: Object<Integer> =
|
||||
wire::decode(values.first().copied().unwrap_or(r#"{"value":0}"#)).map_err(fault)?;
|
||||
let result = input
|
||||
.0
|
||||
.value
|
||||
.checked_mul(2)
|
||||
.ok_or_else(|| fault("overflow".into()))?;
|
||||
wire::response(&result).map_err(fault)
|
||||
}
|
||||
|
||||
// 独立对照只处理这里的固定标量 record;结构预算由这两个协议本身保证。
|
||||
// 不调用 Dever 的 object/decode/response,也不替换原来的共享运行库对照。
|
||||
fn rust_input<Value: serde::de::DeserializeOwned>(input: &str) -> Result<Value, Fault> {
|
||||
if input.len() > 16 * 1024 * 1024 {
|
||||
return Err(fault("input limit".into()));
|
||||
}
|
||||
struct Record<Value>(std::marker::PhantomData<Value>);
|
||||
impl<'input, Value: Deserialize<'input>> serde::de::Visitor<'input> for Record<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))
|
||||
}
|
||||
}
|
||||
let mut decoder = serde_json::Deserializer::from_str(input);
|
||||
let value =
|
||||
serde::Deserializer::deserialize_map(&mut decoder, Record(std::marker::PhantomData))
|
||||
.map_err(|error| fault(error.to_string()))?;
|
||||
decoder.end().map_err(|error| fault(error.to_string()))?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn rust_response<Value: Serialize>(data: Value) -> Result<String, Fault> {
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde")]
|
||||
struct Response<Value> {
|
||||
code: u16,
|
||||
message: &'static str,
|
||||
data: Value,
|
||||
}
|
||||
struct Buffer(Vec<u8>);
|
||||
impl std::io::Write for Buffer {
|
||||
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 limit"));
|
||||
}
|
||||
self.0.extend_from_slice(bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
// 与 serde_json::to_vec 相同;独立 Rust 参照也避免短输出反复扩容。
|
||||
let mut output = Buffer(Vec::with_capacity(128));
|
||||
serde_json::to_writer(
|
||||
&mut output,
|
||||
&Response {
|
||||
code: 0,
|
||||
message: "ok",
|
||||
data,
|
||||
},
|
||||
)
|
||||
.map_err(|error| fault(error.to_string()))?;
|
||||
Ok(String::from_utf8(output.0).unwrap())
|
||||
}
|
||||
|
||||
fn greeting_rust(values: &[&str]) -> Result<String, Fault> {
|
||||
let input: Greeting = rust_input(values.first().copied().unwrap_or(r#"{"name":""}"#))?;
|
||||
let mut result = String::with_capacity(input.name.len() + 9);
|
||||
result.push_str("[Hello, ");
|
||||
result.push_str(&input.name);
|
||||
result.push(']');
|
||||
rust_response(result)
|
||||
}
|
||||
|
||||
fn double_rust(values: &[&str]) -> Result<String, Fault> {
|
||||
let input: Integer = rust_input(values.first().copied().unwrap_or(r#"{"value":0}"#))?;
|
||||
rust_response(
|
||||
input
|
||||
.value
|
||||
.checked_mul(2)
|
||||
.ok_or_else(|| fault("overflow".into()))?,
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for input in [
|
||||
r#"{"value":9223372036854775807}"#,
|
||||
r#"{"value":1,"value":2}"#,
|
||||
"[1]",
|
||||
"{}",
|
||||
] {
|
||||
assert_eq!(
|
||||
double_generated(&[input]).is_err(),
|
||||
double_reference(&[input]).is_err()
|
||||
);
|
||||
assert_eq!(
|
||||
double_generated(&[input]).is_err(),
|
||||
double_rust(&[input]).is_err()
|
||||
);
|
||||
}
|
||||
for input in [
|
||||
r#"{"value":["a","b",[],null]}"#,
|
||||
r#"{"value":{"id":"a","name":"b","scores":[],"extra":1}}"#,
|
||||
] {
|
||||
assert_eq!(
|
||||
echo_generated(&[input]).is_err(),
|
||||
echo_reference(&[input]).is_err()
|
||||
);
|
||||
}
|
||||
let long_name = "雪".repeat(2048);
|
||||
let long_greeting = format!("{{\"name\":\"{long_name}\"}}");
|
||||
let echo = format!(
|
||||
"{{\"value\":{{\"id\":\"a\",\"name\":\"{long_name}\",\"scores\":[1,2,3],\"ratio\":0.5}}}}"
|
||||
);
|
||||
println!("case,sample,implementation,elapsed_ns,checksum");
|
||||
compare(
|
||||
"command_greet_short",
|
||||
&[r#"{"name":"Dever"}"#],
|
||||
50000,
|
||||
greeting_generated,
|
||||
greeting_reference,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
compare(
|
||||
"command_greet_long",
|
||||
&[long_greeting.as_str()],
|
||||
3000,
|
||||
greeting_generated,
|
||||
greeting_reference,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
compare(
|
||||
"command_echo",
|
||||
&[echo.as_str()],
|
||||
3000,
|
||||
echo_generated,
|
||||
echo_reference,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
compare(
|
||||
"command_double",
|
||||
&[r#"{"value":21}"#],
|
||||
50000,
|
||||
double_generated,
|
||||
double_reference,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
compare(
|
||||
"command_greet_short_independent",
|
||||
&[r#"{"name":"Dever"}"#],
|
||||
50000,
|
||||
greeting_generated,
|
||||
greeting_rust,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
compare(
|
||||
"command_greet_long_independent",
|
||||
&[long_greeting.as_str()],
|
||||
3000,
|
||||
greeting_generated,
|
||||
greeting_rust,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
compare(
|
||||
"command_double_independent",
|
||||
&[r#"{"value":21}"#],
|
||||
50000,
|
||||
double_generated,
|
||||
double_rust,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// 使用 auth 样例实际生成的 RestCreate0;独立 Rust 直接复用 Serde,合同相同。
|
||||
// 测量仅包含请求对象解码,不包括字段业务约束、认证、SQL 或网络。
|
||||
use dever_runtime::wire::serde::{self, Deserialize};
|
||||
use dever_runtime::wire::serde_json;
|
||||
|
||||
fn fault(_: impl std::fmt::Display) -> Fault {
|
||||
&FaultInfo { message: "REST input failure", source: "rest-reference", line: 1, column: 1 }
|
||||
}
|
||||
|
||||
enum Status { Active, Disabled }
|
||||
|
||||
impl<'input> Deserialize<'input> for Status {
|
||||
fn deserialize<Decoder: serde::Deserializer<'input>>(decoder: Decoder) -> Result<Self, Decoder::Error> {
|
||||
struct Choice;
|
||||
impl<'input> serde::de::Visitor<'input> for Choice {
|
||||
type Value = Status;
|
||||
fn expecting(&self, output: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { output.write_str("a status name") }
|
||||
fn visit_str<Error: serde::de::Error>(self, value: &str) -> Result<Status, Error> {
|
||||
match value {
|
||||
"Active" => Ok(Status::Active),
|
||||
"Disabled" => Ok(Status::Disabled),
|
||||
_ => Err(Error::custom("unknown status")),
|
||||
}
|
||||
}
|
||||
}
|
||||
decoder.deserialize_str(Choice)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
||||
struct Profile {
|
||||
name: String,
|
||||
bio: Option<String>,
|
||||
#[serde(default = "rank")]
|
||||
rank: i64,
|
||||
#[serde(default = "tag")]
|
||||
tag: Option<String>,
|
||||
#[serde(default = "status")]
|
||||
status: Status,
|
||||
}
|
||||
|
||||
fn rank() -> i64 { 7 }
|
||||
fn tag() -> Option<String> { Some("automatic".into()) }
|
||||
fn status() -> Status { Status::Active }
|
||||
|
||||
fn generated(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else { return Ok(String::new()); };
|
||||
let value: RestCreate0 = REST_DECODER(source).map_err(fault)?;
|
||||
Ok(value.field0)
|
||||
}
|
||||
|
||||
fn reference(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else { return Ok(String::new()); };
|
||||
if source.len() > 16 * 1024 * 1024 { return Err(fault("byte limit")); }
|
||||
struct Object;
|
||||
impl<'input> serde::de::Visitor<'input> for Object {
|
||||
type Value = Profile;
|
||||
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<Profile, Access::Error> {
|
||||
Profile::deserialize(serde::de::value::MapAccessDeserializer::new(access))
|
||||
}
|
||||
}
|
||||
let mut decoder = serde_json::Deserializer::from_str(source);
|
||||
let value = serde::Deserializer::deserialize_map(&mut decoder, Object).map_err(fault)?;
|
||||
decoder.end().map_err(fault)?;
|
||||
// 固定五个标量字段,类型已证明层数和值数;保留其正常构造与释放。
|
||||
let Profile { name, bio, rank, tag, status } = value;
|
||||
drop((bio, rank, tag, status));
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("workload,sample,implementation,nanos,checksum");
|
||||
for source in [
|
||||
r#"{"name":"Dever"}"#,
|
||||
r#"{"name":"Dever","bio":null,"tag":null,"status":"Disabled"}"#,
|
||||
] {
|
||||
assert_eq!(generated(&[source.into()]).unwrap(), "Dever");
|
||||
assert_eq!(reference(&[source.into()]).unwrap(), "Dever");
|
||||
}
|
||||
for source in [
|
||||
"[]", "null", "{}", r#"{"name":"x","name":"y"}"#,
|
||||
r#"{"name":"x","na\u006de":"y"}"#, r#"{"name":"x","extra":1}"#,
|
||||
r#"{"name":"x","rank":null}"#, r#"{"name":"x","status":"Other"}"#,
|
||||
r#"{"name":"x","status":{"Active":null}}"#,
|
||||
r#"{"name":"x","bio":"\ud800"}"#, r#"{"name":"x"} null"#,
|
||||
] {
|
||||
assert!(generated(&[source.into()]).is_err(), "{source}");
|
||||
assert!(reference(&[source.into()]).is_err(), "{source}");
|
||||
}
|
||||
for (name, bio) in [
|
||||
("short", "x".repeat(16)),
|
||||
("plain_4096", "x".repeat(4096)),
|
||||
("plain_65536", "x".repeat(65536)),
|
||||
("escaped", "雪\"\\\n".repeat(1024)),
|
||||
] {
|
||||
let input = vec![serde_json::json!({"name":"Dever","bio":bio,"rank":7,"tag":"automatic","status":"Active"}).to_string()];
|
||||
compare(&format!("rest_input_{name}"), &input, 1000, generated, reference, |name| name.len() as u64);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
use dever_runtime::wire::serde::{self, Deserialize};
|
||||
use dever_runtime::wire::serde_json::{self, value::RawValue};
|
||||
|
||||
fn fault(_: impl std::fmt::Display) -> Fault {
|
||||
&FaultInfo {
|
||||
message: "REST JSON input",
|
||||
source: "rest-json-reference",
|
||||
line: 1,
|
||||
column: 1,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
||||
struct Fields {
|
||||
value: Box<RawValue>,
|
||||
extra: Option<Box<RawValue>>,
|
||||
}
|
||||
|
||||
fn generated(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let value: RAW_ARGS = dever_runtime::wire::deserialize(source).map_err(fault)?;
|
||||
Ok(value.field0.into_text())
|
||||
}
|
||||
|
||||
fn reference(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
struct Object;
|
||||
impl<'input> serde::de::Visitor<'input> for Object {
|
||||
type Value = Fields;
|
||||
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<Fields, Access::Error> {
|
||||
Fields::deserialize(serde::de::value::MapAccessDeserializer::new(access))
|
||||
}
|
||||
}
|
||||
let mut decoder = serde_json::Deserializer::from_str(source);
|
||||
let value = serde::Deserializer::deserialize_map(&mut decoder, Object).map_err(fault)?;
|
||||
decoder.end().map_err(fault)?;
|
||||
drop(value.extra);
|
||||
Ok(Box::<str>::from(value.value).into())
|
||||
}
|
||||
|
||||
// 保留原始参照,单独改变入口或字段表示,定位短输入的固定开销。
|
||||
fn generated_decoder(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let mut decoder = serde_json::Deserializer::from_str(source);
|
||||
let value = RAW_ARGS::deserialize(&mut decoder).map_err(fault)?;
|
||||
decoder.end().map_err(fault)?;
|
||||
Ok(value.field0.into_text())
|
||||
}
|
||||
|
||||
struct RawFields(Fields);
|
||||
|
||||
impl<'input> Deserialize<'input> for RawFields {
|
||||
fn deserialize<Decoder: serde::Deserializer<'input>>(
|
||||
decoder: Decoder,
|
||||
) -> Result<Self, Decoder::Error> {
|
||||
struct Object;
|
||||
impl<'input> serde::de::Visitor<'input> for Object {
|
||||
type Value = RawFields;
|
||||
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<RawFields, Access::Error> {
|
||||
Fields::deserialize(serde::de::value::MapAccessDeserializer::new(access))
|
||||
.map(RawFields)
|
||||
}
|
||||
}
|
||||
decoder.deserialize_map(Object)
|
||||
}
|
||||
}
|
||||
|
||||
fn reference_entry(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let value: RawFields = serde_json::from_str(source).map_err(fault)?;
|
||||
Ok(Box::<str>::from(value.0.value).into())
|
||||
}
|
||||
|
||||
fn raw_text<'input, Decoder: serde::Deserializer<'input>>(
|
||||
decoder: Decoder,
|
||||
) -> Result<String, Decoder::Error> {
|
||||
Box::<RawValue>::deserialize(decoder).map(|raw| Box::<str>::from(raw).into())
|
||||
}
|
||||
|
||||
fn optional_raw_text<'input, Decoder: serde::Deserializer<'input>>(
|
||||
decoder: Decoder,
|
||||
) -> Result<Option<String>, Decoder::Error> {
|
||||
Option::<Box<RawValue>>::deserialize(decoder)
|
||||
.map(|raw| raw.map(|raw| Box::<str>::from(raw).into()))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
|
||||
struct TextFields {
|
||||
#[serde(deserialize_with = "raw_text")]
|
||||
value: String,
|
||||
#[serde(default, deserialize_with = "optional_raw_text")]
|
||||
extra: Option<String>,
|
||||
}
|
||||
|
||||
fn reference_text(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
struct Object;
|
||||
impl<'input> serde::de::Visitor<'input> for Object {
|
||||
type Value = TextFields;
|
||||
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<TextFields, Access::Error> {
|
||||
TextFields::deserialize(serde::de::value::MapAccessDeserializer::new(access))
|
||||
}
|
||||
}
|
||||
let mut decoder = serde_json::Deserializer::from_str(source);
|
||||
let value = serde::Deserializer::deserialize_map(&mut decoder, Object).map_err(fault)?;
|
||||
decoder.end().map_err(fault)?;
|
||||
drop(value.extra);
|
||||
Ok(value.value)
|
||||
}
|
||||
|
||||
fn generated_output(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let value: RAW_ARGS = dever_runtime::wire::deserialize(source).map_err(fault)?;
|
||||
GENERATED_RAW_ENCODE(&value.field0).map_err(fault)
|
||||
}
|
||||
|
||||
fn reference_output(values: &[String]) -> Result<String, Fault> {
|
||||
let Some(source) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let value: RawFields = serde_json::from_str(source).map_err(fault)?;
|
||||
serde_json::to_string(&value.0.value).map_err(fault)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if std::env::args().any(|argument| argument == "--output-only") {
|
||||
println!("workload,sample,implementation,nanos,checksum");
|
||||
output::run();
|
||||
return;
|
||||
}
|
||||
let short_only = std::env::args().any(|argument| argument == "--short");
|
||||
eprintln!(
|
||||
"layout,json={},string={},raw={}",
|
||||
std::mem::size_of::<dever_runtime::wire::json::Json>(),
|
||||
std::mem::size_of::<String>(),
|
||||
std::mem::size_of::<Box<RawValue>>()
|
||||
);
|
||||
println!("workload,sample,implementation,nanos,checksum");
|
||||
for (name, text) in [
|
||||
("short", "x".repeat(16)),
|
||||
("plain_4096", "x".repeat(4096)),
|
||||
("plain_65536", "x".repeat(65536)),
|
||||
("escaped", "雪\"\\\n".repeat(1024)),
|
||||
] {
|
||||
if short_only && name != "short" {
|
||||
continue;
|
||||
}
|
||||
let raw = format!(
|
||||
"{{\"text\":{},\"number\":1e9999}}",
|
||||
serde_json::to_string(&text).unwrap()
|
||||
);
|
||||
let source = format!("{{\"value\":{raw},\"extra\":{raw}}}");
|
||||
// 整体请求校验在两侧计时外完成;这里单独比较已校验请求的字段解码。
|
||||
let checked: RAW_ARGS = dever_runtime::wire::decode(&source).unwrap();
|
||||
assert_eq!(checked.field0.as_str(), raw);
|
||||
assert_eq!(checked.field1.unwrap().as_str(), raw);
|
||||
// 短输入每组至少持续数十毫秒,避免亚毫秒样本的调度噪声主导百分比。
|
||||
let count = if name == "short" { 100000 } else { 1000 };
|
||||
let values = [source];
|
||||
compare(
|
||||
&format!("rest_json_{name}"),
|
||||
&values,
|
||||
count,
|
||||
generated,
|
||||
reference,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
compare(
|
||||
&format!("rest_json_output_{name}"),
|
||||
&values,
|
||||
count,
|
||||
generated_output,
|
||||
reference_output,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
}
|
||||
let raw = r#"{"text":"xxxxxxxxxxxxxxxx","number":1e9999}"#;
|
||||
let input = [format!("{{\"value\":{raw},\"extra\":{raw}}}")];
|
||||
for (name, implementation) in [
|
||||
(
|
||||
"decoder",
|
||||
generated_decoder as fn(&[String]) -> Result<String, Fault>,
|
||||
),
|
||||
("raw_from_str", reference_entry),
|
||||
("text_decoder", reference_text),
|
||||
] {
|
||||
if short_only && name != "decoder" {
|
||||
continue;
|
||||
}
|
||||
compare(
|
||||
&format!("rest_json_control_{name}"),
|
||||
&input,
|
||||
100000,
|
||||
implementation,
|
||||
reference,
|
||||
|value| value.len() as u64,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
use chrono::Datelike;
|
||||
use dever_runtime::wire::json::Json;
|
||||
use dever_runtime::wire::serde::{Serialize, Serializer};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Timestamp(i64);
|
||||
|
||||
impl Serialize for Timestamp {
|
||||
fn serialize<Encoder: Serializer>(
|
||||
&self,
|
||||
encoder: Encoder,
|
||||
) -> Result<Encoder::Ok, Encoder::Error> {
|
||||
let value = chrono::DateTime::from_timestamp_millis(self.0)
|
||||
.filter(|value| (0..=9999).contains(&value.year()))
|
||||
.ok_or_else(|| dever_runtime::wire::serde::ser::Error::custom("invalid timestamp"))?;
|
||||
let pattern = if value.timestamp_subsec_millis() == 0 {
|
||||
"%Y-%m-%dT%H:%M:%SZ"
|
||||
} else {
|
||||
"%Y-%m-%dT%H:%M:%S%.3fZ"
|
||||
};
|
||||
encoder.collect_str(&value.naive_utc().format(pattern))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde")]
|
||||
struct Row {
|
||||
id: i64,
|
||||
created_at: Timestamp,
|
||||
value: Box<RawValue>,
|
||||
extra: Option<Box<RawValue>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde")]
|
||||
struct Reply<Value> {
|
||||
code: u16,
|
||||
message: &'static str,
|
||||
data: Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "dever_runtime::wire::serde")]
|
||||
struct Page<Items> {
|
||||
items: Items,
|
||||
page: i64,
|
||||
size: i64,
|
||||
total: i64,
|
||||
pages: i64,
|
||||
}
|
||||
|
||||
struct Case {
|
||||
dever: Vec<RAW_MODEL>,
|
||||
rust: Vec<Row>,
|
||||
}
|
||||
|
||||
fn detail(values: &[Case]) -> Result<String, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let row = &value.dever[0];
|
||||
REST_DETAIL_ENCODE.map_err(fault)
|
||||
}
|
||||
|
||||
fn page(values: &[Case]) -> Result<String, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let page = Page {
|
||||
items: value.dever.as_slice(),
|
||||
page: 1,
|
||||
size: 20,
|
||||
total: value.dever.len() as i64,
|
||||
pages: 1,
|
||||
};
|
||||
REST_PAGE_ENCODE.map_err(fault)
|
||||
}
|
||||
|
||||
fn encode<Value: Serialize>(data: Value, count: usize) -> Result<String, Fault> {
|
||||
if count > 65_536 {
|
||||
return Err(fault("value budget"));
|
||||
}
|
||||
let text = serde_json::to_string(&Reply {
|
||||
code: 0,
|
||||
message: "ok",
|
||||
data,
|
||||
})
|
||||
.map_err(fault)?;
|
||||
if text.len() > 16 * 1024 * 1024 {
|
||||
return Err(fault("byte budget"));
|
||||
}
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
// 固定夹具中的 Json 是两个标量字段的对象;预算在初次构造后复用。
|
||||
// 独立 Rust 的字段编码、原文输出及响应封装不调用 Dever 运行库。
|
||||
fn row_values(row: &Row) -> usize {
|
||||
if row.extra.is_some() { 9 } else { 7 }
|
||||
}
|
||||
|
||||
fn rust_detail(values: &[Case]) -> Result<String, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
encode(&value.rust[0], 3 + row_values(&value.rust[0]))
|
||||
}
|
||||
|
||||
fn rust_page(values: &[Case]) -> Result<String, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(String::new());
|
||||
};
|
||||
let page = Page {
|
||||
items: value.rust.as_slice(),
|
||||
page: 1,
|
||||
size: 20,
|
||||
total: value.rust.len() as i64,
|
||||
pages: 1,
|
||||
};
|
||||
// 五层容器固定小于 64;根、响应字段、分页对象与数组共九个值。
|
||||
encode(page, 9 + value.rust.iter().map(row_values).sum::<usize>())
|
||||
}
|
||||
|
||||
pub(super) fn run() {
|
||||
for (name, text) in [
|
||||
("short", "x".repeat(16)),
|
||||
("plain_4096", "x".repeat(4096)),
|
||||
("plain_65536", "x".repeat(65536)),
|
||||
("escaped", "雪\"\\\n".repeat(1024)),
|
||||
] {
|
||||
let source = format!(
|
||||
"{{\"text\":{},\"number\":1e9999}}",
|
||||
serde_json::to_string(&text).unwrap()
|
||||
);
|
||||
let dever = (0..16)
|
||||
.map(|index| RAW_MODEL {
|
||||
field0: dever_runtime::database::Id::<RAW_MODEL_ID>(index + 1),
|
||||
field1: dever_runtime::time::calendar::DateTime::from_millis(0).unwrap(),
|
||||
field2: Json::parse(&source).unwrap(),
|
||||
field3: (index % 2 == 0).then(|| Json::parse(&source).unwrap()),
|
||||
})
|
||||
.collect();
|
||||
let rust = (0..16)
|
||||
.map(|index| Row {
|
||||
id: index + 1,
|
||||
created_at: Timestamp(0),
|
||||
value: serde_json::from_str(&source).unwrap(),
|
||||
extra: (index % 2 == 0).then(|| serde_json::from_str(&source).unwrap()),
|
||||
})
|
||||
.collect();
|
||||
let values = [Case { dever, rust }];
|
||||
compare_named(
|
||||
&format!("rest_detail_output_{name}"),
|
||||
&values,
|
||||
if name == "short" { 10000 } else { 1000 },
|
||||
detail,
|
||||
rust_detail,
|
||||
|text| text.len() as u64,
|
||||
"rust_bounded",
|
||||
);
|
||||
compare_named(
|
||||
&format!("rest_page_output_{name}"),
|
||||
&values,
|
||||
if name == "short" { 1000 } else { 100 },
|
||||
page,
|
||||
rust_page,
|
||||
|text| text.len() as u64,
|
||||
"rust_bounded",
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user