diff options
author | Daniel C. Sobral <dcsobral@gmail.com> | 2012-01-26 18:07:49 -0200 |
---|---|---|
committer | Daniel C. Sobral <dcsobral@gmail.com> | 2012-01-26 18:07:49 -0200 |
commit | 2df3934168f62d8662d5f29c1b0db6119353406d (patch) | |
tree | b58c7db7ae0370dbacc35fbed8ce99b776aac743 | |
parent | c94d342b385fa510882721b8b7f2070750c60f0e (diff) | |
download | scala-2df3934168f62d8662d5f29c1b0db6119353406d.tar.gz scala-2df3934168f62d8662d5f29c1b0db6119353406d.tar.bz2 scala-2df3934168f62d8662d5f29c1b0db6119353406d.zip |
Performance improvements for CommentFactory.
A few small improvements to the parsing steps of CommentFactory (both
main parsing and wiki format parsing), resulting in a 30% reduction
of time spent in def parse, which gives me 5% reduction in docs.lib.
Turn docBody into a StringBuilder, to improve concatenation speed. Make
WikiParser and CharReader work with String instead of Array[String], to
reduce copying of data. Get rid of the implicit conversion from String
to Array[String].
Also, adjust a couple of regex to avoid expensive backtracking. The
new regex has a different semantics, which is arguably more correct
than the former one. There's absolutely no change in the generated
docs for Scala anyway.
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala | 100 |
1 files changed, 53 insertions, 47 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index ea36eb03c7..efa524503c 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -196,11 +196,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => /** The start of a scaladoc code block */ protected val CodeBlockStart = - new Regex("""(.*)((?:\{\{\{)|(?:\u000E<pre(?: [^>]*)?>\u000E))(.*)""") + new Regex("""(.*?)((?:\{\{\{)|(?:\u000E<pre(?: [^>]*)?>\u000E))(.*)""") /** The end of a scaladoc code block */ protected val CodeBlockEnd = - new Regex("""(.*)((?:\}\}\})|(?:\u000E</pre>\u000E))(.*)""") + new Regex("""(.*?)((?:\}\}\})|(?:\u000E</pre>\u000E))(.*)""") /** A key used for a tag map. The key is built from the name of the tag and * from the linked symbol if the tag has one. @@ -250,7 +250,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * @param remaining The lines that must still recursively be parsed. * @param inCodeBlock Whether the next line is part of a code block (in which no tags must be read). */ def parse0 ( - docBody: String, + docBody: StringBuilder, tags: Map[TagKey, List[String]], lastTagKey: Option[TagKey], remaining: List[String], @@ -258,9 +258,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => ): Comment = remaining match { case CodeBlockStart(before, marker, after) :: ls if (!inCodeBlock) => - if (before.trim != "") - parse0(docBody, tags, lastTagKey, before :: (marker + after) :: ls, false) - else if (after.trim != "") + if (!before.trim.isEmpty && !after.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, false) + else if (!before.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: ls, false) + else if (!after.trim.isEmpty) parse0(docBody, tags, lastTagKey, marker :: after :: ls, true) else lastTagKey match { case Some(key) => @@ -271,24 +273,26 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } parse0(docBody, tags + (key -> value), lastTagKey, ls, true) case None => - parse0(docBody + endOfLine + marker, tags, lastTagKey, ls, true) + parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, true) } case CodeBlockEnd(before, marker, after) :: ls => - if (before.trim != "") - parse0(docBody, tags, lastTagKey, before :: (marker + after) :: ls, true) - else if (after.trim != "") + if (!before.trim.isEmpty && !after.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, true) + if (!before.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: ls, true) + else if (!after.trim.isEmpty) parse0(docBody, tags, lastTagKey, marker :: after :: ls, false) else lastTagKey match { case Some(key) => val value = ((tags get key): @unchecked) match { - case Some(b :: bs) => (b + endOfLine + "}}}") :: bs + case Some(b :: bs) => (b + endOfLine + marker) :: bs case None => oops("lastTagKey set when no tag exists for key") } parse0(docBody, tags + (key -> value), lastTagKey, ls, false) case None => - parse0(docBody + endOfLine + marker, tags, lastTagKey, ls, false) + parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, false) } case SymbolTag(name, sym, body) :: ls if (!inCodeBlock) => @@ -311,8 +315,9 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => parse0(docBody, tags + (key -> value), lastTagKey, ls, inCodeBlock) case line :: ls => - val newBody = if (docBody == "") line else docBody + endOfLine + line - parse0(newBody, tags, lastTagKey, ls, inCodeBlock) + if (docBody.length > 0) docBody append endOfLine + docBody append line + parse0(docBody, tags, lastTagKey, ls, inCodeBlock) case Nil => @@ -350,7 +355,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } val com = createComment ( - body0 = Some(parseWiki(docBody, pos)), + body0 = Some(parseWiki(docBody.toString, pos)), authors0 = allTags(SimpleTagKey("author")), see0 = allTags(SimpleTagKey("see")), result0 = oneTag(SimpleTagKey("return")), @@ -374,7 +379,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } - parse0("", Map.empty, None, clean(comment), false) + parse0(new StringBuilder(comment.size), Map.empty, None, clean(comment), false) } @@ -385,7 +390,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * - Removed all end-of-line whitespace. * - Only `endOfLine` is used to mark line endings. */ def parseWiki(string: String, pos: Position): Body = { - new WikiParser(string.toArray, pos).document() + new WikiParser(string, pos).document() } /** TODO @@ -393,7 +398,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * @author Ingo Maier * @author Manohar Jonnalagedda * @author Gilles Dubochet */ - protected final class WikiParser(val buffer: Array[Char], pos: Position) extends CharReader(buffer) { wiki => + protected final class WikiParser(val buffer: String, pos: Position) extends CharReader(buffer) { wiki => var summaryParsed = false @@ -411,7 +416,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => def block(): Block = { if (checkSkipInitWhitespace("{{{")) code() - else if (checkSkipInitWhitespace("=")) + else if (checkSkipInitWhitespace('=')) title() else if (checkSkipInitWhitespace("----")) hrule() @@ -493,7 +498,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => def title(): Block = { jumpWhitespace() val inLevel = repeatJump("=") - val text = inline(check(Array.fill(inLevel)('='))) + val text = inline(check("=" * inLevel)) val outLevel = repeatJump("=", inLevel) if (inLevel != outLevel) reportError(pos, "unbalanced or unclosed heading") @@ -734,11 +739,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => val pc = char nextChar() // read EOL val ok = { - checkSkipInitWhitespace(Array(endOfLine)) || - checkSkipInitWhitespace(Array('=')) || - checkSkipInitWhitespace(Array('{', '{', '{')) || + checkSkipInitWhitespace(endOfLine) || + checkSkipInitWhitespace('=') || + checkSkipInitWhitespace("{{{") || checkList || - checkSkipInitWhitespace(Array('\u003D')) + checkSkipInitWhitespace('\u003D') } offset = poff char = pc @@ -751,7 +756,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } } - protected sealed class CharReader(buffer: Array[Char]) { reader => + protected sealed class CharReader(buffer: String) { reader => var char: Char = _ var offset: Int = 0 @@ -760,36 +765,37 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => if (offset >= buffer.length) char = endOfText else { - char = buffer(offset) + char = buffer charAt offset offset += 1 } } - implicit def strintToChars(s: String): Array[Char] = s.toArray - - def store(body: => Unit): String = { - val pre = offset - body - val post = offset - buffer.toArray.slice(pre, post).toString + final def check(chars: String): Boolean = { + val poff = offset + val pc = char + val ok = jump(chars) + offset = poff + char = pc + ok } - final def check(chars: Array[Char]): Boolean = { + def checkSkipInitWhitespace(c: Char): Boolean = { val poff = offset val pc = char - val ok = jump(chars) + jumpWhitespace() + val ok = jump(c) offset = poff char = pc ok } - def checkSkipInitWhitespace(chars: Array[Char]): Boolean = { + def checkSkipInitWhitespace(chars: String): Boolean = { val poff = offset val pc = char jumpWhitespace() val (ok0, chars0) = - if (chars.head == ' ') - (offset > poff, chars.tail) + if (chars.charAt(0) == ' ') + (offset > poff, chars substring 1) else (true, chars) val ok = ok0 && jump(chars0) @@ -825,16 +831,16 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => /** jumps all the characters in chars, consuming them in the process. * @return true only if the correct characters have been jumped */ - final def jump(chars: Array[Char]): Boolean = { + final def jump(chars: String): Boolean = { var index = 0 - while (index < chars.length && char == chars(index) && char != endOfText) { + while (index < chars.length && char == chars.charAt(index) && char != endOfText) { nextChar() index += 1 } index == chars.length } - final def checkedJump(chars: Array[Char]): Boolean = { + final def checkedJump(chars: String): Boolean = { val poff = offset val pc = char val ok = jump(chars) @@ -845,7 +851,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => ok } - final def repeatJump(chars: Array[Char], max: Int): Int = { + final def repeatJump(chars: String, max: Int): Int = { var count = 0 var more = true while (more && count < max) { @@ -857,7 +863,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => count } - final def repeatJump(chars: Array[Char]): Int = { + final def repeatJump(chars: String): Int = { var count = 0 var more = true while (more) { @@ -878,10 +884,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => count } - final def jumpUntil(chars: Array[Char]): Int = { + final def jumpUntil(chars: String): Int = { assert(chars.length > 0) var count = 0 - val c = chars(0) + val c = chars.charAt(0) while (!check(chars) && char != endOfText) { nextChar() while (char != c && char != endOfText) { @@ -922,10 +928,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => count } - final def readUntil(chars: Array[Char]): Int = { + final def readUntil(chars: String): Int = { assert(chars.length > 0) var count = 0 - val c = chars(0) + val c = chars.charAt(0) while (!check(chars) && char != endOfText) { readBuilder += char nextChar() |