Skip to content

Commit

Permalink
Change behavior of recording readers to not capture more after rewind (
Browse files Browse the repository at this point in the history
…#482)

* Change behavior of recording readers to not capture more input after rewind

* Fixed compilation

* Fixed reader
  • Loading branch information
vigoo authored Nov 10, 2021
1 parent 27d6cb6 commit d1f8742
Showing 1 changed file with 25 additions and 40 deletions.
65 changes: 25 additions & 40 deletions zio-json/shared/src/main/scala/zio/json/internal/readers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ private[zio] final class UnexpectedEnd
)
with NoStackTrace

private[zio] final class RewindTwice
extends Exception(
"RecordingReader's rewind was called twice"
)

/**
* A Reader that can retract and replay the last char that it read.
*
Expand Down Expand Up @@ -130,8 +135,8 @@ final class WithRetractReader(in: java.io.Reader) extends RetractReader with Aut

/**
* Records the contents of an underlying Reader and allows rewinding back to
* the beginning many times. If rewound and reading continues past the
* recording, the recording continues.
* the beginning once. If rewound and reading continues past the
* recording, the recording no longer continues.
*
* To avoid feature interaction edge cases, `retract` is not allowed as the
* first action nor is `retract` allowed to happen immediately before or after
Expand All @@ -142,10 +147,7 @@ private[zio] sealed trait RecordingReader extends RetractReader {
}
private[zio] object RecordingReader {
def apply(in: OneCharReader): RecordingReader =
in match {
case rr: PlaybackReader => new WrappedRecordingReader(rr)
case _ => new WithRecordingReader(in, 64)
}
new WithRecordingReader(in, 64)
}

// used to optimise RecordingReader
Expand Down Expand Up @@ -183,25 +185,35 @@ private[zio] final class WithRecordingReader(in: OneCharReader, initial: Int)
if (reading == eob) throw new UnexpectedEnd
val v = tape(reading)
reading += 1
if (reading >= writing)
if (reading >= writing) {
reading = -1 // caught up
writing = -1 // stop recording
}
v
} else {
val v = in.readChar()
tape(writing) = v
writing += 1
if (writing == tape.length)
tape = Arrays.copyOf(tape, tape.length * 2)
if (writing != -1) {
tape(writing) = v
writing += 1
if (writing == tape.length)
tape = Arrays.copyOf(tape, tape.length * 2)
}
v
}

def rewind(): Unit = reading = 0
def rewind(): Unit =
if (writing != -1)
reading = 0
else throw new RewindTwice

def retract(): Unit =
if (reading == -1) {
in match {
case rr: RetractReader =>
rr.retract()
writing -= 1 // factor in retracted delegate
if (writing != -1) {
writing -= 1 // factor in retracted delegate
}

case _ =>
reading = writing - 1
Expand All @@ -217,30 +229,3 @@ private[zio] final class WithRecordingReader(in: OneCharReader, initial: Int)

def history(idx: Int): Char = tape(idx)
}

// since the underlying is a recording reader, it implies that anything we would
// be recording has already been recorded as part of a larger recording.
// Therefore, reuse the existing recording.
private[zio] final class WrappedRecordingReader(rr: PlaybackReader) extends RecordingReader with PlaybackReader {

private[this] val start = rr.offset()
private[this] var i: Int = start
def offset(): Int = i

def close(): Unit = rr.close()

override def read(): Int =
try readChar().toInt
catch { case _: UnexpectedEnd => -1 }

override def readChar(): Char = {
val v = if (rr.offset() <= i) rr.readChar() else history(i)
i += 1
v
}

def retract(): Unit = i -= 1
def rewind(): Unit = i = start
def history(idx: Int): Char = rr.history(idx)

}

0 comments on commit d1f8742

Please sign in to comment.