Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,167 @@
|
||||
// 对比实际运行库、Axum Query 和其底层 serde_urlencoded;不包含网络或路由耗时。
|
||||
// 默认库的错误输入行为另行检查,合法输入计时不能证明完整边界语义等价。
|
||||
use axum::{extract::Query, http::Uri};
|
||||
use dever_runtime::api::{self, Request};
|
||||
use serde::{Deserialize, de::DeserializeOwned};
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct QueryArguments {
|
||||
name: String,
|
||||
count: i64,
|
||||
enabled: bool,
|
||||
ratio: f64,
|
||||
tag: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct WideArguments {
|
||||
field0: String,
|
||||
field1: String,
|
||||
field2: String,
|
||||
field3: String,
|
||||
field4: String,
|
||||
field5: String,
|
||||
field6: String,
|
||||
field7: String,
|
||||
field8: String,
|
||||
field9: String,
|
||||
field10: String,
|
||||
field11: String,
|
||||
field12: String,
|
||||
field13: String,
|
||||
field14: String,
|
||||
field15: String,
|
||||
field16: String,
|
||||
field17: String,
|
||||
field18: String,
|
||||
field19: String,
|
||||
field20: String,
|
||||
field21: String,
|
||||
field22: String,
|
||||
field23: String,
|
||||
field24: String,
|
||||
field25: String,
|
||||
field26: String,
|
||||
field27: String,
|
||||
field28: String,
|
||||
field29: String,
|
||||
field30: String,
|
||||
field31: String,
|
||||
}
|
||||
|
||||
struct QueryInput {
|
||||
request: Request,
|
||||
uri: Uri,
|
||||
}
|
||||
|
||||
fn query_fault(_: impl std::fmt::Display) -> Fault {
|
||||
&FaultInfo {
|
||||
message: "invalid query",
|
||||
source: "query-comparison",
|
||||
line: 1,
|
||||
column: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn runtime_query<Arguments: DeserializeOwned>(
|
||||
values: &[QueryInput],
|
||||
) -> Result<Option<Arguments>, Fault> {
|
||||
let Some(input) = values.first() else {
|
||||
return Ok(None);
|
||||
};
|
||||
api::input(&input.request)
|
||||
.map_err(query_fault)?
|
||||
.decode(dever_runtime::wire::decode)
|
||||
.map(Some)
|
||||
.map_err(query_fault)
|
||||
}
|
||||
|
||||
fn axum_query<Arguments: DeserializeOwned>(
|
||||
values: &[QueryInput],
|
||||
) -> Result<Option<Arguments>, Fault> {
|
||||
let Some(input) = values.first() else {
|
||||
return Ok(None);
|
||||
};
|
||||
Query::<Arguments>::try_from_uri(&input.uri)
|
||||
.map(|Query(arguments)| Some(arguments))
|
||||
.map_err(query_fault)
|
||||
}
|
||||
|
||||
fn serde_query<Arguments: DeserializeOwned>(
|
||||
values: &[QueryInput],
|
||||
) -> Result<Option<Arguments>, Fault> {
|
||||
let Some(input) = values.first() else {
|
||||
return Ok(None);
|
||||
};
|
||||
serde_urlencoded::from_str(input.uri.query().unwrap_or_default())
|
||||
.map(Some)
|
||||
.map_err(query_fault)
|
||||
}
|
||||
|
||||
fn query_input(query: &str) -> QueryInput {
|
||||
let target = format!("/test?{query}");
|
||||
QueryInput {
|
||||
uri: target.parse().unwrap(),
|
||||
request: axum::http::Request::builder()
|
||||
.uri(target)
|
||||
.body(Default::default())
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn query_comparison<Arguments: DeserializeOwned + PartialEq + std::fmt::Debug>(
|
||||
name: &str,
|
||||
query: &str,
|
||||
count: usize,
|
||||
) {
|
||||
let input = [query_input(query)];
|
||||
for (label, decoder) in [
|
||||
("axum", axum_query::<Arguments> as fn(&[QueryInput]) -> _),
|
||||
(
|
||||
"serde_urlencoded",
|
||||
serde_query::<Arguments> as fn(&[QueryInput]) -> _,
|
||||
),
|
||||
] {
|
||||
compare_named(
|
||||
&format!("query_{name}_{label}"),
|
||||
&input,
|
||||
count,
|
||||
runtime_query::<Arguments>,
|
||||
decoder,
|
||||
|output| u64::from(output.is_some()),
|
||||
label,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if std::env::args().any(|argument| argument == "--check") {
|
||||
query_checks();
|
||||
return;
|
||||
}
|
||||
println!("case,sample,implementation,elapsed_ns,checksum");
|
||||
for (name, value, count) in [
|
||||
("plain", "Alice".to_owned(), 20_000),
|
||||
("unicode", "%E9%9B%AA+test".to_owned(), 20_000),
|
||||
("escaped", "%22%5C%00%26%3D%25".repeat(8), 10_000),
|
||||
("long", "value%2B%E9%9B%AA".repeat(256), 1_000),
|
||||
] {
|
||||
query_comparison::<QueryArguments>(
|
||||
name,
|
||||
&format!("name={value}&count=-7&enabled=true&ratio=1.25&tag=demo"),
|
||||
count,
|
||||
);
|
||||
}
|
||||
for (name, value, count) in [
|
||||
("wide_plain", "testvalue".to_owned(), 2_000),
|
||||
("wide_escaped", "value%2B%E9%9B%AA".repeat(8), 500),
|
||||
] {
|
||||
let query = (0..32)
|
||||
.map(|index| format!("field{index}={value}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("&");
|
||||
query_comparison::<WideArguments>(name, &query, count);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user