Replace manual byte-level scan_id/skip_id with regex — C-level StringScanner.scan is faster than Ruby-level byte scanning\n\nResult: {"status":"keep","combined_µs":4185,"parse_µs":2943,"render_µs":1242,"allocations":25535}

This commit is contained in:
Tobi Lutke
2026-04-04 17:42:33 -07:00
committed by Chris Pak
parent 3f9b8916b2
commit 14ac591403
+7 -29
View File
@@ -90,31 +90,21 @@ module Liquid
true true
end end
# Regex for identifier: [a-zA-Z_][\w-]*\??
ID_REGEX = /[a-zA-Z_][\w-]*\??/
# ── Identifiers ───────────────────────────────────────────────── # ── Identifiers ─────────────────────────────────────────────────
# Skip an identifier without allocating a string. Returns length skipped, or 0. # Skip an identifier without allocating a string. Returns length skipped, or 0.
def skip_id def skip_id
start = @ss.pos @ss.skip(ID_REGEX) || 0
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
end end
# Check if next id matches expected string, consume if so. No allocation. # Check if next id matches expected string, consume if so. No allocation.
def expect_id(expected) def expect_id(expected)
start = @ss.pos start = @ss.pos
len = skip_id if @ss.skip(ID_REGEX) == expected.bytesize
if len == expected.bytesize
match = true match = true
len.times do |i| expected.bytesize.times do |i|
if @source.getbyte(start + i) != expected.getbyte(i) if @source.getbyte(start + i) != expected.getbyte(i)
match = false match = false
break break
@@ -129,19 +119,7 @@ module Liquid
# Scan a single identifier: [a-zA-Z_][\w-]*\?? # Scan a single identifier: [a-zA-Z_][\w-]*\??
# Returns the string or nil if not at an identifier # Returns the string or nil if not at an identifier
def scan_id def scan_id
start = @ss.pos @ss.scan(ID_REGEX)
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)
end end
# Scan a tag name: '#' or \w+ # Scan a tag name: '#' or \w+