Author: markt Date: Wed Sep 5 22:14:08 2012 New Revision: 1381417 URL: http://svn.apache.org/viewvc?rev=1381417&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=53713 Performance improvement (up to four times faster parsing) for JSP processing. Patch provided by Sheldon Shao.
Modified: tomcat/tc7.0.x/trunk/ (props changed) tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/JspReader.java tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/Mark.java tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Propchange: tomcat/tc7.0.x/trunk/ ------------------------------------------------------------------------------ Merged /tomcat/trunk:r1381411 Modified: tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/JspReader.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/JspReader.java?rev=1381417&r1=1381416&r2=1381417&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/JspReader.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/JspReader.java Wed Sep 5 22:14:08 2012 @@ -193,6 +193,65 @@ class JspReader { } /** + * A faster approach than calling {@link #mark()} & {@link #nextChar()}. + * However, this approach is only safe if the mark is only used within the + * JspReader. + */ + private int nextChar(Mark mark) throws JasperException { + if (!hasMoreInput()) { + return -1; + } + + int ch = current.stream[current.cursor]; + + mark.init(current, singleFile); + + current.cursor++; + + if (ch == '\n') { + current.line++; + current.col = 0; + } else { + current.col++; + } + return ch; + } + + /** + * Search the given character, If it was found, then mark the current cursor + * and the cursor point to next character. + */ + private Boolean indexOf(char c, Mark mark) throws JasperException { + if (!hasMoreInput()) + return null; + + int end = current.stream.length; + int ch; + int line = current.line; + int col = current.col; + int i = current.cursor; + for(; i < end; i ++) { + ch = current.stream[i]; + + if (ch == c) { + mark.update(i, line, col); + } + if (ch == '\n') { + line++; + col = 0; + } else { + col++; + } + if (ch == c) { + current.update(i+1, line, col); + return Boolean.TRUE; + } + } + current.update(i, line, col); + return Boolean.FALSE; + } + + /** * Back up the current cursor by one char, assumes current.cursor > 0, * and that the char to be pushed back is not '\n'. */ @@ -205,10 +264,11 @@ class JspReader { Mark oldstart = mark(); reset(start); CharArrayWriter caw = new CharArrayWriter(); - while (!stop.equals(mark())) + while (!markEquals(stop)) { caw.write(nextChar()); + } caw.close(); - reset(oldstart); + setCurrent(oldstart); return caw.toString(); } @@ -222,11 +282,27 @@ class JspReader { return new Mark(current); } + + /** + * This method avoids a call to {@link #mark()} when doing comparison. + */ + private boolean markEquals(Mark another) { + return another.equals(current); + } + void reset(Mark mark) { current = new Mark(mark); } /** + * Similar to {@link #reset(Mark)} but no new Mark will be created. + * Therefore, the parameter mark must NOT be used in other places. + */ + private void setCurrent(Mark mark) { + current = mark; + } + + /** * search the stream for a match to a string * @param string The string to match * @return <strong>true</strong> is one is found, the current position @@ -234,17 +310,40 @@ class JspReader { * false</strong> otherwise, position in stream unchanged. */ boolean matches(String string) throws JasperException { - Mark mark = mark(); - int ch = 0; - int i = 0; - do { - ch = nextChar(); - if (((char) ch) != string.charAt(i++)) { - reset(mark); - return false; - } - } while (i < string.length()); - return true; + int len = string.length(); + int cursor = current.cursor; + int streamSize = current.stream.length; + if (cursor + len < streamSize) { //Try to scan in memory + int line = current.line; + int col = current.col; + int ch; + int i = 0; + for(; i < len; i ++) { + ch = current.stream[i+cursor]; + if (string.charAt(i) != ch) { + return false; + } + if (ch == '\n') { + line ++; + col = 0; + } else { + col++; + } + } + current.update(i+cursor, line, col); + } else { + Mark mark = mark(); + int ch = 0; + int i = 0; + do { + ch = nextChar(); + if (((char) ch) != string.charAt(i++)) { + setCurrent(mark); + return false; + } + } while (i < len); + } + return true; } boolean matchesETag(String tagName) throws JasperException { @@ -256,7 +355,7 @@ class JspReader { if (nextChar() == '>') return true; - reset(mark); + setCurrent(mark); return false; } @@ -271,7 +370,7 @@ class JspReader { if (nextChar() == '>') return true; - reset(mark); + setCurrent(mark); return false; } @@ -290,7 +389,7 @@ class JspReader { skipSpaces(); boolean result = matches( s ); if( !result ) { - reset( mark ); + setCurrent(mark); } return result; @@ -315,24 +414,29 @@ class JspReader { * otherwise. */ Mark skipUntil(String limit) throws JasperException { - Mark ret = null; + Mark ret = mark(); int limlen = limit.length(); - int ch; + char firstChar = limit.charAt(0); + Boolean result = null; + Mark restart = null; skip: - for (ret = mark(), ch = nextChar() ; ch != -1 ; - ret = mark(), ch = nextChar()) { - if (ch == limit.charAt(0)) { - Mark restart = mark(); - for (int i = 1 ; i < limlen ; i++) { - if (peekChar() == limit.charAt(i)) - nextChar(); - else { - reset(restart); - continue skip; - } - } - return ret; + while((result = indexOf(firstChar, ret)) != null) { + if (result.booleanValue()) { + if (restart != null) { + restart.init(current, singleFile); + } else { + restart = mark(); + } + for (int i = 1 ; i < limlen ; i++) { + if (peekChar() == limit.charAt(i)) { + nextChar(); + } else { + setCurrent(restart); + continue skip; + } + } + return ret; } } return null; @@ -349,18 +453,16 @@ class JspReader { * otherwise. */ Mark skipUntilIgnoreEsc(String limit) throws JasperException { - Mark ret = null; + Mark ret = mark(); int limlen = limit.length(); int ch; int prev = 'x'; // Doesn't matter - + char firstChar = limit.charAt(0); skip: - for (ret = mark(), ch = nextChar() ; ch != -1 ; - ret = mark(), prev = ch, ch = nextChar()) { + for (ch = nextChar(ret) ; ch != -1 ; prev = ch, ch = nextChar(ret)) { if (ch == '\\' && prev == '\\') { ch = 0; // Double \ is not an escape char anymore - } - else if (ch == limit.charAt(0) && prev != '\\') { + } else if (ch == firstChar && prev != '\\') { for (int i = 1 ; i < limlen ; i++) { if (peekChar() == limit.charAt(i)) nextChar(); @@ -477,10 +579,10 @@ class JspReader { Mark mark = mark(); if (((ch = nextChar()) == '>') || ((ch == '-') && (nextChar() == '>'))) { - reset(mark); + setCurrent(mark); return true; } else { - reset(mark); + setCurrent(mark); return false; } } Modified: tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/Mark.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/Mark.java?rev=1381417&r1=1381416&r2=1381417&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/Mark.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/jasper/compiler/Mark.java Wed Sep 5 22:14:08 2012 @@ -89,22 +89,37 @@ final class Mark { * Constructor */ Mark(Mark other) { + init(other, false); + } + + void update(int cursor, int line, int col) { + this.cursor = cursor; + this.line = line; + this.col = col; + } - this.reader = other.reader; - this.ctxt = other.reader.getJspCompilationContext(); - this.stream = other.stream; - this.fileId = other.fileId; - this.fileName = other.fileName; + void init(Mark other, boolean singleFile) { this.cursor = other.cursor; this.line = other.line; this.col = other.col; - this.baseDir = other.baseDir; - this.encoding = other.encoding; - // clone includeStack without cloning contents - includeStack = new Stack<IncludeState>(); - for ( int i=0; i < other.includeStack.size(); i++ ) { - includeStack.addElement( other.includeStack.elementAt(i) ); + if (!singleFile) { + this.reader = other.reader; + this.ctxt = other.ctxt; + this.stream = other.stream; + this.fileId = other.fileId; + this.fileName = other.fileName; + this.baseDir = other.baseDir; + this.encoding = other.encoding; + + if (includeStack == null) { + includeStack = new Stack<IncludeState>(); + } else { + includeStack.clear(); + } + for (int i = 0; i < other.includeStack.size(); i++ ) { + includeStack.addElement(other.includeStack.elementAt(i)); + } } } Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1381417&r1=1381416&r2=1381417&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Wed Sep 5 22:14:08 2012 @@ -66,6 +66,14 @@ </fix> </changelog> </subsection> + <subsection name="Jasper"> + <changelog> + <scode> + <bug>53713</bug>: Performance improvement of up to four times faster + parsing of JSP pages. Patch provided by Sheldon Shao. (markt) + </scode> + </changelog> + </subsection> <subsection name="Cluster"> <changelog> <add> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org