diff --git a/src/utils/html.rs b/src/utils/html.rs index cbca198..ef2549f 100644 --- a/src/utils/html.rs +++ b/src/utils/html.rs @@ -9,13 +9,30 @@ /// /// 单引号统一为 `'`(HTML5 规范,与原 server 端 `helpers::escape_html` 一致)。 /// 可安全用于文本节点与属性值上下文。 +/// +/// 单遍扫描:旧实现链式调用 5 次 `str::replace`,每次全量扫描 + 分配一个新 String, +/// 5 个特殊字符意味着 5 次堆分配 + 5 遍扫描。单遍 `match` 只扫描一次、只分配一次。 +/// 该函数被 markdown 渲染管线密集调用(每个标题、每个代码块),属于热点路径。 pub fn escape_html(input: &str) -> String { - input - .replace('&', "&") - .replace('<', "<") - .replace('>', ">") - .replace('"', """) - .replace('\'', "'") + // 快速路径:无特殊字符直接 clone(大多纯文本走这里,零额外扫描)。 + // memchr 风格的逐字节查找比总是分配 + 遍历更省。 + if !input.as_bytes().iter().any(|&b| { + matches!(b, b'&' | b'<' | b'>' | b'"' | b'\'') + }) { + return input.to_string(); + } + let mut out = String::with_capacity(input.len()); + for c in input.chars() { + match c { + '&' => out.push_str("&"), + '<' => out.push_str("<"), + '>' => out.push_str(">"), + '"' => out.push_str("""), + '\'' => out.push_str("'"), + _ => out.push(c), + } + } + out } #[cfg(test)]