]> Cypherpunks repositories - gostls13.git/commitdiff
text/template: improve lexer performance in finding left delimiters.
authorPaul Borman <borman@google.com>
Tue, 12 Jul 2016 15:54:09 +0000 (08:54 -0700)
committerRob Pike <r@golang.org>
Tue, 13 Sep 2016 12:51:13 +0000 (12:51 +0000)
The existing implementation calls l.next for each run up to the next
instance of the left delimiter ({{).  For ascii text, this is multiple
function calls per byte.  Change to use strings.Index to find the left
delimiter.  The performace improvement ranges from 1:1 (no text outside
of {{}}'s) to multiple times faster (9:1 was seen on 8K of text with no
{{ }}'s).

Change-Id: I2f82bea63b78b6714f09a725f7b2bbb00a3448a3
Reviewed-on: https://go-review.googlesource.com/24863
Reviewed-by: Rob Pike <r@golang.org>
Run-TryBot: Rob Pike <r@golang.org>

src/text/template/parse/lex.go

index 079c0ea6f7166706f938e679af431b73cfdf8811..7811cc1d4fa7114ca21beb4cafb2e944c6dba042 100644 (file)
@@ -236,24 +236,23 @@ const (
 
 // lexText scans until an opening action delimiter, "{{".
 func lexText(l *lexer) stateFn {
-       for {
-               delim, trimSpace := l.atLeftDelim()
-               if delim {
-                       trimLength := Pos(0)
-                       if trimSpace {
-                               trimLength = rightTrimLength(l.input[l.start:l.pos])
-                       }
-                       l.pos -= trimLength
-                       if l.pos > l.start {
-                               l.emit(itemText)
-                       }
-                       l.pos += trimLength
-                       l.ignore()
-                       return lexLeftDelim
+       l.width = 0
+       if x := strings.Index(l.input[l.pos:], l.leftDelim); x >= 0 {
+               ldn := Pos(len(l.leftDelim))
+               l.pos += Pos(x)
+               trimLength := Pos(0)
+               if strings.HasPrefix(l.input[l.pos+ldn:], leftTrimMarker) {
+                       trimLength = rightTrimLength(l.input[l.start:l.pos])
                }
-               if l.next() == eof {
-                       break
+               l.pos -= trimLength
+               if l.pos > l.start {
+                       l.emit(itemText)
                }
+               l.pos += trimLength
+               l.ignore()
+               return lexLeftDelim
+       } else {
+               l.pos = Pos(len(l.input))
        }
        // Correctly reached EOF.
        if l.pos > l.start {
@@ -263,16 +262,6 @@ func lexText(l *lexer) stateFn {
        return nil
 }
 
-// atLeftDelim reports whether the lexer is at a left delimiter, possibly followed by a trim marker.
-func (l *lexer) atLeftDelim() (delim, trimSpaces bool) {
-       if !strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
-               return false, false
-       }
-       // The left delim might have the marker afterwards.
-       trimSpaces = strings.HasPrefix(l.input[l.pos+Pos(len(l.leftDelim)):], leftTrimMarker)
-       return true, trimSpaces
-}
-
 // rightTrimLength returns the length of the spaces at the end of the string.
 func rightTrimLength(s string) Pos {
        return Pos(len(s) - len(strings.TrimRight(s, spaceChars)))