diff options
author | Paul Phillips <paulp@improving.org> | 2011-07-01 06:58:03 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-07-01 06:58:03 +0000 |
commit | 300cb9e1ee6559ea08b58b04dd89de94e4449fce (patch) | |
tree | aafc568430fc96757c484754f5639607e6f51ee9 | |
parent | 9d02b4adea3512e671e97c11dec9dd7fd8514c49 (diff) | |
download | scala-300cb9e1ee6559ea08b58b04dd89de94e4449fce.tar.gz scala-300cb9e1ee6559ea08b58b04dd89de94e4449fce.tar.bz2 scala-300cb9e1ee6559ea08b58b04dd89de94e4449fce.zip |
Keep BufferedSource from losing buffered data w...
Keep BufferedSource from losing buffered data when a derivative iterator
is created via getLines. Closes #4671, #4662, no review.
-rw-r--r-- | src/library/scala/io/BufferedSource.scala | 59 | ||||
-rw-r--r-- | test/files/run/bug4671.check | 46 | ||||
-rw-r--r-- | test/files/run/bug4671.scala | 13 |
3 files changed, 108 insertions, 10 deletions
diff --git a/src/library/scala/io/BufferedSource.scala b/src/library/scala/io/BufferedSource.scala index 60d67a0b58..b5144a891f 100644 --- a/src/library/scala/io/BufferedSource.scala +++ b/src/library/scala/io/BufferedSource.scala @@ -8,7 +8,7 @@ package scala.io -import java.io.{ InputStream, BufferedReader, InputStreamReader } +import java.io.{ InputStream, BufferedReader, InputStreamReader, PushbackReader } import Source.DefaultBufSize import scala.collection.Iterator @@ -22,20 +22,59 @@ class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val cod def reader() = new InputStreamReader(inputStream, codec.decoder) def bufferedReader() = new BufferedReader(reader(), bufferSize) - override val iter = { - val reader = bufferedReader() - Iterator continually (codec wrap reader.read()) takeWhile (_ != -1) map (_.toChar) + // The same reader has to be shared between the iterators produced + // by iter and getLines. This is because calling hasNext can cause a + // block of data to be read from the stream, which will then be lost + // to getLines if it creates a new reader, even though next() was + // never called on the original. + private var charReaderCreated = false + private lazy val charReader = { + charReaderCreated = true + bufferedReader() } + override lazy val iter = ( + Iterator + continually (codec wrap charReader.read()) + takeWhile (_ != -1) + map (_.toChar) + ) + class BufferedLineIterator extends Iterator[String] { - val bufReader = BufferedSource.this.bufferedReader() - var nextLine = bufReader.readLine + // Don't want to lose a buffered char sitting in iter either. Yes, + // this is ridiculous, but if I can't get rid of Source, and all the + // Iterator bits are designed into Source, and people create Sources + // in the repl, and the repl calls toString for the result line, and + // that calls hasNext to find out if they're empty, and that leads + // to chars being buffered, and no, I don't work here, they left a + // door unlocked. + private val lineReader: BufferedReader = { + // To avoid inflicting this silliness indiscriminately, we can + // skip it if the char reader was never created: and almost always + // it will not have been created, since getLines will be called + // immediately on the source. + if (charReaderCreated && iter.hasNext) { + val pb = new PushbackReader(charReader) + pb unread iter.next() + new BufferedReader(pb, bufferSize) + } + else charReader + } + var nextLine: String = null - override def hasNext = nextLine != null + override def hasNext = { + if (nextLine == null) + nextLine = lineReader.readLine + + nextLine != null + } override def next(): String = { - val result = nextLine - nextLine = bufReader.readLine - result + val result = { + if (nextLine == null) lineReader.readLine + else try nextLine finally nextLine = null + } + if (result == null) Iterator.empty.next + else result } } diff --git a/test/files/run/bug4671.check b/test/files/run/bug4671.check new file mode 100644 index 0000000000..dc92c9a72f --- /dev/null +++ b/test/files/run/bug4671.check @@ -0,0 +1,46 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> object o { val file = sys.props("partest.cwd") + "/bug4671.scala" } +defined module o + +scala> val s = scala.io.Source.fromFile(o.file) +s: scala.io.BufferedSource = non-empty iterator + +scala> println(s.getLines.mkString("\n")) +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + // My god...it's full of quines + def code = """ +object o { val file = sys.props("partest.cwd") + "/bug4671.scala" } +val s = scala.io.Source.fromFile(o.file) +println(s.getLines.mkString("\n")) + +val s = scala.io.Source.fromFile(o.file) +println(s.mkString("")) +""".trim +} + +scala> + +scala> val s = scala.io.Source.fromFile(o.file) +s: scala.io.BufferedSource = non-empty iterator + +scala> println(s.mkString("")) +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + // My god...it's full of quines + def code = """ +object o { val file = sys.props("partest.cwd") + "/bug4671.scala" } +val s = scala.io.Source.fromFile(o.file) +println(s.getLines.mkString("\n")) + +val s = scala.io.Source.fromFile(o.file) +println(s.mkString("")) +""".trim +} + + +scala> diff --git a/test/files/run/bug4671.scala b/test/files/run/bug4671.scala new file mode 100644 index 0000000000..0c1e5908bf --- /dev/null +++ b/test/files/run/bug4671.scala @@ -0,0 +1,13 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + // My god...it's full of quines + def code = """ +object o { val file = sys.props("partest.cwd") + "/bug4671.scala" } +val s = scala.io.Source.fromFile(o.file) +println(s.getLines.mkString("\n")) + +val s = scala.io.Source.fromFile(o.file) +println(s.mkString("")) +""".trim +} |