Skip to content

Commit 7d64e7c

Browse files
authored
SeekableInMemoryByteChannel.position(long) shouldn't throw an IllegalArgumentException for a new positive position that's too large (#756)
* SeekableInMemoryByteChannel.position(long) shouldn't throw an IllegalArgumentException for a new positive position that's too large * SeekableInMemoryByteChannel position and truncate now follow SeekableByteChannel and WritableByteChannel Javadoc better * Add comments and refactor local variable
1 parent b83da24 commit 7d64e7c

File tree

2 files changed

+46
-20
lines changed

2 files changed

+46
-20
lines changed

src/main/java/org/apache/commons/compress/utils/SeekableInMemoryByteChannel.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public class SeekableInMemoryByteChannel implements SeekableByteChannel {
4747
private static final int NAIVE_RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
4848
private byte[] data;
4949
private final AtomicBoolean closed = new AtomicBoolean();
50-
private int position;
50+
private long position;
5151
private int size;
5252

5353
/**
@@ -113,25 +113,28 @@ public long position() throws ClosedChannelException {
113113
@Override
114114
public SeekableByteChannel position(final long newPosition) throws IOException {
115115
ensureOpen();
116-
if (newPosition < 0L || newPosition > Integer.MAX_VALUE) {
117-
throw new IllegalArgumentException(String.format("Position must be in range [0..%,d]: %,d", Integer.MAX_VALUE, newPosition));
116+
if (newPosition < 0L) {
117+
throw new IllegalArgumentException(String.format("New position is negative: %,d", newPosition));
118118
}
119-
position = (int) newPosition;
119+
position = newPosition;
120120
return this;
121121
}
122122

123123
@Override
124124
public int read(final ByteBuffer buf) throws IOException {
125125
ensureOpen();
126+
if (position > Integer.MAX_VALUE) {
127+
return -1;
128+
}
126129
int wanted = buf.remaining();
127-
final int possible = size - position;
130+
final int possible = size - (int) position;
128131
if (possible <= 0) {
129132
return -1;
130133
}
131134
if (wanted > possible) {
132135
wanted = possible;
133136
}
134-
buf.put(data, position, wanted);
137+
buf.put(data, (int) position, wanted);
135138
position += wanted;
136139
return wanted;
137140
}
@@ -160,36 +163,42 @@ public long size() throws ClosedChannelException {
160163
@Override
161164
public SeekableByteChannel truncate(final long newSize) throws ClosedChannelException {
162165
ensureOpen();
163-
if (newSize < 0L || newSize > Integer.MAX_VALUE) {
164-
throw new IllegalArgumentException("Size must be range [0.." + Integer.MAX_VALUE + "]");
166+
if (newSize < 0L) {
167+
throw new IllegalArgumentException(String.format("New size is negative: %,d", newSize));
165168
}
166169
if (size > newSize) {
167170
size = (int) newSize;
168171
}
169172
if (position > newSize) {
170-
position = (int) newSize;
173+
position = newSize;
171174
}
172175
return this;
173176
}
174177

175178
@Override
176179
public int write(final ByteBuffer b) throws IOException {
177180
ensureOpen();
181+
if (position > Integer.MAX_VALUE) {
182+
throw new IOException("position > Integer.MAX_VALUE");
183+
}
178184
int wanted = b.remaining();
179-
final int possibleWithoutResize = size - position;
185+
// intPos <= Integer.MAX_VALUE
186+
int intPos = (int) position;
187+
final int possibleWithoutResize = size - intPos;
180188
if (wanted > possibleWithoutResize) {
181-
final int newSize = position + wanted;
189+
final int newSize = intPos + wanted;
182190
if (newSize < 0) { // overflow
183191
resize(Integer.MAX_VALUE);
184-
wanted = Integer.MAX_VALUE - position;
192+
wanted = Integer.MAX_VALUE - intPos;
185193
} else {
186194
resize(newSize);
187195
}
188196
}
189-
b.get(data, position, wanted);
190-
position += wanted;
191-
if (size < position) {
192-
size = position;
197+
b.get(data, intPos, wanted);
198+
// intPos + wanted is at most (Integer.MAX_VALUE - intPos) + intPos
199+
position = intPos += wanted;
200+
if (size < intPos) {
201+
size = intPos;
193202
}
194203
return wanted;
195204
}

src/test/java/org/apache/commons/compress/utils/SeekableInMemoryByteChannelTest.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,33 @@ void testShouldThrowExceptionOnWritingToClosedChannel() {
152152
}
153153

154154
@Test
155-
void testShouldThrowExceptionWhenSettingIncorrectPosition() {
155+
void testShouldThrowWhenSettingIncorrectPosition() throws IOException {
156156
try (SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel()) {
157-
assertThrows(IllegalArgumentException.class, () -> c.position(Integer.MAX_VALUE + 1L));
157+
final ByteBuffer buffer = ByteBuffer.allocate(1);
158+
c.position(c.size() + 1);
159+
assertEquals(c.size() + 1, c.position());
160+
assertEquals(-1, c.read(buffer));
161+
c.position(Integer.MAX_VALUE + 1L);
162+
assertEquals(Integer.MAX_VALUE + 1L, c.position());
163+
assertEquals(-1, c.read(buffer));
164+
assertThrows(IOException.class, () -> c.write(buffer));
165+
assertThrows(IllegalArgumentException.class, () -> c.position(-1));
166+
assertThrows(IllegalArgumentException.class, () -> c.position(Integer.MIN_VALUE));
167+
assertThrows(IllegalArgumentException.class, () -> c.position(Long.MIN_VALUE));
158168
}
159169
}
160170

161171
@Test
162-
void testShouldThrowExceptionWhenTruncatingToIncorrectSize() {
172+
void testShouldThrowWhenTruncatingToIncorrectSize() throws IOException {
163173
try (SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel()) {
164-
assertThrows(IllegalArgumentException.class, () -> c.truncate(Integer.MAX_VALUE + 1L));
174+
final ByteBuffer buffer = ByteBuffer.allocate(1);
175+
c.truncate(c.size() + 1);
176+
assertEquals(1, c.read(buffer));
177+
c.truncate(Integer.MAX_VALUE + 1L);
178+
assertEquals(0, c.read(buffer));
179+
assertThrows(IllegalArgumentException.class, () -> c.truncate(-1));
180+
assertThrows(IllegalArgumentException.class, () -> c.truncate(Integer.MIN_VALUE));
181+
assertThrows(IllegalArgumentException.class, () -> c.truncate(Long.MIN_VALUE));
165182
}
166183
}
167184

0 commit comments

Comments
 (0)