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

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

commit 137f8d30e109a0f62facc5a27b7b71c89775ca36
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 d68f7c54cb..1a84886078 100644
--- a/java/org/apache/el/parser/SimpleCharStream.java
+++ b/java/org/apache/el/parser/SimpleCharStream.java
@@ -58,9 +58,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) {
@@ -93,7 +99,7 @@ public class SimpleCharStream {
             throw new Error(t.getMessage());
         }
 
-        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