Files
core/test/rust/performance/json/raw.rs
T
2026-11-03 03:32:07 +00:00

265 lines
10 KiB
Rust

// 两侧从已验证的原始值开始,独立参照直接序列化 Serde RawValue。
// 此组只比较原文输出;构造与更新成本由现有 json 组同时检查。
struct RawOutputCase {
dever: Json,
rust: Box<RawValue>,
values: usize,
depth: usize,
}
fn runtime_raw_output(values: &[RawOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
Ok(value.dever.as_str().trim_ascii().to_owned())
})
}
fn native_raw_output(values: &[RawOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
serde_json::to_string(&value.rust).map_err(json_fault)
})
}
fn runtime_nested_output(values: &[RawOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
Json::array(std::slice::from_ref(&value.dever)).map(Json::into_text).map_err(json_fault)
})
}
fn native_nested_output(values: &[RawOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
if value.values + 1 > 65_536 || value.depth + 1 > 64 || value.rust.get().len() + 2 > 16 * 1024 * 1024 {
return Err(json_fault("JSON budget"));
}
serde_json::to_string(&[&value.rust]).map_err(json_fault)
})
}
fn generated_nested_array_output(values: &[RawOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
GENERATED_ARRAY(std::slice::from_ref(&value.dever)).map_err(json_fault)
})
}
fn runtime_from_text(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
Json::parse(value).map(Json::into_text).map_err(json_fault)
})
}
fn native_from_text(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
serde_json::from_str::<Box<RawValue>>(value)
.map(|raw| Box::<str>::from(raw).into()).map_err(json_fault)
})
}
fn runtime_from_text_nested(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
let raw = Json::parse(value).map_err(json_fault)?;
Json::array(&[raw]).map(Json::into_text).map_err(json_fault)
})
}
fn native_from_text_nested(values: &[String]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
let raw: Box<RawValue> = serde_json::from_str(value).map_err(json_fault)?;
serde_json::to_string(&[raw]).map_err(json_fault)
})
}
fn from_text_costs(name: &str, source: &str, values: &[RawOutputCase]) {
let count = if name == "short" { 100_000 } else { 1000 };
compare_named(
&format!("json_nested_output_{name}"), values, count,
runtime_nested_output, native_nested_output,
|value| value.len() as u64, "rust_serde",
);
compare_named(
&format!("json_dever_array_output_{name}"), values, count,
generated_nested_array_output, native_nested_output,
|value| value.len() as u64, "rust_serde",
);
// 原生参照仅检查 JSON 语法,不包含 Dever 的去重和预算,不能据此宣称合同追平。
let sources = [source.to_owned()];
compare_named(
&format!("json_from_text_{name}"), &sources, count,
runtime_from_text, native_from_text,
|value| value.len() as u64, "rust_syntax_only",
);
compare_named(
&format!("json_from_text_nested_{name}"), &sources, count,
runtime_from_text_nested, native_from_text_nested,
|value| value.len() as u64, "rust_syntax_only",
);
}
fn raw_output_costs() {
nested_output_costs();
array_output_costs();
for (name, text) in [
("short", "a".repeat(64)),
("plain_4096", "a".repeat(4096)),
("plain_65536", "a".repeat(65536)),
("escaped", "雪\"\\\n".repeat(1024)),
] {
let source = format!(
"{{\"text\":{},\"number\":1e9999}}",
serde_json::to_string(&text).unwrap()
);
let values = [RawOutputCase {
dever: Json::parse(&source).unwrap(),
rust: serde_json::from_str(&source).unwrap(),
// 上面构造的对象恰好包含 text、number 两个标量字段。
values: 3,
depth: 1,
}];
assert_eq!(runtime_raw_output(&values).unwrap(), source);
assert_eq!(native_raw_output(&values).unwrap(), source);
from_text_costs(name, &source, &values);
compare_named(
&format!("json_raw_output_{name}"),
&values,
1000,
runtime_raw_output,
native_raw_output,
|value| value.len() as u64,
"rust_serde",
);
// 输入解码已经取得 RawValue;输出不应丢掉它后再从 String 解析。
let decoded = [RawOutputCase {
dever: serde_json::from_str(&source).unwrap(),
rust: serde_json::from_str(&source).unwrap(),
values: 3,
depth: 1,
}];
compare_named(
&format!("json_decoded_output_{name}"),
&decoded,
1000,
runtime_raw_output,
native_raw_output,
|value| value.len() as u64,
"rust_serde",
);
}
}
struct ArrayOutputCase {
dever: Vec<Json>,
rust: Vec<NativeArrayValue>,
}
struct NativeArrayValue {
raw: Box<RawValue>,
values: usize,
depth: usize,
}
struct NativeArray<'value>(&'value [NativeArrayValue]);
impl Serialize for NativeArray<'_> {
fn serialize<Encoder: Serializer>(&self, encoder: Encoder) -> Result<Encoder::Ok, Encoder::Error> {
use dever_runtime::wire::serde::ser::{Error, SerializeSeq};
let mut sequence = encoder.serialize_seq(Some(self.0.len()))?;
let mut count = 1;
let mut bytes = 2;
for (index, value) in self.0.iter().enumerate() {
count += value.values;
bytes += value.raw.get().len() + usize::from(index != 0);
if count > 65_536 || value.depth + 1 > 64 || bytes > 16 * 1024 * 1024 {
return Err(Encoder::Error::custom("JSON budget"));
}
sequence.serialize_element(&value.raw)?;
}
sequence.end()
}
}
fn runtime_array_output(values: &[ArrayOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
Json::array(&value.dever).map(Json::into_text).map_err(json_fault)
})
}
fn native_array_output(values: &[ArrayOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
serde_json::to_string(&NativeArray(&value.rust)).map_err(json_fault)
})
}
fn generated_array_output(values: &[ArrayOutputCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| {
GENERATED_ARRAY(&value.dever).map_err(json_fault)
})
}
fn array_output_costs() {
for count in [0, 1, 4, 16] {
let sources: Vec<_> = [" null ", " [1,-0] ", r#" {"雪":"\u96ea"} "#, " [] "]
.into_iter().cycle().take(count).collect();
// 两侧每个子值都已验证且保留结构统计;计时包含合并预算和完整写出。
let values = [ArrayOutputCase {
dever: sources.iter().map(|source| Json::parse(source).unwrap()).collect(),
rust: sources.iter().map(|source| {
let (values, depth) = reference_shape(&serde_json::from_str(source).unwrap());
NativeArrayValue { raw: serde_json::from_str(source).unwrap(), values, depth }
}).collect(),
}];
compare_named(&format!("json_array_mixed_{count}"), &values, 100_000,
runtime_array_output, native_array_output, |value| value.len() as u64, "rust_bounded");
compare_named(&format!("json_dever_array_mixed_{count}"), &values, 100_000,
generated_array_output, native_array_output, |value| value.len() as u64, "rust_bounded");
}
}
struct NestedCase {
dever: Json,
rust: Box<RawValue>,
values: usize,
depth: usize,
}
fn runtime_nested_record(values: &[NestedCase]) -> Result<String, Fault> {
values.first().map_or(Ok(String::new()), |value| GENERATED_NESTED(&value.dever).map_err(json_fault))
}
fn native_nested_record(values: &[NestedCase]) -> Result<String, Fault> {
#[derive(Serialize)]
#[serde(crate = "dever_runtime::wire::serde")]
struct Nested<'value> { value: &'value RawValue }
let Some(value) = values.first() else { return Ok(String::new()); };
if value.values + 1 > 65_536 || value.depth + 1 > 64 || value.rust.get().len() + 10 > 16 * 1024 * 1024 {
return Err(json_fault("JSON budget"));
}
serde_json::to_string(&Nested { value: &value.rust }).map_err(json_fault)
}
fn reference_shape(value: &serde_json::Value) -> (usize, usize) {
let children: Vec<_> = match value {
serde_json::Value::Array(values) => values.iter().map(reference_shape).collect(),
serde_json::Value::Object(values) => values.values().map(reference_shape).collect(),
_ => return (1, 0),
};
(1 + children.iter().map(|child| child.0).sum::<usize>(), 1 + children.iter().map(|child| child.1).max().unwrap_or(0))
}
fn nested_output_costs() {
for (name, text) in [
("short", "a".repeat(64)),
("plain_4096", "a".repeat(4096)),
("plain_65536", "a".repeat(65536)),
("escaped", "雪\"\\\n".repeat(1024)),
] {
let source = format!("{{\"text\":{},\"number\":-0}}", serde_json::to_string(&text).unwrap());
// 两侧都从已验证原文及结构信息开始;独立 Rust 不调用 Dever 的验证或输出。
let (count, depth) = reference_shape(&serde_json::from_str(&source).unwrap());
let values = [NestedCase {
dever: Json::parse(&source).unwrap(),
rust: serde_json::from_str(&source).unwrap(), values: count, depth,
}];
compare_named(&format!("json_dever_nested_{name}"), &values,
if name == "short" { 100_000 } else { 1000 },
runtime_nested_record, native_nested_record, |value| value.len() as u64, "rust_bounded");
}
}