[ 
https://issues.apache.org/jira/browse/COMPRESS-595?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17438698#comment-17438698
 ] 

NP commented on COMPRESS-595:
-----------------------------

Test case in Java syntax, closer to the other two test cases for readRange()
{code:java}
    @Test
    public void readRangeFromChannelHandlesPartialRead() throws IOException {
        byte[] bytes = {1, 2, 3, 4, 5};
        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        try (ReadableByteChannel in = new ReadableByteChannel() {
            @Override
            public int read(ByteBuffer dst) throws IOException {
                if (! byteBuffer.hasRemaining()) return -1;
                int wanted = dst.remaining();
                for (int bytesReadSoFar = 1; bytesReadSoFar <= wanted ; 
bytesReadSoFar++) {
                    byte currentByte = byteBuffer.get();
                    dst.put(currentByte);
                    if (currentByte == 2) return bytesReadSoFar; // simulate 
one partial read
                }
                return wanted;
            }

            @Override
            public boolean isOpen() {
                throw new UnsupportedOperationException("not called");
            }

            @Override
            public void close() throws IOException {
                throw new UnsupportedOperationException("not called");
            }
        }) {
            byte[] read = IOUtils.readRange(in, 3);
            Assert.assertEquals(2, byteBuffer.remaining());
            Assert.assertArrayEquals(new byte[] { 1, 2, 3 }, read);
            final ByteBuffer b = ByteBuffer.allocate(1);
            Assert.assertEquals(1, in.read(b));
            Assert.assertEquals(1, byteBuffer.remaining());
            Assert.assertArrayEquals(new byte[] { 4 }, b.array());
        }
    }
{code}

> IOUtils.readRange(ReadableByteChannel input, int len) reads more than len 
> when input.read() returns < len (ie. rewind() makes no sense)
> ---------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: COMPRESS-595
>                 URL: https://issues.apache.org/jira/browse/COMPRESS-595
>             Project: Commons Compress
>          Issue Type: Bug
>    Affects Versions: 1.21
>            Reporter: NP
>            Priority: Major
>         Attachments: IOUtilsTest.kt
>
>
> When `input.read(b)` returns `readNow` < `len`, then it means
> `input.read(b)` will need to be called again with the same
> buffer, whose `remaining()` is now the old `remaining()` - `readNow`.
> This way the ReadableByteChannel knows how many bytes are to be
> read in subsequent iterations of the `while (read < len)` loop.
> This is currently not the case, because there is a call to rewind()
> which results in a buffer whose remaining() is reset to `len` if
> `readNow` < `len`.
> I suspect the readRange() method has only been used with channels that never 
> do partial reads (such as File Channels), and hence the problem has not been 
> experienced until now.
> Here is a test case that exhibits the bug:
>  
> ```kotlin
> import org.apache.commons.compress.utils.IOUtils
> import org.junit.jupiter.api.Assertions.assertArrayEquals
> import org.junit.jupiter.api.Assertions.assertEquals
> import org.junit.jupiter.api.Test
> import java.nio.ByteBuffer
> import java.nio.channels.ReadableByteChannel
> class IOUtilsTest {
>     private class ReadableBytePerByteChannel: ReadableByteChannel {
>         val data = byteArrayOf(0x03, 0x02, 0x01)
>         val dataBuffer = ByteBuffer.wrap(data)!!
>         override fun close(): Unit = TODO("Not needed")
>         override fun isOpen(): Boolean = TODO("Not needed")
>         override fun read(dst: ByteBuffer): Int {
>             if (! dataBuffer.hasRemaining()) return -1
>             val bytesAvailableForReading = dataBuffer.remaining()
>             for (bytesReadSoFar in 1..bytesAvailableForReading) {
>                 val nextByte: Byte = dataBuffer.get()
>                 dst.put(nextByte)
>                 // first read() call reads 1 byte, less than requested(2)
>                 if (0x03.toByte() == nextByte) return bytesReadSoFar
>             }
>             return bytesAvailableForReading
>         }
>     }
>     @Test
>     fun readRangeShouldHandlePartialReads() {
>         val channel = ReadableBytePerByteChannel()
>         // should result in two calls to read()
>         val actual = IOUtils.readRange(channel, 2)
>         val expected = byteArrayOf(0x03, 0x02)
>         assertArrayEquals(expected, actual)
>         assertEquals(1, channel.dataBuffer.remaining())
>     }
> }
>  ```
>   
>  



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to