From f8b08b5b6474001e4bcc8cefac0c1974ac39a1a2 Mon Sep 17 00:00:00 2001 From: Tobi Lutke Date: Wed, 11 Mar 2026 10:40:10 -0400 Subject: [PATCH] =?UTF-8?q?Replace=20manual=20scan=5Ffragment/scan=5Fquote?= =?UTF-8?q?d=5Fstring=5Fraw/skip=5Ffragment=20with=20regex=20=E2=80=94=20c?= =?UTF-8?q?leaner,=20same/better=20perf\n\nResult:=20{"status":"keep","com?= =?UTF-8?q?bined=5F=C2=B5s":4132,"parse=5F=C2=B5s":2890,"render=5F=C2=B5s"?= =?UTF-8?q?:1242,"allocations":25535}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/liquid/cursor.rb | 50 ++++++++------------------------------------ 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/lib/liquid/cursor.rb b/lib/liquid/cursor.rb index 67c5f610..71e19ccb 100644 --- a/lib/liquid/cursor.rb +++ b/lib/liquid/cursor.rb @@ -161,17 +161,12 @@ module Liquid content end + # Regex for quoted strings (single or double quoted, including quotes) + QUOTED_STRING_RAW = /"[^"]*"|'[^']*'/ + # Scan a quoted string including quotes. Returns the full "..." or '...' string, or nil. def scan_quoted_string_raw - b = @ss.peek_byte - return unless b == QUOTE_S || b == QUOTE_D - - quote = b - start = @ss.pos - @ss.scan_byte - @ss.scan_byte while (b = @ss.peek_byte) && b != quote - @ss.scan_byte if @ss.peek_byte == quote - @source.byteslice(start, @ss.pos - start) + @ss.scan(QUOTED_STRING_RAW) end # ── Expressions ───────────────────────────────────────────────── @@ -193,42 +188,15 @@ module Liquid # Skip a fragment without allocating. Returns length skipped, or 0. def skip_fragment - b = @ss.peek_byte - return 0 unless b - - start = @ss.pos - if b == QUOTE_S || b == QUOTE_D - quote = b - @ss.scan_byte - @ss.scan_byte while (b = @ss.peek_byte) && b != quote - @ss.scan_byte if @ss.peek_byte == quote - else - while (b = @ss.peek_byte) - break if b == SPACE || b == TAB || b == NL || b == CR || b == COMMA || b == PIPE - - @ss.scan_byte - end - end - @ss.pos - start + @ss.skip(QUOTED_STRING_RAW) || @ss.skip(UNQUOTED_FRAGMENT) || 0 end + # Regex for unquoted fragment: non-whitespace/comma/pipe sequence + UNQUOTED_FRAGMENT = /[^\s,|]+/ + # Scan a "QuotedFragment" — a quoted string or non-whitespace/comma/pipe run def scan_fragment - b = @ss.peek_byte - return unless b - - if b == QUOTE_S || b == QUOTE_D - scan_quoted_string_raw - else - start = @ss.pos - while (b = @ss.peek_byte) - break if b == SPACE || b == TAB || b == NL || b == CR || b == COMMA || b == PIPE - - @ss.scan_byte - end - len = @ss.pos - start - len > 0 ? @source.byteslice(start, len) : nil - end + @ss.scan(QUOTED_STRING_RAW) || @ss.scan(UNQUOTED_FRAGMENT) end # ── Comparison operators ────────────────────────────────────────