From 1667c1180e8f99eb4c515053c009fba74741e25c Mon Sep 17 00:00:00 2001 From: Watson Date: Sat, 12 Mar 2022 01:41:12 +0900 Subject: [PATCH 1/4] Use start_with? and end_with? to detect SQUARE_BRAKET MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Test code ```ruby require 'benchmark/ips' SQUARE_BRACKETED = /\A\[(.*)\]\z/m markup = "[product.catchall]" Benchmark.ips do |x| x.report("SQUARE_BRACKETED") { if markup =~ SQUARE_BRACKETED Regexp.last_match(1) end } x.report("start/end_with?") { if markup&.start_with?('[') && markup&.end_with?(']') markup[1..-2] end } x.compare! end ``` ## Result ``` Warming up -------------------------------------- SQUARE_BRACKETED 261.300k i/100ms start/end_with? 548.813k i/100ms Calculating ------------------------------------- SQUARE_BRACKETED 2.632M (± 0.6%) i/s - 13.326M in 5.064085s start/end_with? 5.471M (± 0.5%) i/s - 27.441M in 5.015770s Comparison: start/end_with?: 5470994.1 i/s SQUARE_BRACKETED: 2631642.3 i/s - 2.08x (± 0.00) slower ``` --- lib/liquid/variable_lookup.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/liquid/variable_lookup.rb b/lib/liquid/variable_lookup.rb index 9d5dba68..775b002e 100644 --- a/lib/liquid/variable_lookup.rb +++ b/lib/liquid/variable_lookup.rb @@ -2,8 +2,7 @@ module Liquid class VariableLookup - SQUARE_BRACKETED = /\A\[(.*)\]\z/m - COMMAND_METHODS = ['size', 'first', 'last'].freeze + COMMAND_METHODS = ['size', 'first', 'last'].freeze attr_reader :name, :lookups @@ -15,8 +14,8 @@ module Liquid lookups = markup.scan(VariableParser) name = lookups.shift - if name =~ SQUARE_BRACKETED - name = Expression.parse(Regexp.last_match(1)) + if name&.start_with?('[') && name&.end_with?(']') + name = Expression.parse(name[1..-2]) end @name = name @@ -25,8 +24,8 @@ module Liquid @lookups.each_index do |i| lookup = lookups[i] - if lookup =~ SQUARE_BRACKETED - lookups[i] = Expression.parse(Regexp.last_match(1)) + if lookup&.start_with?('[') && lookup&.end_with?(']') + lookups[i] = Expression.parse(lookup[1..-2]) elsif COMMAND_METHODS.include?(lookup) @command_flags |= 1 << i end From dd7ed00ec4e2d26172642add35583a86e25dabe0 Mon Sep 17 00:00:00 2001 From: Watson Date: Sat, 12 Mar 2022 18:17:06 +0900 Subject: [PATCH 2/4] Use strip & empty? to detect Whitespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Test code ```ruby require 'benchmark/ips' WhitespaceOrNothing = /\A\s*\z/ token = " " * 20 Benchmark.ips do |x| x.report("WhitespaceOrNothing") { token =~ WhitespaceOrNothing } x.report("strip & empty?") { token.strip.empty? } x.compare! end ``` ## Result ``` Warming up -------------------------------------- WhitespaceOrNothing 266.391k i/100ms strip & empty? 1.044M i/100ms Calculating ------------------------------------- WhitespaceOrNothing 2.705M (± 0.4%) i/s - 13.586M in 5.023453s strip & empty? 10.400M (± 1.1%) i/s - 52.182M in 5.017990s Comparison: strip & empty?: 10400286.2 i/s WhitespaceOrNothing: 2704552.3 i/s - 3.85x (± 0.00) slower ``` --- lib/liquid/block_body.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 2921ce88..8de7f6cb 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -7,7 +7,6 @@ module Liquid LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om - WhitespaceOrNothing = /\A\s*\z/ TAGSTART = "{%" VARSTART = "{{" @@ -37,7 +36,7 @@ module Liquid private def parse_for_liquid_tag(tokenizer, parse_context) while (token = tokenizer.shift) - unless token.empty? || token =~ WhitespaceOrNothing + unless token.strip.empty? unless token =~ LiquidTagToken # line isn't empty but didn't match tag syntax, yield and let the # caller raise a syntax error @@ -150,7 +149,7 @@ module Liquid end parse_context.trim_whitespace = false @nodelist << token - @blank &&= !!(token =~ WhitespaceOrNothing) + @blank &&= token.strip.empty? end parse_context.line_number = tokenizer.line_number end From 22568080b1cb6f19969d55d68dc39b5c04a37b17 Mon Sep 17 00:00:00 2001 From: Watson Date: Wed, 16 Mar 2022 12:33:45 +0900 Subject: [PATCH 3/4] Revert "Use strip & empty? to detect Whitespaces" This reverts commit dd7ed00ec4e2d26172642add35583a86e25dabe0. --- lib/liquid/block_body.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 8de7f6cb..2921ce88 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -7,6 +7,7 @@ module Liquid LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om + WhitespaceOrNothing = /\A\s*\z/ TAGSTART = "{%" VARSTART = "{{" @@ -36,7 +37,7 @@ module Liquid private def parse_for_liquid_tag(tokenizer, parse_context) while (token = tokenizer.shift) - unless token.strip.empty? + unless token.empty? || token =~ WhitespaceOrNothing unless token =~ LiquidTagToken # line isn't empty but didn't match tag syntax, yield and let the # caller raise a syntax error @@ -149,7 +150,7 @@ module Liquid end parse_context.trim_whitespace = false @nodelist << token - @blank &&= token.strip.empty? + @blank &&= !!(token =~ WhitespaceOrNothing) end parse_context.line_number = tokenizer.line_number end From fad58ef436bb9a516ea8364d9398cb6f6876d784 Mon Sep 17 00:00:00 2001 From: Watson Date: Wed, 16 Mar 2022 12:41:00 +0900 Subject: [PATCH 4/4] Use String#match? instead of String#=~ to reduce allocation for backreferecne MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Test code ```ruby require 'benchmark/ips' WhitespaceOrNothing = /\A\s*\z/ token = " " * 20 token =~ WhitespaceOrNothing Benchmark.ips do |x| x.report("=~") { token =~ WhitespaceOrNothing } x.report("match?") { token.match?(WhitespaceOrNothing) } x.compare! end ``` ## Result ``` Warming up -------------------------------------- =~ 271.356k i/100ms match? 579.655k i/100ms Calculating ------------------------------------- =~ 2.717M (± 0.4%) i/s - 13.839M in 5.092947s match? 5.695M (± 1.6%) i/s - 28.983M in 5.090640s Comparison: match?: 5694747.3 i/s =~: 2717370.9 i/s - 2.10x (± 0.00) slower ``` --- lib/liquid/block_body.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 2921ce88..d8c7e453 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -37,7 +37,7 @@ module Liquid private def parse_for_liquid_tag(tokenizer, parse_context) while (token = tokenizer.shift) - unless token.empty? || token =~ WhitespaceOrNothing + unless token.empty? || token.match?(WhitespaceOrNothing) unless token =~ LiquidTagToken # line isn't empty but didn't match tag syntax, yield and let the # caller raise a syntax error @@ -150,7 +150,7 @@ module Liquid end parse_context.trim_whitespace = false @nodelist << token - @blank &&= !!(token =~ WhitespaceOrNothing) + @blank &&= token.match?(WhitespaceOrNothing) end parse_context.line_number = tokenizer.line_number end