Write the comments in both languages, part five
This commit is contained in:
parent
b30203fc29
commit
5592e127e9
142
src/json.rs
142
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<Value>),
|
||||
/// 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<String>) -> Self {
|
||||
Self::String(text.into())
|
||||
}
|
||||
|
||||
/// Creates a number value.
|
||||
///
|
||||
/// 创建一个数值。
|
||||
#[allow(dead_code)]
|
||||
pub fn number(value: impl Into<f64>) -> 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<f64> {
|
||||
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<bool> {
|
||||
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<T: Into<Value>> From<Vec<T>> 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<Value, JsonError> {
|
|||
}
|
||||
|
||||
/// 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<Value, JsonError> {
|
||||
self.value_at_depth(0)
|
||||
}
|
||||
|
||||
/// Parses a value, rejecting nesting beyond [`MAX_DEPTH`].
|
||||
///
|
||||
/// 解析一个值,拒绝超过 [`MAX_DEPTH`] 的嵌套。
|
||||
fn value_at_depth(&mut self, depth: usize) -> Result<Value, JsonError> {
|
||||
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<u8> {
|
||||
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<Value, JsonError> {
|
||||
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<String, JsonError> {
|
||||
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<u32, JsonError> {
|
||||
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<Value, JsonError> {
|
||||
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<Value, JsonError> {
|
||||
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<Value, JsonError> {
|
||||
self.position += 1; // opening brace
|
||||
let mut members = Vec::new();
|
||||
|
|
|
|||
Loading…
Reference in New Issue