Repository: netty/netty
Issue: Optimize ByteBuf implementations
Labels: improvement

Our ByteBuf implementations have some great potential for improvements by reduce the sub-classing etc of them and remove abstract methods and so on. 

This allows for better inlining. At the moment even if we are able to inline such an abstract method as in AbstractByteBuf offered we have the overhead of typechecks on each call. As ByteBuf is one of the "key" players we should make sure it is as fast as possible.

This also includes to optimize methods for common pattern so they are small enough for inline.


Issue comments:
Another issue is the hierarchy. We have a too many implementations to allow for optimal inlining. Seems like the JIT can handle classes with 2 impls the best (when it comes to more then one impl). See also: http://www.cliffc.org/blog/2007/11/06/clone-or-not-clone/

Let me close this...


Pull request review comments:
`{@link CharSequence}`

`{@link CharSequence}`

cruft or continuation from the previous line?

![INFO](https://raw.githubusercontent.com/SonarCommunity/sonar-github/master/images/severity-info.png) Complete the task associated to this TODO comment. [![rule](https://raw.githubusercontent.com/SonarCommunity/sonar-github/master/images/rule.png)](https://garage.netty.io/sonarqube/coding_rules#rule_key=squid%3AS1135)

consider using [utf8MaxBytes](https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java#L451) instead.

it seems like the majority of the time the encoding will be `utf8` or `ascii`. what about adding methods to handle these cases on the `ByteBuf` interface? This could save the conditional checks every time and we could call these method here too.

``` java
public abstract int setUtf8(int index, CharSequence seq);
public abstract int setAscii(int index, CharSequence seq);
public abstract int setCharSequence(int index, CharSequence sequence, Charset charset);
...
```

update this comment as we no longer update the writerIndex here.

@Scottmitch I kind of not like to add these method as this make the interface even broader :(

will do

Later :)

Can we also check for `ISO_8859_1` here?
@fenik17 PRs welcome
@normanmaurer done: #6882

Patch that resolved the issue:
diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java
index e8dce970031..18281b90a7b 100644
--- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java
@@ -16,6 +16,7 @@
 package io.netty.buffer;
 
 import io.netty.util.ByteProcessor;
+import io.netty.util.CharsetUtil;
 import io.netty.util.IllegalReferenceCountException;
 import io.netty.util.ResourceLeakDetector;
 import io.netty.util.internal.PlatformDependent;
@@ -483,6 +484,19 @@ public ByteBuf getBytes(int index, ByteBuf dst, int length) {
         return this;
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        // TODO: We could optimize this for UTF8 and US_ASCII
+        return toString(index, length, charset);
+    }
+
+    @Override
+    public CharSequence readCharSequence(int length, Charset charset) {
+        CharSequence sequence = getCharSequence(readerIndex, length, charset);
+        readerIndex += length;
+        return sequence;
+    }
+
     @Override
     public ByteBuf setByte(int index, int value) {
         checkIndex(index);
@@ -649,6 +663,23 @@ public ByteBuf setZero(int index, int length) {
         return this;
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        if (charset.equals(CharsetUtil.UTF_8)) {
+            ensureWritable(ByteBufUtil.utf8MaxBytes(sequence));
+            return ByteBufUtil.writeUtf8(this, index, sequence, sequence.length());
+        }
+        if (charset.equals(CharsetUtil.US_ASCII)) {
+            int len = sequence.length();
+            ensureWritable(len);
+            return ByteBufUtil.writeAscii(this, index, sequence, len);
+        }
+        byte[] bytes = sequence.toString().getBytes(charset);
+        ensureWritable(bytes.length);
+        setBytes(index, bytes);
+        return bytes.length;
+    }
+
     @Override
     public byte readByte() {
         checkReadableBytes0(1);
@@ -1111,6 +1142,13 @@ public ByteBuf writeZero(int length) {
         return this;
     }
 
+    @Override
+    public int writeCharSequence(CharSequence sequence, Charset charset) {
+        int written = setCharSequence(writerIndex, sequence, charset);
+        writerIndex += written;
+        return written;
+    }
+
     @Override
     public ByteBuf copy() {
         return copy(readerIndex, readableBytes());
diff --git a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java
index ea11606fbf9..f4858933202 100644
--- a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java
@@ -244,6 +244,12 @@ public int getBytes(int index, GatheringByteChannel out, int length) throws IOEx
         return super.getBytes(index, out, length);
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.getCharSequence(index, length, charset);
+    }
+
     @Override
     public ByteBuf setBoolean(int index, boolean value) {
         recordLeakNonRefCountingOperation(leak);
@@ -352,6 +358,12 @@ public ByteBuf setZero(int index, int length) {
         return super.setZero(index, length);
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.setCharSequence(index, sequence, charset);
+    }
+
     @Override
     public boolean readBoolean() {
         recordLeakNonRefCountingOperation(leak);
@@ -484,6 +496,12 @@ public int readBytes(GatheringByteChannel out, int length) throws IOException {
         return super.readBytes(out, length);
     }
 
+    @Override
+    public CharSequence readCharSequence(int length, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.readCharSequence(length, charset);
+    }
+
     @Override
     public ByteBuf skipBytes(int length) {
         recordLeakNonRefCountingOperation(leak);
@@ -844,6 +862,12 @@ public ByteBuf writeLongLE(long value) {
         return super.writeLongLE(value);
     }
 
+    @Override
+    public int writeCharSequence(CharSequence sequence, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.writeCharSequence(sequence, charset);
+    }
+
     @Override
     public int getBytes(int index, FileChannel out, long position, int length) throws IOException {
         recordLeakNonRefCountingOperation(leak);
diff --git a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBuf.java
index 70777ae70f3..87be9e6e679 100644
--- a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBuf.java
@@ -226,6 +226,12 @@ public int getBytes(int index, GatheringByteChannel out, int length) throws IOEx
         return super.getBytes(index, out, length);
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.getCharSequence(index, length, charset);
+    }
+
     @Override
     public CompositeByteBuf setBoolean(int index, boolean value) {
         recordLeakNonRefCountingOperation(leak);
@@ -466,6 +472,12 @@ public int readBytes(GatheringByteChannel out, int length) throws IOException {
         return super.readBytes(out, length);
     }
 
+    @Override
+    public CharSequence readCharSequence(int length, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.readCharSequence(length, charset);
+    }
+
     @Override
     public CompositeByteBuf skipBytes(int length) {
         recordLeakNonRefCountingOperation(leak);
@@ -580,6 +592,12 @@ public CompositeByteBuf writeZero(int length) {
         return super.writeZero(length);
     }
 
+    @Override
+    public int writeCharSequence(CharSequence sequence, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.writeCharSequence(sequence, charset);
+    }
+
     @Override
     public int indexOf(int fromIndex, int toIndex, byte value) {
         recordLeakNonRefCountingOperation(leak);
@@ -760,6 +778,12 @@ public ByteBuf setLongLE(int index, long value) {
         return super.setLongLE(index, value);
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        recordLeakNonRefCountingOperation(leak);
+        return super.setCharSequence(index, sequence, charset);
+    }
+
     @Override
     public short readShortLE() {
         recordLeakNonRefCountingOperation(leak);
diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java
index b38abda2a62..91d1acfae30 100644
--- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java
@@ -913,6 +913,17 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
      */
     public abstract int getBytes(int index, FileChannel out, long position, int length) throws IOException;
 
+    /**
+     * Gets a {@link CharSequence} with the given length at the given index.
+     *
+     * @param length the length to read
+     * @param charset that should be used
+     * @return the sequence
+     * @throws IndexOutOfBoundsException
+     *         if {@code length} is greater than {@code this.readableBytes}
+     */
+    public abstract CharSequence getCharSequence(int index, int length, Charset charset);
+
     /**
      * Sets the specified boolean at the specified absolute {@code index} in this
      * buffer.
@@ -1247,6 +1258,19 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
      */
     public abstract ByteBuf setZero(int index, int length);
 
+    /**
+     * Writes the specified {@link CharSequence} at the current {@code writerIndex} and increases
+     * the {@code writerIndex} by the written bytes.
+     *
+     * @param index on which the sequence should be written
+     * @param sequence to write
+     * @param charset that should be used.
+     * @return the written number of bytes.
+     * @throws IndexOutOfBoundsException
+     *         if {@code this.writableBytes} is not large enough to write the whole sequence
+     */
+    public abstract int setCharSequence(int index, CharSequence sequence, Charset charset);
+
     /**
      * Gets a boolean at the current {@code readerIndex} and increases
      * the {@code readerIndex} by {@code 1} in this buffer.
@@ -1579,6 +1603,18 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
      */
     public abstract int readBytes(GatheringByteChannel out, int length) throws IOException;
 
+    /**
+     * Gets a {@link CharSequence} with the given length at the current {@code readerIndex}
+     * and increases the {@code readerIndex} by the given length.
+     *
+     * @param length the length to read
+     * @param charset that should be used
+     * @return the sequence
+     * @throws IndexOutOfBoundsException
+     *         if {@code length} is greater than {@code this.readableBytes}
+     */
+    public abstract CharSequence readCharSequence(int length, Charset charset);
+
     /**
      * Transfers this buffer's data starting at the current {@code readerIndex}
      * to the specified channel starting at the given file position.
@@ -1885,6 +1921,19 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
      */
     public abstract ByteBuf writeZero(int length);
 
+    /**
+     * Writes the specified {@link CharSequence} at the current {@code writerIndex} and increases
+     * the {@code writerIndex} by the written bytes.
+     * in this buffer.
+     *
+     * @param sequence to write
+     * @param charset that should be used
+     * @return the written number of bytes
+     * @throws IndexOutOfBoundsException
+     *         if {@code this.writableBytes} is not large enough to write the whole sequence
+     */
+    public abstract int writeCharSequence(CharSequence sequence, Charset charset);
+
     /**
      * Locates the first occurrence of the specified {@code value} in this
      * buffer.  The search takes place from the specified {@code fromIndex}
diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
index 9588758ca23..9ad62240ee4 100644
--- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
+++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
@@ -382,7 +382,10 @@ public static int writeUtf8(ByteBuf buf, CharSequence seq) {
 
         for (;;) {
             if (buf instanceof AbstractByteBuf) {
-                return writeUtf8((AbstractByteBuf) buf, seq, len);
+                AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
+                int written = writeUtf8(byteBuf, byteBuf.writerIndex, seq, len);
+                byteBuf.writerIndex += written;
+                return written;
             } else if (buf instanceof WrappedByteBuf) {
                 // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
                 buf = buf.unwrap();
@@ -395,9 +398,8 @@ public static int writeUtf8(ByteBuf buf, CharSequence seq) {
     }
 
     // Fast-Path implementation
-    private static int writeUtf8(AbstractByteBuf buffer, CharSequence seq, int len) {
-        int oldWriterIndex = buffer.writerIndex;
-        int writerIndex = oldWriterIndex;
+    static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
+        int oldWriterIndex = writerIndex;
 
         // We can use the _set methods as these not need to do any index checks and reference checks.
         // This is possible as we called ensureWritable(...) before.
@@ -440,8 +442,6 @@ private static int writeUtf8(AbstractByteBuf buffer, CharSequence seq, int len)
                 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
             }
         }
-        // update the writerIndex without any extra checks for performance reasons
-        buffer.writerIndex = writerIndex;
         return writerIndex - oldWriterIndex;
     }
 
@@ -483,8 +483,10 @@ public static int writeAscii(ByteBuf buf, CharSequence seq) {
         } else {
             for (;;) {
                 if (buf instanceof AbstractByteBuf) {
-                    writeAscii((AbstractByteBuf) buf, seq, len);
-                    break;
+                    AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
+                    int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len);
+                    byteBuf.writerIndex += written;
+                    return written;
                 } else if (buf instanceof WrappedByteBuf) {
                     // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
                     buf = buf.unwrap();
@@ -497,16 +499,14 @@ public static int writeAscii(ByteBuf buf, CharSequence seq) {
     }
 
     // Fast-Path implementation
-    private static void writeAscii(AbstractByteBuf buffer, CharSequence seq, int len) {
-        int writerIndex = buffer.writerIndex;
+    static int writeAscii(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
 
         // We can use the _set methods as these not need to do any index checks and reference checks.
         // This is possible as we called ensureWritable(...) before.
         for (int i = 0; i < len; i++) {
             buffer._setByte(writerIndex++, (byte) seq.charAt(i));
         }
-        // update the writerIndex without any extra checks for performance reasons
-        buffer.writerIndex = writerIndex;
+        return len;
     }
 
     /**
diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java
index 52cfedddac3..674e9e56ae0 100644
--- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java
@@ -391,6 +391,12 @@ public int getBytes(int index, FileChannel out, long position, int length) {
         return 0;
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        checkIndex(index, length);
+        return null;
+    }
+
     @Override
     public ByteBuf setBoolean(int index, boolean value) {
         throw new IndexOutOfBoundsException();
@@ -509,6 +515,11 @@ public ByteBuf setZero(int index, int length) {
         return checkIndex(index, length);
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        throw new IndexOutOfBoundsException();
+    }
+
     @Override
     public boolean readBoolean() {
         throw new IndexOutOfBoundsException();
@@ -666,6 +677,12 @@ public int readBytes(FileChannel out, long position, int length) {
         return 0;
     }
 
+    @Override
+    public CharSequence readCharSequence(int length, Charset charset) {
+        checkLength(length);
+        return null;
+    }
+
     @Override
     public ByteBuf skipBytes(int length) {
         return checkLength(length);
@@ -789,6 +806,11 @@ public ByteBuf writeZero(int length) {
         return checkLength(length);
     }
 
+    @Override
+    public int writeCharSequence(CharSequence sequence, Charset charset) {
+        throw new IndexOutOfBoundsException();
+    }
+
     @Override
     public int indexOf(int fromIndex, int toIndex, byte value) {
         checkIndex(fromIndex);
diff --git a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java
index a10ae864a21..64c48e2123b 100644
--- a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java
@@ -16,6 +16,7 @@
 package io.netty.buffer;
 
 import io.netty.util.ByteProcessor;
+import io.netty.util.CharsetUtil;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -25,6 +26,7 @@
 import java.nio.channels.FileChannel;
 import java.nio.channels.GatheringByteChannel;
 import java.nio.channels.ScatteringByteChannel;
+import java.nio.charset.Charset;
 
 /**
  * A derived buffer which exposes its parent's sub-region only.  It is
@@ -264,6 +266,12 @@ public ByteBuf setByte(int index, int value) {
         return this;
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        checkIndex0(index, length);
+        return buffer.getCharSequence(idx(index), length, charset);
+    }
+
     @Override
     protected void _setByte(int index, int value) {
         unwrap().setByte(idx(index), value);
@@ -386,6 +394,23 @@ public ByteBuf setBytes(int index, ByteBuffer src) {
         return this;
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        if (charset.equals(CharsetUtil.UTF_8)) {
+            checkIndex0(index, ByteBufUtil.utf8MaxBytes(sequence));
+            return ByteBufUtil.writeUtf8(this, idx(index), sequence, sequence.length());
+        }
+        if (charset.equals(CharsetUtil.US_ASCII)) {
+            int len = sequence.length();
+            checkIndex0(index, len);
+            return ByteBufUtil.writeAscii(this, idx(index), sequence, len);
+        }
+        byte[] bytes = sequence.toString().getBytes(charset);
+        checkIndex0(index, bytes.length);
+        buffer.setBytes(idx(index), bytes);
+        return bytes.length;
+    }
+
     @Override
     public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
         checkIndex0(index, length);
diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java
index 72fec39fca4..3e9b075d21c 100644
--- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java
@@ -376,6 +376,11 @@ public int getBytes(int index, FileChannel out, long position, int length) throw
         return buf.getBytes(index, out, position, length);
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        return buf.getCharSequence(index, length, charset);
+    }
+
     @Override
     public ByteBuf setBoolean(int index, boolean value) {
         buf.setBoolean(index, value);
@@ -511,6 +516,11 @@ public ByteBuf setZero(int index, int length) {
         return this;
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        return buf.setCharSequence(index, sequence, charset);
+    }
+
     @Override
     public boolean readBoolean() {
         return buf.readBoolean();
@@ -673,6 +683,11 @@ public int readBytes(FileChannel out, long position, int length) throws IOExcept
         return buf.readBytes(out, position, length);
     }
 
+    @Override
+    public CharSequence readCharSequence(int length, Charset charset) {
+        return buf.readCharSequence(length, charset);
+    }
+
     @Override
     public ByteBuf skipBytes(int length) {
         buf.skipBytes(length);
@@ -814,6 +829,11 @@ public ByteBuf writeZero(int length) {
         return this;
     }
 
+    @Override
+    public int writeCharSequence(CharSequence sequence, Charset charset) {
+        return buf.writeCharSequence(sequence, charset);
+    }
+
     @Override
     public int indexOf(int fromIndex, int toIndex, byte value) {
         return buf.indexOf(fromIndex, toIndex, value);
diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
index 954d5e6ff63..0d0bec85d7c 100644
--- a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
@@ -366,6 +366,11 @@ public int getBytes(int index, FileChannel out, long position, int length) throw
         return buf.getBytes(index, out, position, length);
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        return buf.getCharSequence(index, length, charset);
+    }
+
     @Override
     public ByteBuf setBoolean(int index, boolean value) {
         buf.setBoolean(index, value);
@@ -501,6 +506,11 @@ public ByteBuf setZero(int index, int length) {
         return this;
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        return buf.setCharSequence(index, sequence, charset);
+    }
+
     @Override
     public boolean readBoolean() {
         return buf.readBoolean();
@@ -663,6 +673,11 @@ public int readBytes(FileChannel out, long position, int length) throws IOExcept
         return buf.readBytes(out, position, length);
     }
 
+    @Override
+    public CharSequence readCharSequence(int length, Charset charset) {
+        return buf.readCharSequence(length, charset);
+    }
+
     @Override
     public ByteBuf skipBytes(int length) {
         buf.skipBytes(length);
@@ -804,6 +819,11 @@ public ByteBuf writeZero(int length) {
         return this;
     }
 
+    @Override
+    public int writeCharSequence(CharSequence sequence, Charset charset) {
+        return buf.writeCharSequence(sequence, charset);
+    }
+
     @Override
     public int indexOf(int fromIndex, int toIndex, byte value) {
         return buf.indexOf(fromIndex, toIndex, value);
diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java
index e533fcc2b9f..a9c69a924bc 100644
--- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java
+++ b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java
@@ -359,6 +359,12 @@ public double getDouble(int index) {
         return buffer.getDouble(index);
     }
 
+    @Override
+    public CharSequence getCharSequence(int index, int length, Charset charset) {
+        checkIndex(index, length);
+        return buffer.getCharSequence(index, length, charset);
+    }
+
     @Override
     public int hashCode() {
         reject();
@@ -712,6 +718,12 @@ public double readDouble() {
         return buffer.readDouble();
     }
 
+    @Override
+    public CharSequence readCharSequence(int length, Charset charset) {
+        checkReadableBytes(length);
+        return buffer.readCharSequence(length, charset);
+    }
+
     @Override
     public ByteBuf resetReaderIndex() {
         buffer.resetReaderIndex();
@@ -1114,6 +1126,18 @@ public ByteBuf writeDouble(double value) {
         return this;
     }
 
+    @Override
+    public int setCharSequence(int index, CharSequence sequence, Charset charset) {
+        reject();
+        return -1;
+    }
+
+    @Override
+    public int writeCharSequence(CharSequence sequence, Charset charset) {
+        reject();
+        return -1;
+    }
+
     private void checkIndex(int index, int length) {
         if (index + length > buffer.writerIndex()) {
             throw REPLAY;


Test patch: