From 5592e127e93614907fcdb8351c0a82a3e64261c6 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 2 Oct 2026 13:32:41 +0800 Subject: [PATCH] Write the comments in both languages, part five --- src/json.rs | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/src/json.rs b/src/json.rs index 7058511..5f3e7ec 100644 --- a/src/json.rs +++ b/src/json.rs @@ -20,23 +20,54 @@ //! Encoding follows RFC 8259: strings are always quoted, control characters //! and the quote and reverse solidus are escaped, and non-finite floats are //! encoded as `null` because JSON has no representation for them. +//! +//! 手写的 JSON 编解码器。 +//! +//! 服务除 `apsis-rs` 外不得依赖任何 crate,因此 JSON 的编码与解码在此实现。 +//! 该编解码器覆盖服务所需的 RFC 8259 子集: +//! +//! - [`Value`] 为任何 JSON 值建模,含嵌套的数组与对象。 +//! - [`parse`] 解码一份文档,并以 [`JsonError`] 报告第一个问题的字节偏移。 +//! - [`to_string`] 以紧凑形式编码一个值,[`to_string_pretty`] 以缩进编码以便 +//! 阅读。 +//! - [`object`] 宏构造对象字面量,无需重复 `Value` 的构造函数。 +//! +//! 数字以 `f64` 持有。需要完整 `i64` 范围的整数,只要能无损地表示为 `f64`, +//! 就会被精确解码,这在上限 `2^53` 之内成立;服务只交换该范围内的量与标识。 +//! +//! 编码遵循 RFC 8259:字符串始终加引号,控制字符、引号与反斜杠被转义,非有限 +//! 浮点数编码为 `null`,因为 JSON 没有它们的表示。 use std::fmt::{self, Write as _}; /// A JSON value. +/// +/// 一个 JSON 值。 #[derive(Debug, Clone, PartialEq)] pub enum Value { /// The `null` literal. + /// + /// `null` 字面量。 Null, /// A boolean, `true` or `false`. + /// + /// 一个布尔值,`true` 或 `false`。 Bool(bool), /// A number. JSON does not distinguish integers from floats. + /// + /// 一个数值。JSON 不区分整数与浮点数。 Number(f64), /// A string. + /// + /// 一个字符串。 String(String), /// An array of values. + /// + /// 一个值的数组。 Array(Vec), /// An object, preserving the order in which members were inserted. + /// + /// 一个对象,保持成员被插入时的顺序。 Object(Vec<(String, Value)>), } @@ -45,12 +76,19 @@ impl Value { /// /// The constructor and [`Value::number`] complete the pair of scalar /// constructors for callers that build a value without a literal. + /// + /// 由任何可转换为 `String` 的东西创建一个字符串值。 + /// + /// 该构造函数与 [`Value::number`] 构成一对标量构造函数,供不通过字面量构造取值 + /// 的调用方使用。 #[allow(dead_code)] pub fn string(text: impl Into) -> Self { Self::String(text.into()) } /// Creates a number value. + /// + /// 创建一个数值。 #[allow(dead_code)] pub fn number(value: impl Into) -> Self { Self::Number(value.into()) @@ -58,6 +96,8 @@ impl Value { /// Returns the value as an object member list, or an empty slice when it /// is not an object. + /// + /// 把该值返回为对象的成员列表,若它不是对象则为空切片。 pub fn members(&self) -> &[(String, Value)] { match self { Self::Object(members) => members, @@ -67,6 +107,8 @@ impl Value { /// Returns the member with `key`, or `None` when it is absent or when /// the value is not an object. + /// + /// 返回键为 `key` 的成员;它不存在或该值不是对象时返回 `None`。 pub fn get(&self, key: &str) -> Option<&Value> { self.members() .iter() @@ -76,6 +118,8 @@ impl Value { /// Returns the value as a string slice, or `None` when it is not a /// string. + /// + /// 把该值返回为字符串切片,若它不是字符串则为 `None`。 pub fn as_str(&self) -> Option<&str> { match self { Self::String(text) => Some(text), @@ -89,6 +133,11 @@ impl Value { /// that arrive from a request are read through the tool parameter /// wrappers, which validate a number against its declared bounds, so /// this is used where a value is known to be a number. + /// + /// 把该值返回为数值,若它不是数值则为 `None`。 + /// + /// 该访问器补齐了 [`Value`] 上带类型的读取。来自请求的取值经由工具参数包装器 + /// 读取,那些包装器会依据声明的上下界校验数值;因此这里用在已知是数值的地方。 #[allow(dead_code)] pub fn as_f64(&self) -> Option { match self { @@ -98,6 +147,8 @@ impl Value { } /// Returns the value as a `bool`, or `None` when it is not a boolean. + /// + /// 把该值返回为 `bool`,若它不是布尔值则为 `None`。 pub fn as_bool(&self) -> Option { match self { Self::Bool(value) => Some(*value), @@ -107,6 +158,8 @@ impl Value { /// Returns the value as an array, or an empty slice when it is not an /// array. + /// + /// 把该值返回为数组,若它不是数组则为空切片。 pub fn as_array(&self) -> &[Value] { match self { Self::Array(items) => items, @@ -120,6 +173,11 @@ impl Value { /// Each key is converted to a `String` and each value is converted to a /// [`Value`], so literals such as `object!{"n" => 1.0}` work without /// repeating the constructors. +/// +/// 由 `key => value` 对构造一个对象 [`Value`]。 +/// +/// 每个键被转换为 `String`,每个值被转换为 [`Value`],因此诸如 +/// `object!{"n" => 1.0}` 的字面量无需重复构造函数即可使用。 #[macro_export] macro_rules! json_object { () => { $crate::json::Value::Object(Vec::new()) }; @@ -195,11 +253,20 @@ impl> From> for Value { /// detected, counted from zero. The message names the condition without /// quoting the input, so that a malformed request cannot echo attacker /// controlled text back through the error channel. +/// +/// 一次 JSON 解码失败。 +/// +/// 偏移是从零数起的、输入中检测到问题的字节位置。消息指名具体条件而不复述输入, +/// 使畸形的请求无法经由错误通道回显攻击者控制的文本。 #[derive(Debug, Clone, PartialEq, Eq)] pub struct JsonError { /// Byte offset of the first problem. + /// + /// 第一个问题的字节偏移。 pub offset: usize, /// Description of the problem. + /// + /// 问题的描述。 pub message: String, } @@ -224,10 +291,18 @@ impl std::error::Error for JsonError {} /// /// Whitespace is allowed around every token. Trailing content after the /// first complete value is rejected. +/// +/// 解码一份 JSON 文档。 +/// +/// 每个记号周围都允许空白。第一个完整值之后的剩余内容会被拒绝。 /// Returns the length in bytes of the UTF-8 sequence starting with `byte`. /// /// The leading byte states the length; the continuation bytes are checked /// when the sequence itself is validated. +/// +/// 返回以 `byte` 开头的 UTF-8 序列的字节长度。 +/// +/// 首字节给出长度;后续字节在该序列本身被校验时检查。 fn utf8_length(byte: u8) -> usize { match byte { 0x00..=0x7f => 1, @@ -236,6 +311,9 @@ fn utf8_length(byte: u8) -> usize { 0xf0..=0xf7 => 4, // A continuation or an invalid leading byte; one byte is taken so // that the validation reports it as invalid rather than looping. + + // 一个后续字节或非法的首字节;只取一个字节,使校验把它报告为非法, + // 而不是陷入循环。 _ => 1, } } @@ -258,9 +336,13 @@ pub fn parse(input: &str) -> Result { } /// Encodes a value in compact form. +/// +/// 以紧凑形式编码一个值。 pub fn to_string(value: &Value) -> String { let mut output = String::new(); // Writing into a String cannot fail, so the result is discarded. + + // 向 String 写入不会失败,因此结果被丢弃。 let _ = write_value(&mut output, value); output } @@ -269,6 +351,10 @@ pub fn to_string(value: &Value) -> String { /// /// A pretty document is larger than the compact form, so the service sends /// the compact form; this is used when a payload is written for a reader. +/// +/// 以每层嵌套两个空格的缩进编码一个值。 +/// +/// 带缩进的文档比紧凑形式更大,因此服务发送紧凑形式;这个用于为读者书写载荷时。 #[allow(dead_code)] pub fn to_string_pretty(value: &Value) -> String { let mut output = String::new(); @@ -277,6 +363,8 @@ pub fn to_string_pretty(value: &Value) -> String { } /// Writes a value to `output` in compact form. +/// +/// 以紧凑形式把一个值写入 `output`。 fn write_value(output: &mut String, value: &Value) -> fmt::Result { match value { Value::Null => output.push_str("null"), @@ -315,6 +403,8 @@ fn write_value(output: &mut String, value: &Value) -> fmt::Result { } /// Writes a value to `output` with `depth` levels of indentation. +/// +/// 以 `depth` 层缩进把一个值写入 `output`。 #[allow(dead_code)] fn write_pretty(output: &mut String, value: &Value, depth: usize) -> fmt::Result { let indent = " ".repeat(depth); @@ -361,6 +451,11 @@ fn write_pretty(output: &mut String, value: &Value, depth: usize) -> fmt::Result /// Non-finite values have no JSON representation and are written as `null`, /// so that a computation that produced a not-a-number value cannot produce /// an unparsable document. +/// +/// 以 JSON 接受的形式写出一个数值。 +/// +/// 有限值以 `f64` 的最短往返格式写出。非有限值没有 JSON 表示,写为 `null`, +/// 使产生了非数值的计算不可能产生一份无法解析的文档。 fn write_number(output: &mut String, number: f64) -> fmt::Result { if !number.is_finite() { output.push_str("null"); @@ -375,6 +470,11 @@ fn write_number(output: &mut String, number: f64) -> fmt::Result { /// Characters below `0x20` are escaped; the quote and reverse solidus are /// escaped; every other character is emitted as is, which keeps UTF-8 input /// valid because the input was already valid UTF-8. +/// +/// 写出一个带 RFC 8259 所要求转义的加引号字符串。 +/// +/// `0x20` 以下的字符被转义;引号与反斜杠被转义;其余字符按原样输出,这保持了 +/// UTF-8 输入的有效性,因为输入本就是合法 UTF-8。 fn write_string(output: &mut String, text: &str) -> fmt::Result { output.push('"'); for character in text.chars() { @@ -400,21 +500,31 @@ fn write_string(output: &mut String, text: &str) -> fmt::Result { /// /// Recursion depth follows the nesting of the document; [`MAX_DEPTH`] /// bounds it so that a hostile document cannot exhaust the stack. +/// +/// 一个面向字节的递归下降解析器。 +/// +/// 递归深度跟随文档的嵌套;[`MAX_DEPTH`] 给它设界,使恶意文档无法耗尽栈。 struct Parser<'a> { bytes: &'a [u8], position: usize, } /// Maximum nesting depth accepted by [`parse`]. +/// +/// [`parse`] 接受的最大嵌套深度。 pub const MAX_DEPTH: usize = 64; impl Parser<'_> { /// Parses a value at the current position. + /// + /// 在当前位置解析一个值。 fn value(&mut self) -> Result { self.value_at_depth(0) } /// Parses a value, rejecting nesting beyond [`MAX_DEPTH`]. + /// + /// 解析一个值,拒绝超过 [`MAX_DEPTH`] 的嵌套。 fn value_at_depth(&mut self, depth: usize) -> Result { if depth > MAX_DEPTH { return Err(JsonError::new(self.position, "nesting is too deep")); @@ -433,11 +543,15 @@ impl Parser<'_> { } /// Returns the byte at the current position without consuming it. + /// + /// 返回当前位置的字节而不消费它。 fn peek(&self) -> Option { self.bytes.get(self.position).copied() } /// Advances past any JSON whitespace. + /// + /// 越过任何 JSON 空白。 fn skip_whitespace(&mut self) { while let Some(byte) = self.peek() { match byte { @@ -448,6 +562,8 @@ impl Parser<'_> { } /// Consumes a fixed literal and returns `value`. + /// + /// 消费一个固定字面量并返回 `value`。 fn literal(&mut self, text: &str, value: Value) -> Result { if self.bytes[self.position..].starts_with(text.as_bytes()) { self.position += text.len(); @@ -458,6 +574,8 @@ impl Parser<'_> { } /// Parses a quoted string, decoding the escape sequences. + /// + /// 解析一个加引号的字符串,并解码各转义序列。 fn string(&mut self) -> Result { let start = self.position; self.position += 1; // opening quote @@ -489,6 +607,12 @@ impl Parser<'_> { // quadratic, because it repeats for every character in // the document: a catalogue of seven hundred kilobytes // spent half a minute in this one line. + + // 从此处的字节解码一个 UTF-8 标量。 + // + // 只校验该序列本身。改为校验整个剩余缓冲虽然正确却是二次 + // 复杂度,因为它会为文档中的每个字符重复一遍:一份七百 + // 千字节的目录曾在这一行上花掉半分钟。 let length = utf8_length(byte); let end = self.position + length; if end > self.bytes.len() { @@ -504,6 +628,8 @@ impl Parser<'_> { } /// Decodes one escape sequence after the reverse solidus. + /// + /// 解码反斜杠之后的一个转义序列。 fn escape(&mut self, output: &mut String) -> Result<(), JsonError> { let Some(byte) = self.peek() else { return Err(JsonError::new(self.position, "unterminated escape")); @@ -526,10 +652,14 @@ impl Parser<'_> { } /// Decodes a `\uXXXX` escape, pairing surrogates into one scalar value. + /// + /// 解码一个 `\uXXXX` 转义,把代理对合并为一个标量值。 fn unicode_escape(&mut self, output: &mut String) -> Result<(), JsonError> { let first = self.hex4()?; let scalar = match first { // A high surrogate must be followed by a low surrogate. + + // 高代理之后必须跟一个低代理。 0xd800..=0xdbff => { if self.peek() != Some(b'\\') { return Err(JsonError::new(self.position, "unpaired high surrogate")); @@ -557,6 +687,8 @@ impl Parser<'_> { } /// Reads exactly four hexadecimal digits. + /// + /// 恰好读取四个十六进制数字。 fn hex4(&mut self) -> Result { if self.position + 4 > self.bytes.len() { return Err(JsonError::new(self.position, "truncated unicode escape")); @@ -583,6 +715,12 @@ impl Parser<'_> { /// optional exponent. The token is collected in full and handed to the /// standard `f64` parser, which rounds once and applies the correct /// sign to every part of the value. + /// + /// 解析一个数值。 + /// + /// 文法即 RFC 8259 的那一个:可选的负号、无前导零的整数部分、可选的小数部分、 + /// 可选的指数。该记号被完整收集后交给标准库的 `f64` 解析器,它只做一次舍入, + /// 并对该值的每一部分施加正确的符号。 fn number(&mut self) -> Result { let start = self.position; if self.peek() == Some(b'-') { @@ -633,6 +771,8 @@ impl Parser<'_> { } /// Parses an array. + /// + /// 解析一个数组。 fn array(&mut self, depth: usize) -> Result { self.position += 1; // opening bracket let mut items = Vec::new(); @@ -657,6 +797,8 @@ impl Parser<'_> { } /// Parses an object. + /// + /// 解析一个对象。 fn object(&mut self, depth: usize) -> Result { self.position += 1; // opening brace let mut members = Vec::new();