Skip to content

Commit 4c9e658

Browse files
committed
[IO-853] BoundedInputStream.reset() not updating count
1 parent f7c4334 commit 4c9e658

3 files changed

Lines changed: 150 additions & 5 deletions

File tree

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ The <action> type attribute can be add,update,fix,remove.
5151
<action dev="ggregory" type="fix" due-to="Gary Gregory">Reimplement FileSystemUtils using NIO.</action>
5252
<action dev="ggregory" type="fix" issue="IO-851" due-to="Sebb, Gary Gregory">FileSystemUtils no longer throws IllegalStateException.</action>
5353
<action dev="ggregory" type="fix" due-to="Gary Gregory">Avoid possible NullPointerException in FileUtils.listAccumulate(File, IOFileFilter, IOFileFilter, FileVisitOption...).</action>
54+
<action dev="ggregory" type="fix" issue="IO-853" due-to="Mike Drob, Gary Gregory">BoundedInputStream.reset() not updating count.</action>
5455
<!-- UPDATE -->
5556
<action dev="ggregory" type="update" due-to="Gary Gregory">Bump commons.bytebuddy.version from 1.14.12 to 1.14.13 #605.</action>
5657
<action dev="ggregory" type="update" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 67 to 69 #608.</action>

src/main/java/org/apache/commons/io/input/BoundedInputStream.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ public static Builder builder() {
249249
/** The current count of bytes counted. */
250250
private long count;
251251

252+
/** The current mark. */
253+
private long mark;
254+
252255
/** The max count of bytes to read. */
253256
private final long maxCount;
254257

@@ -347,7 +350,7 @@ public void close() throws IOException {
347350
* @return The count of bytes read.
348351
* @since 2.12.0
349352
*/
350-
public long getCount() {
353+
public synchronized long getCount() {
351354
return count;
352355
}
353356

@@ -404,6 +407,7 @@ public boolean isPropagateClose() {
404407
@Override
405408
public synchronized void mark(final int readLimit) {
406409
in.mark(readLimit);
410+
mark = count;
407411
}
408412

409413
/**
@@ -482,6 +486,7 @@ public int read(final byte[] b, final int off, final int len) throws IOException
482486
@Override
483487
public synchronized void reset() throws IOException {
484488
in.reset();
489+
count = mark;
485490
}
486491

487492
/**
@@ -504,7 +509,7 @@ public void setPropagateClose(final boolean propagateClose) {
504509
* @throws IOException if an I/O error occurs.
505510
*/
506511
@Override
507-
public long skip(final long n) throws IOException {
512+
public synchronized long skip(final long n) throws IOException {
508513
final long skip = super.skip(toReadLen(n));
509514
count += skip;
510515
return skip;

src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.concurrent.atomic.AtomicBoolean;
2828

2929
import org.apache.commons.io.IOUtils;
30+
import org.apache.commons.lang3.mutable.MutableInt;
3031
import org.junit.jupiter.api.Test;
3132
import org.junit.jupiter.params.ParameterizedTest;
3233
import org.junit.jupiter.params.provider.ValueSource;
@@ -36,10 +37,12 @@
3637
*/
3738
public class BoundedInputStreamTest {
3839

39-
private void compare(final String msg, final byte[] expected, final byte[] actual) {
40-
assertEquals(expected.length, actual.length, msg + " length");
40+
private void compare(final String message, final byte[] expected, final byte[] actual) {
41+
assertEquals(expected.length, actual.length, () -> message + " (array length equals check)");
42+
final MutableInt mi = new MutableInt();
4143
for (int i = 0; i < expected.length; i++) {
42-
assertEquals(expected[i], actual[i], msg + " byte[" + i + "]");
44+
mi.setValue(i);
45+
assertEquals(expected[i], actual[i], () -> message + " byte[" + mi + "]");
4346
}
4447
}
4548

@@ -144,6 +147,107 @@ public void testCounts(final long startCount) throws Exception {
144147
}
145148
}
146149

150+
@Test
151+
public void testMarkReset() throws Exception {
152+
final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
153+
final int helloWorldLen = helloWorld.length;
154+
final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
155+
final byte[] world = " World".getBytes(StandardCharsets.UTF_8);
156+
final int helloLen = hello.length;
157+
// limit = -1
158+
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
159+
assertTrue(bounded.markSupported());
160+
bounded.mark(0);
161+
compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
162+
// should be invariant
163+
assertTrue(bounded.markSupported());
164+
// again
165+
bounded.reset();
166+
compare("limit = -1", hello, IOUtils.toByteArray(bounded, helloLen));
167+
bounded.mark(helloWorldLen);
168+
compare("limit = -1", world, IOUtils.toByteArray(bounded));
169+
bounded.reset();
170+
compare("limit = -1", world, IOUtils.toByteArray(bounded));
171+
// should be invariant
172+
assertTrue(bounded.markSupported());
173+
}
174+
// limit = 0
175+
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
176+
assertTrue(bounded.markSupported());
177+
bounded.mark(0);
178+
compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
179+
// should be invariant
180+
assertTrue(bounded.markSupported());
181+
// again
182+
bounded.reset();
183+
compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
184+
bounded.mark(helloWorldLen);
185+
compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
186+
// should be invariant
187+
assertTrue(bounded.markSupported());
188+
}
189+
// limit = length
190+
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
191+
.setMaxCount(helloWorld.length).get()) {
192+
assertTrue(bounded.markSupported());
193+
bounded.mark(0);
194+
compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
195+
// should be invariant
196+
assertTrue(bounded.markSupported());
197+
// again
198+
bounded.reset();
199+
compare("limit = length", hello, IOUtils.toByteArray(bounded, helloLen));
200+
bounded.mark(helloWorldLen);
201+
compare("limit = length", world, IOUtils.toByteArray(bounded));
202+
bounded.reset();
203+
compare("limit = length", world, IOUtils.toByteArray(bounded));
204+
// should be invariant
205+
assertTrue(bounded.markSupported());
206+
}
207+
// limit > length
208+
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
209+
.setMaxCount(helloWorld.length + 1).get()) {
210+
assertTrue(bounded.markSupported());
211+
bounded.mark(0);
212+
compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
213+
// should be invariant
214+
assertTrue(bounded.markSupported());
215+
// again
216+
bounded.reset();
217+
compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
218+
bounded.reset();
219+
compare("limit > length", hello, IOUtils.toByteArray(bounded, helloLen));
220+
bounded.mark(helloWorldLen);
221+
compare("limit > length", world, IOUtils.toByteArray(bounded));
222+
bounded.reset();
223+
compare("limit > length", world, IOUtils.toByteArray(bounded));
224+
// should be invariant
225+
assertTrue(bounded.markSupported());
226+
}
227+
// limit < length
228+
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
229+
.setMaxCount(helloWorld.length - (hello.length + 1)).get()) {
230+
assertTrue(bounded.markSupported());
231+
bounded.mark(0);
232+
compare("limit < length", hello, IOUtils.toByteArray(bounded));
233+
// should be invariant
234+
assertTrue(bounded.markSupported());
235+
// again
236+
bounded.reset();
237+
compare("limit < length", hello, IOUtils.toByteArray(bounded));
238+
//
239+
bounded.reset();
240+
compare("limit < length", hello, IOUtils.toByteArray(bounded, helloLen));
241+
bounded.mark(helloWorldLen);
242+
compare("limit < length", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
243+
bounded.reset();
244+
compare("limit < length", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
245+
246+
// should be invariant
247+
assertTrue(bounded.markSupported());
248+
}
249+
}
250+
147251
@SuppressWarnings("deprecation")
148252
@Test
149253
public void testOnMaxLength() throws Exception {
@@ -326,35 +430,70 @@ public void testReadSingle() throws Exception {
326430
public void testReset() throws Exception {
327431
final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
328432
final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
433+
// limit = -1
329434
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
330435
assertTrue(bounded.markSupported());
436+
bounded.reset();
437+
compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
438+
// should be invariant
439+
assertTrue(bounded.markSupported());
440+
// again
441+
bounded.reset();
331442
compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
332443
// should be invariant
333444
assertTrue(bounded.markSupported());
334445
}
446+
// limit = 0
335447
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
336448
assertTrue(bounded.markSupported());
449+
bounded.reset();
450+
compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
451+
// should be invariant
452+
assertTrue(bounded.markSupported());
453+
// again
454+
bounded.reset();
337455
compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
338456
// should be invariant
339457
assertTrue(bounded.markSupported());
340458
}
459+
// limit = length
341460
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
342461
.setMaxCount(helloWorld.length).get()) {
343462
assertTrue(bounded.markSupported());
463+
bounded.reset();
464+
compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
465+
// should be invariant
466+
assertTrue(bounded.markSupported());
467+
// again
468+
bounded.reset();
344469
compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
345470
// should be invariant
346471
assertTrue(bounded.markSupported());
347472
}
473+
// limit > length
348474
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
349475
.setMaxCount(helloWorld.length + 1).get()) {
350476
assertTrue(bounded.markSupported());
477+
bounded.reset();
478+
compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
479+
// should be invariant
480+
assertTrue(bounded.markSupported());
481+
// again
482+
bounded.reset();
351483
compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
352484
// should be invariant
353485
assertTrue(bounded.markSupported());
354486
}
487+
// limit < length
355488
try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
356489
.setMaxCount(helloWorld.length - 6).get()) {
357490
assertTrue(bounded.markSupported());
491+
bounded.reset();
492+
compare("limit < length", hello, IOUtils.toByteArray(bounded));
493+
// should be invariant
494+
assertTrue(bounded.markSupported());
495+
// again
496+
bounded.reset();
358497
compare("limit < length", hello, IOUtils.toByteArray(bounded));
359498
// should be invariant
360499
assertTrue(bounded.markSupported());

0 commit comments

Comments
 (0)