summaryrefslogblamecommitdiff
path: root/examples/scala-js/javalib/src/main/scala/java/io/BufferedReader.scala
blob: 0f065238eab02ef87f4d15437c18c78ac69ebea9 (plain) (tree)
















































































































































                                                                             
package java.io

class BufferedReader(in: Reader, sz: Int) extends Reader {

  def this(in: Reader) = this(in, 4096)

  private[this] var buf = new Array[Char](sz)

  /** Last valid value in the buffer (exclusive) */
  private[this] var end = 0

  /** Next position to read from buffer */
  private[this] var pos = 0

  private[this] var closed = false

  private[this] var validMark = false

  override def close(): Unit = {
    closed = true
  }

  override def mark(readAheadLimit: Int): Unit = {
    ensureOpen()

    val srcBuf = buf
    if (buf.size < readAheadLimit)
      buf = new Array[Char](readAheadLimit)

    // Move data to beginning of buffer
    if (pos != 0 || (buf ne srcBuf))
      System.arraycopy(srcBuf, pos, buf, 0, end - pos)

    // Update internal state
    end -= pos
    pos = 0
    validMark = true
  }

  override def markSupported(): Boolean = true

  override def read(): Int = {
    ensureOpen()

    if (prepareRead()) {
      val res = buf(pos).toInt
      pos += 1
      res
    } else -1
  }

  override def read(cbuf: Array[Char], off: Int, len: Int): Int = {
    ensureOpen()

    if (off < 0 || len < 0 || len > cbuf.length - off)
      throw new IndexOutOfBoundsException

    if (len == 0) 0
    else if (prepareRead()) {
      val count = Math.min(len, end - pos)
      System.arraycopy(this.buf, pos, cbuf, off, count)
      pos += count
      count
    } else -1
  }

  def readLine(): String = {
    ensureOpen()

    var res = ""

    while (prepareRead() && buf(pos) != '\n' && buf(pos) != '\r') {
      res += buf(pos)
      pos += 1
    }

    if (pos >= end) {
      // We have reached the end of the stream (prepareRead() returned false)
      if (res == "") null
      else res
    } else {
      // Consume terminator
      pos += 1

      // Check whether we have a \r\n. This may overrun the buffer
      // and then push a value back which may unnecessarily invalidate
      // the mark. This mimics java behavior
      if (buf(pos-1) == '\r' && prepareRead() && buf(pos) == '\n')
        pos += 1 // consume '\n'

      res
    }
  }

  override def ready(): Boolean = {
    ensureOpen()
    pos < end || in.ready()
  }

  override def reset(): Unit = {
    ensureOpen()

    if (!validMark) throw new IOException("Mark invalid")
    pos = 0
  }

  override def skip(n: Long): Long = {
    if (n < 0) throw new IllegalArgumentException("n negative")
    else if (pos < end) {
      val count = Math.min(n, end - pos).toInt
      pos += count
      count.toLong
    } else {
      validMark = false
      in.skip(n)
    }
  }

  /** Prepare the buffer for reading. Returns false if EOF */
  private def prepareRead(): Boolean =
    pos < end || fillBuffer()

  /** Tries to fill the buffer. Returns false if EOF */
  private def fillBuffer(): Boolean = {
    if (validMark && end < buf.length) {
      // we may not do a full re-read, since we'll damage the mark.
      val read = in.read(buf, end, buf.length - end)
      if (read > 0) // protect from adding -1
        end += read
      read > 0
    } else {
      // Full re-read
      validMark = false
      end = in.read(buf)
      pos = 0
      end > 0
    }
  }

  private def ensureOpen(): Unit = {
    if (closed)
      throw new IOException("Operation on closed stream")
  }

}