From 14ac591403557c69fbb247cb66c7ebef15e44b92 Mon Sep 17 00:00:00 2001 From: Tobi Lutke Date: Wed, 11 Mar 2026 10:37:09 -0400 Subject: [PATCH] =?UTF-8?q?Replace=20manual=20byte-level=20scan=5Fid/skip?= =?UTF-8?q?=5Fid=20with=20regex=20=E2=80=94=20C-level=20StringScanner.scan?= =?UTF-8?q?=20is=20faster=20than=20Ruby-level=20byte=20scanning\n\nResult:?= =?UTF-8?q?=20{"status":"keep","combined=5F=C2=B5s":4185,"parse=5F=C2=B5s"?= =?UTF-8?q?:2943,"render=5F=C2=B5s":1242,"allocations":25535}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/liquid/cursor.rb | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/lib/liquid/cursor.rb b/lib/liquid/cursor.rb index de79955b..0660b159 100644 --- a/lib/liquid/cursor.rb +++ b/lib/liquid/cursor.rb @@ -90,31 +90,21 @@ module Liquid true end + # Regex for identifier: [a-zA-Z_][\w-]*\?? + ID_REGEX = /[a-zA-Z_][\w-]*\??/ + # ── Identifiers ───────────────────────────────────────────────── # Skip an identifier without allocating a string. Returns length skipped, or 0. def skip_id - start = @ss.pos - b = @ss.peek_byte - return 0 unless b && ((b >= 97 && b <= 122) || (b >= 65 && b <= 90) || b == USCORE) - - @ss.scan_byte - while (b = @ss.peek_byte) - break unless (b >= 97 && b <= 122) || (b >= 65 && b <= 90) || - (b >= 48 && b <= 57) || b == USCORE || b == DASH - - @ss.scan_byte - end - @ss.scan_byte if @ss.peek_byte == QMARK - @ss.pos - start + @ss.skip(ID_REGEX) || 0 end # Check if next id matches expected string, consume if so. No allocation. def expect_id(expected) start = @ss.pos - len = skip_id - if len == expected.bytesize + if @ss.skip(ID_REGEX) == expected.bytesize match = true - len.times do |i| + expected.bytesize.times do |i| if @source.getbyte(start + i) != expected.getbyte(i) match = false break @@ -129,19 +119,7 @@ module Liquid # Scan a single identifier: [a-zA-Z_][\w-]*\?? # Returns the string or nil if not at an identifier def scan_id - start = @ss.pos - b = @ss.peek_byte - return unless b && ((b >= 97 && b <= 122) || (b >= 65 && b <= 90) || b == USCORE) - - @ss.scan_byte - while (b = @ss.peek_byte) - break unless (b >= 97 && b <= 122) || (b >= 65 && b <= 90) || - (b >= 48 && b <= 57) || b == USCORE || b == DASH - - @ss.scan_byte - end - @ss.scan_byte if @ss.peek_byte == QMARK - @source.byteslice(start, @ss.pos - start) + @ss.scan(ID_REGEX) end # Scan a tag name: '#' or \w+