Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion base-io/src/main/java/build/base/io/LookaheadReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ private boolean prepare(final int desired) {
* available
*/
private boolean prepare() {
return prepare(DEFAULT_LOOKAHEAD);
return prepare(1);
}

/**
Expand Down
31 changes: 31 additions & 0 deletions base-io/src/test/java/build/base/io/LookaheadReaderTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import java.io.IOException;
import java.io.StringReader;
import java.util.concurrent.atomic.AtomicInteger;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
Expand Down Expand Up @@ -255,4 +256,34 @@ void shouldLookaheadConsumeNSelfAdjustsBufferSize() {
assertThat(reader.consume(initialBufferSize * 2))
.isEqualTo(Strings.repeat("a", initialBufferSize * 2));
}

// prepare() previously passed DEFAULT_LOOKAHEAD as the desired size even for single-character
// operations, so "remaining < desired" became true again after consuming just one character
// from a full buffer, forcing a fresh underlying read() on every single consume(). Asserting
// on the raw read() call count (rather than just consumed output) is what catches that.
@Test
void shouldNotRereadUnderlyingStreamForEverySingleCharacterConsumed() {
final var content = Strings.repeat("a", 10_000);
final var readCalls = new AtomicInteger();

final var reader = new LookaheadReader(new StringReader(content) {
@Override
public int read(char [] cbuf, int off, int len) throws IOException {
readCalls.incrementAndGet();
return super.read(cbuf, off, len);
}
});

int consumed = 0;
while (reader.available()) {
reader.consume();
consumed++;
}

assertThat(consumed)
.isEqualTo(content.length());

assertThat(readCalls.get())
.isLessThan(content.length() / 10);
}
}
Loading