Skip to content

Commit b67b0b7

Browse files
committed
Optimize buffering in Scanner
Avoid an extra toCharArray when converting a buffer to a name
1 parent 0b2bc3f commit b67b0b7

File tree

2 files changed

+37
-45
lines changed

2 files changed

+37
-45
lines changed

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import core.StdNames._, core.Comments._
77
import util.SourceFile
88
import java.lang.Character.isDigit
99
import scala.internal.Chars._
10-
import util.SourcePosition
10+
import util.{SourcePosition, CharBuffer}
1111
import util.Spans.Span
1212
import config.Config
1313
import config.Printers.lexical
@@ -21,38 +21,6 @@ import config.Feature.migrateTo3
2121
import config.SourceVersion._
2222
import reporting.Message
2323

24-
object Cbufs {
25-
import java.lang.StringBuilder
26-
27-
private final val TargetCapacity = 256
28-
29-
opaque type Cbuf = StringBuilder
30-
object Cbuf:
31-
def apply(): Cbuf = new StringBuilder(TargetCapacity)
32-
33-
extension (buf: Cbuf):
34-
def clear(): Unit = {
35-
if buf.capacity() > TargetCapacity then
36-
buf.setLength(TargetCapacity)
37-
buf.trimToSize()
38-
end if
39-
buf.setLength(0)
40-
}
41-
def toCharArray: Array[Char] = {
42-
val n = buf.length()
43-
val res = new Array[Char](n)
44-
buf.getChars(0, n, res, 0)
45-
res
46-
}
47-
def append(c: Char): buf.type = { buf.append(c) ; buf }
48-
def isEmpty: Boolean = buf.length() == 0
49-
def length: Int = buf.length()
50-
def last: Char = buf.charAt(buf.length() - 1)
51-
end extension
52-
}
53-
54-
import Cbufs._
55-
5624
object Scanners {
5725

5826
/** Offset into source character array */
@@ -142,22 +110,16 @@ object Scanners {
142110

143111
/** A character buffer for literals
144112
*/
145-
protected val litBuf = Cbuf()
113+
protected val litBuf = CharBuffer()
146114

147115
/** append Unicode character to "litBuf" buffer
148116
*/
149117
protected def putChar(c: Char): Unit = litBuf.append(c)
150118

151-
/** Return buffer contents and clear */
152-
def flushBuf(buf: Cbuf): String = {
153-
val str = buf.toString
154-
buf.clear()
155-
str
156-
}
157-
158119
/** Clear buffer and set name and token */
159120
def finishNamed(idtoken: Token = IDENTIFIER, target: TokenData = this): Unit = {
160-
target.name = termName(flushBuf(litBuf))
121+
target.name = termName(litBuf.chars, 0, litBuf.length)
122+
litBuf.clear()
161123
target.token = idtoken
162124
if (idtoken == IDENTIFIER)
163125
target.token = toToken(target.name)
@@ -168,7 +130,8 @@ object Scanners {
168130

169131
/** Clear buffer and set string */
170132
def setStrVal(): Unit =
171-
strVal = flushBuf(litBuf)
133+
strVal = litBuf.toString
134+
litBuf.clear()
172135

173136
@inline def isNumberSeparator(c: Char): Boolean = c == '_'
174137

@@ -241,7 +204,7 @@ object Scanners {
241204
def getDocComment(pos: Int): Option[Comment] = docstringMap.get(pos)
242205

243206
/** A buffer for comments */
244-
private val commentBuf = Cbuf()
207+
private val commentBuf = CharBuffer()
245208

246209
private def handleMigration(keyword: Token): Token =
247210
if keyword == ERASED && !ctx.settings.YerasedTerms.value then IDENTIFIER
@@ -888,7 +851,8 @@ object Scanners {
888851
def finishComment(): Boolean = {
889852
if (keepComments) {
890853
val pos = Span(start, charOffset - 1, start)
891-
val comment = Comment(pos, flushBuf(commentBuf))
854+
val comment = Comment(pos, commentBuf.toString)
855+
commentBuf.clear()
892856
commentPosBuf += pos
893857

894858
if (comment.isDocComment)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dotty.tools
2+
package dotc
3+
package util
4+
5+
/** A character buffer that exposes the internal array for reading.
6+
* That way we can avoid copying when converting to names.
7+
*/
8+
class CharBuffer(initialSize: Int = 1024):
9+
private var cs: Array[Char] = new Array[Char](initialSize)
10+
private var len: Int = 0
11+
12+
def append(ch: Char): Unit =
13+
if len == cs.length then
14+
val cs1 = new Array[Char](len * 2)
15+
Array.copy(cs, 0, cs1, 0, len)
16+
cs = cs1
17+
cs(len) = ch
18+
len += 1
19+
20+
def chars = cs
21+
def length = len
22+
def isEmpty: Boolean = len == 0
23+
def last: Char = cs(len - 1)
24+
def clear(): Unit = len = 0
25+
26+
override def toString = String(cs, 0, len)
27+
28+

0 commit comments

Comments
 (0)