From 300cb9e1ee6559ea08b58b04dd89de94e4449fce Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 1 Jul 2011 06:58:03 +0000 Subject: 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. --- src/library/scala/io/BufferedSource.scala | 59 +++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 10 deletions(-) (limited to 'src') 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 } } -- cgit v1.2.3