8322141: SequenceInputStream.transferTo should not return as soon as Long.MAX_VALUE bytes have been transferred

Reviewed-by: vsitnikov, bpb, jpai
This commit is contained in:
Markus KARG 2023-12-20 17:00:44 +00:00 committed by Brian Burkhalter
parent e0bad5153b
commit 2d609557ff
2 changed files with 50 additions and 2 deletions

View file

@ -242,11 +242,14 @@ public class SequenceInputStream extends InputStream {
if (getClass() == SequenceInputStream.class) { if (getClass() == SequenceInputStream.class) {
long transferred = 0; long transferred = 0;
while (in != null) { while (in != null) {
long numTransferred = in.transferTo(out);
// increment the total transferred byte count
// only if we haven't already reached the Long.MAX_VALUE
if (transferred < Long.MAX_VALUE) { if (transferred < Long.MAX_VALUE) {
try { try {
transferred = Math.addExact(transferred, in.transferTo(out)); transferred = Math.addExact(transferred, numTransferred);
} catch (ArithmeticException ignore) { } catch (ArithmeticException ignore) {
return Long.MAX_VALUE; transferred = Long.MAX_VALUE;
} }
} }
nextStream(); nextStream();

View file

@ -24,6 +24,7 @@
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.SequenceInputStream; import java.io.SequenceInputStream;
import java.util.Arrays; import java.util.Arrays;
@ -39,6 +40,7 @@ import jdk.test.lib.RandomFactory;
import static java.lang.String.format; import static java.lang.String.format;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
@ -126,6 +128,49 @@ public class TransferTo {
outputStreamProvider, createRandomBytes(4096, 0), 0, 4096); outputStreamProvider, createRandomBytes(4096, 0), 0, 4096);
} }
/*
* Special case: Assert subsequent input stream is read when preceding stream already was MAX_VALUE long.
* Note: Not testing actual content as it requires multiple GBs of memory and long time.
*/
@Test
public void testHugeStream() throws Exception {
InputStream is1 = repeat(0, Long.MAX_VALUE);
InputStream is2 = repeat(0, 1);
assertNotEquals(is1.available(), 0);
assertNotEquals(is2.available(), 0);
SequenceInputStream sis = new SequenceInputStream(is1, is2);
OutputStream nos = OutputStream.nullOutputStream();
sis.transferTo(nos);
assertEquals(is1.available(), 0);
assertEquals(is2.available(), 0);
}
/*
* Produces an input stream that returns b count times.
* Builds a dysfunctional mock that solely implements
* available() and transferTo() particually,
* but fails with any other operation.
*/
private static InputStream repeat(int b, long count) {
return new InputStream() {
private long pos;
@Override
public int available() throws IOException {
return (int) Math.min(count - pos, Integer.MAX_VALUE);
}
@Override
public int read() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long transferTo(OutputStream os) throws IOException {
// skipping actual writing to os to spare time
pos += count;
return count;
}
};
}
/* /*
* Asserts that the transferred content is correct, i.e., compares the bytes * Asserts that the transferred content is correct, i.e., compares the bytes
* actually transferred to those expected. The position of the input and * actually transferred to those expected. The position of the input and