Prevent NPE on backwards seek on Chunker
Ensure that a reset() is not immediately followed by a data member
access with a call to maybeInitialize(). Practically, this can occur if
an offset has progressed locally, but the remote indicates a non-0
committed size, and would always have thrown an NPE on a requisite seek,
proven with the accompanied test case.
Closes #10566.
PiperOrigin-RevId: 289615198
diff --git a/src/main/java/com/google/devtools/build/lib/remote/Chunker.java b/src/main/java/com/google/devtools/build/lib/remote/Chunker.java
index 8904e59..7ce80ee 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/Chunker.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/Chunker.java
@@ -139,18 +139,14 @@
/**
* Seek to an offset, if necessary resetting or initializing
*
- * <p>Closes any open resources (file handles, ...).
+ * <p>May close open resources in order to seek to an earlier offset.
*/
public void seek(long toOffset) throws IOException {
- maybeInitialize();
if (toOffset < offset) {
reset();
- if (toOffset != 0) {
- data.skip(toOffset);
- }
- } else if (offset != toOffset) {
- data.skip(toOffset - offset);
}
+ maybeInitialize();
+ ByteStreams.skipFully(data, toOffset - offset);
offset = toOffset;
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java b/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java
index 7fd0375..bb07311 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java
@@ -159,6 +159,20 @@
assertThat(next.getData()).hasSize(8);
}
+ @Test
+ public void seekBackwards() throws IOException {
+ byte[] data = new byte[10];
+ Chunker chunker = Chunker.builder().setInput(data).setChunkSize(10).build();
+
+ chunker.seek(4);
+ chunker.seek(2);
+
+ Chunk next = chunker.next();
+ assertThat(next).isNotNull();
+ assertThat(next.getOffset()).isEqualTo(2);
+ assertThat(next.getData()).hasSize(8);
+ }
+
private void assertNextEquals(Chunker chunker, byte... data) throws IOException {
assertThat(chunker.hasNext()).isTrue();
ByteString next = chunker.next().getData();