Repository: netty/netty
Issue: IndexOutOfBoundsException for CompositeByteBuf
Labels: defect

This code worked up to netty 4.0.32, but stopped in 4.0.33.Final

```
    ByteBufAllocator alloc = ByteBufAllocator.DEFAULT;
    CompositeByteBuf cBuff = alloc.compositeDirectBuffer();
    cBuff.addComponent(alloc.buffer(8).writeLong(0));
    cBuff.addComponent(alloc.buffer(8).writeLong(0));

    // work as expected
    System.err.println("str="+ cBuff.toString(0, 16, StandardCharsets.UTF_8));
    // fails
    System.err.println("str="+ cBuff.toString(3, 13, StandardCharsets.UTF_8));
```

Exception:

```
Exception in thread "main" java.lang.IndexOutOfBoundsException: index: 3, length: 13 (expected: range(0, 13))
at io.netty.buffer.AbstractByteBuf.checkIndex0(AbstractByteBuf.java:1142)
at io.netty.buffer.AbstractByteBuf.checkIndex(AbstractByteBuf.java:1137)
at io.netty.buffer.UnpooledHeapByteBuf.internalNioBuffer(UnpooledHeapByteBuf.java:280)
at io.netty.buffer.ByteBufUtil.decodeString(ByteBufUtil.java:558)
at io.netty.buffer.AbstractByteBuf.toString(AbstractByteBuf.java:978)
at Tester.main(Tester.java:24)
```


Issue comments:
will have a look

Fixed in #4681

@valodzka could you test with latest 4.0 branch: 

This test works for me:

```

    @Test
    public void testToStringComposite() {
        CompositeByteBuf cBuff = compositeBuffer();
        cBuff.addComponent(buffer(8).writeLong(0));
        cBuff.addComponent(buffer(8).writeLong(0));

        // work as expected
        System.err.println("str="+ cBuff.toString(0, 16, CharsetUtil.UTF_8));
        // fails
        System.err.println("str="+ cBuff.toString(3, 13, CharsetUtil.UTF_8));
    }
```

@valodzka doh! I did not notice @windie fixed it and @Scottmitch pulled it in. @windie @Scottmitch you rock!


Pull request review comments:
Can you use a more descriptive test name? something like `testToStringDoesNotThrowIndexOutOfBounds`


Patch that resolved the issue:
diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
index 1644ab3165f..44fc52eaa6e 100644
--- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
+++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
@@ -548,7 +548,7 @@ static String decodeString(ByteBuf src, int readerIndex, int len, Charset charse
             try {
                 buffer.writeBytes(src, readerIndex, len);
                 // Use internalNioBuffer(...) to reduce object creation.
-                decodeString(decoder, buffer.internalNioBuffer(readerIndex, len), dst);
+                decodeString(decoder, buffer.internalNioBuffer(0, len), dst);
             } finally {
                 // Release the temporary buffer again.
                 buffer.release();


Test patch:
diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java b/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java
index 0309db57ec3..f3bcebef184 100644
--- a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java
+++ b/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java
@@ -187,4 +187,17 @@ private static void testDecodeString(String text, Charset charset) {
         Assert.assertEquals(text, ByteBufUtil.decodeString(buffer, 0, buffer.readableBytes(), charset));
         buffer.release();
     }
+
+    @Test
+    public void testToStringDoesNotThrowIndexOutOfBounds() {
+        CompositeByteBuf buffer = Unpooled.compositeBuffer();
+        try {
+            byte[] bytes = "1234".getBytes(CharsetUtil.UTF_8);
+            buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes));
+            buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes));
+            Assert.assertEquals("1234", buffer.toString(bytes.length, bytes.length, CharsetUtil.UTF_8));
+        } finally {
+            buffer.release();
+        }
+    }
 }