From 51217dbcc64ecc34aa70f126b103bedf07e153fc Mon Sep 17 00:00:00 2001
From: NAITOH Jun <naitoh@gmail.com>
Date: Wed, 31 Jan 2024 16:35:55 +0900
Subject: [PATCH] Reduce calls to StringScanner.new() (#108)

## Why

`StringScanner.new()` instances can be reused within parse_attributes,
reducing initialization costs.

## Benchmark

```
RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.0/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]
Calculating -------------------------------------
                         before       after  before(YJIT)  after(YJIT)
                 dom     11.018      11.207        17.059       16.660 i/s -     100.000 times in 9.075992s 8.923280s 5.861969s 6.002555s
                 sax     29.843      30.821        45.518       47.505 i/s -     100.000 times in 3.350909s 3.244524s 2.196940s 2.105037s
                pull     34.480      35.937        52.816       57.098 i/s -     100.000 times in 2.900205s 2.782632s 1.893370s 1.751378s
              stream     32.430      33.516        46.247       48.412 i/s -     100.000 times in 3.083536s 2.983607s 2.162288s 2.065584s

Comparison:
                              dom
        before(YJIT):        17.1 i/s
         after(YJIT):        16.7 i/s - 1.02x  slower
               after:        11.2 i/s - 1.52x  slower
              before:        11.0 i/s - 1.55x  slower

                              sax
         after(YJIT):        47.5 i/s
        before(YJIT):        45.5 i/s - 1.04x  slower
               after:        30.8 i/s - 1.54x  slower
              before:        29.8 i/s - 1.59x  slower

                             pull
         after(YJIT):        57.1 i/s
        before(YJIT):        52.8 i/s - 1.08x  slower
               after:        35.9 i/s - 1.59x  slower
              before:        34.5 i/s - 1.66x  slower

                           stream
         after(YJIT):        48.4 i/s
        before(YJIT):        46.2 i/s - 1.05x  slower
               after:        33.5 i/s - 1.44x  slower
              before:        32.4 i/s - 1.49x  slower

```

- YJIT=ON : 1.02x - 1.08x faster
- YJIT=OFF : 1.01x - 1.04x faster

CVE: CVE-2024-49761

Upstream-Status: Backport [https://github.com/ruby/rexml/commit/51217dbcc64ecc34aa70f126b103bedf07e153fc]

Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
 .bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
index 7126a12..b66b0ed 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
@@ -115,6 +115,7 @@ module REXML
       def initialize( source )
         self.stream = source
         @listeners = []
+        @attributes_scanner = StringScanner.new('')
       end

       def add_listener( listener )
@@ -601,7 +602,8 @@ module REXML
         return attributes, closed if raw_attributes.nil?
         return attributes, closed if raw_attributes.empty?

-        scanner = StringScanner.new(raw_attributes)
+        @attributes_scanner.string = raw_attributes
+        scanner = @attributes_scanner
         until scanner.eos?
           if scanner.scan(/\s+/)
             break if scanner.eos?
--
2.40.0
