From 47a509cfd36dfac4013f4ee76891165727eb47a1 Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Fri, 16 Oct 2026 06:37:01 -1000 Subject: Trim readme lines in linear time The heading pattern backtracked over a run of spaces in cubic time and the cell trim in quadratic time, so one crafted line in a readme could hold a request for half an hour, well inside the size cap. The heading rule now also keeps a sharp that ends a word, as in "# C#". --- custom/extensions/about-render.lua | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'custom') diff --git a/custom/extensions/about-render.lua b/custom/extensions/about-render.lua index c833c00..ecb2abe 100644 --- a/custom/extensions/about-render.lua +++ b/custom/extensions/about-render.lua @@ -33,8 +33,32 @@ local max_depth = 24 local filename, chunks = "", {} +-- Lua patterns backtrack, so "%s+$" costs the square of a run of spaces in +-- the middle of a line. Finding each end directly stays linear. local function trim(text) - return (text:gsub("^%s+", ""):gsub("%s+$", "")) + local first = text:find("%S") + if first == nil then + return "" + end + local last = #text + while text:find("^%s", last) do + last = last - 1 + end + return text:sub(first, last) +end + +-- A heading may close with a run of hashes, which only counts as markup when +-- a space comes before it, so "# C#" keeps its sharp. +local function heading_text(title) + title = trim(title) + local last = #title + while last > 0 and title:sub(last, last) == "#" do + last = last - 1 + end + if last == 0 or title:find("^%s", last) then + return trim(title:sub(1, last)) + end + return title end -- Normalise CRLF and CR to newlines, then split. A trailing newline yields @@ -332,7 +356,7 @@ if has_lpeg then blocks[#blocks + 1] = { kind = "heading", level = #hashes, - kids = parse_inline((title:gsub("%s*#*%s*$", ""))), + kids = parse_inline(heading_text(title)), } i = i + 1 elseif break_line:match(line) then -- cgit