use dever_runtime::wire::{ self, serde::{self, Deserialize, Serialize}, }; struct Object(Value); impl<'input, Value: Deserialize<'input>> Deserialize<'input> for Object { fn deserialize>( decoder: Decoder, ) -> Result { 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, ratio: Option, } #[derive(Deserialize)] #[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)] struct Echo { value: Object, } fn fault(_: String) -> Fault { &FaultInfo { message: "command failure", source: "reference.dever", line: 1, column: 1, } } fn greeting_generated(values: &[&str]) -> Result { GENERATED_GREETING(values.first().copied().unwrap_or(r#"{"name":""}"#)).map_err(fault) } fn greeting_reference(values: &[&str]) -> Result { let input: Object = 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 { GENERATED_ECHO( values .first() .copied() .unwrap_or(r#"{"value":{"id":"","name":"","scores":[]}}"#), ) .map_err(fault) } fn echo_reference(values: &[&str]) -> Result { let input: Object = 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 { GENERATED_DOUBLE(values.first().copied().unwrap_or(r#"{"value":0}"#)).map_err(fault) } fn double_reference(values: &[&str]) -> Result { let input: Object = 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(input: &str) -> Result { if input.len() > 16 * 1024 * 1024 { return Err(fault("input limit".into())); } struct Record(std::marker::PhantomData); impl<'input, Value: Deserialize<'input>> serde::de::Visitor<'input> for Record { type Value = Value; fn expecting(&self, output: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { output.write_str("an object") } fn visit_map>( self, access: Access, ) -> Result { 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(data: Value) -> Result { #[derive(Serialize)] #[serde(crate = "dever_runtime::wire::serde")] struct Response { code: u16, message: &'static str, data: Value, } struct Buffer(Vec); impl std::io::Write for Buffer { fn write(&mut self, bytes: &[u8]) -> std::io::Result { 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 { 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 { 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, ); }