This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit c10ffabe49f0a7cb01e48e5ee9ff51b45f19acfb
Author: opencode <[email protected]>
AuthorDate: Sun Oct 18 16:20:23 2026 +0200

    Reduce the cost of parsing EL expressions with long tokens
    
    The character stream used by the EL parser grew its buffer by a fixed
    2048 characters on each expansion, copying the existing contents on
    every expansion. Reading a single token larger than the initial buffer,
    such as a long string literal, therefore required one expansion per
    2048 characters read and copied everything read so far on each of
    those expansions. The total cost grew quadratically with the token
    length: a one megabyte string literal took around 640ms to parse and a
    ten megabyte one over thirty seconds.
    
    Grow the buffer in proportion to its current size, doubling it, so the
    number of expansions and the total copy cost grow linearly with the
    input. The arithmetic that relocates the current token uses the
    previous buffer size throughout and is unaffected. Parsing the same
    expressions now takes a few tens of milliseconds with identical
    results, including the error positions reported for invalid
    expressions.
---
 java/org/apache/el/parser/SimpleCharStream.java | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/java/org/apache/el/parser/SimpleCharStream.java 
b/java/org/apache/el/parser/SimpleCharStream.java
index d8eda8b003..ee841ae628 100644
--- a/java/org/apache/el/parser/SimpleCharStream.java
+++ b/java/org/apache/el/parser/SimpleCharStream.java
@@ -43,9 +43,15 @@ public class SimpleCharStream {
 
 
     protected void ExpandBuff(boolean wrapAround) {
-        char[] newbuffer = new char[bufsize + 2048];
-        int newbufline[] = new int[bufsize + 2048];
-        int newbufcolumn[] = new int[bufsize + 2048];
+        // Grow the buffer geometrically. Growing by a fixed amount, as was
+        // done previously, makes the total cost of reading a single token
+        // that is larger than the initial buffer quadratic, since the entire
+        // contents of the buffer are copied on every expansion.
+        int newbufsize = (int) Math.min((long) bufsize * 2, Integer.MAX_VALUE);
+
+        char[] newbuffer = new char[newbufsize];
+        int newbufline[] = new int[newbufsize];
+        int newbufcolumn[] = new int[newbufsize];
 
         try {
             if (wrapAround) {
@@ -79,7 +85,7 @@ public class SimpleCharStream {
         }
 
 
-        bufsize += 2048;
+        bufsize = newbufsize;
         available = bufsize;
         tokenBegin = 0;
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to