// 对比实际运行库、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, } #[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( values: &[QueryInput], ) -> Result, 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( values: &[QueryInput], ) -> Result, Fault> { let Some(input) = values.first() else { return Ok(None); }; Query::::try_from_uri(&input.uri) .map(|Query(arguments)| Some(arguments)) .map_err(query_fault) } fn serde_query( values: &[QueryInput], ) -> Result, 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( name: &str, query: &str, count: usize, ) { let input = [query_input(query)]; for (label, decoder) in [ ("axum", axum_query:: as fn(&[QueryInput]) -> _), ( "serde_urlencoded", serde_query:: as fn(&[QueryInput]) -> _, ), ] { compare_named( &format!("query_{name}_{label}"), &input, count, runtime_query::, 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::( 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::>() .join("&"); query_comparison::(name, &query, count); } }