Repository: eclipse-vertx/vert.x

Problem statement:
Optimize heap buffers
Heap `Buffer` created with `Unpooled.buffer(...)` are instrumented and maintain a reference counting. This reference counting requires to wrap the current `ByteBuf` duplicate with an unreleasable wrapper so that the returned duplicate will not affect the actual `ByteBuf` hold by `BufferImpl`.

We can optimize these buffers by using instead custom subclasses of `UnpooledHeapByteBuf` and `UnpooledUnsafeHeapByteBuf` instead to avoid un-necessary operations. Such classes will be un-instrumented and reference counting methods can be overridden to disable reference counting which avoids to wrap the `ByteBuf` duplicate with an unreleasable wrapper.



Hints:

Patch:
diff --git a/src/main/java/io/vertx/core/buffer/impl/BufferImpl.java b/src/main/java/io/vertx/core/buffer/impl/BufferImpl.java
--- a/src/main/java/io/vertx/core/buffer/impl/BufferImpl.java
+++ b/src/main/java/io/vertx/core/buffer/impl/BufferImpl.java
@@ -72,11 +72,11 @@ public BufferImpl() {
   }
 
   BufferImpl(int initialSizeHint) {
-    buffer = Unpooled.buffer(initialSizeHint, Integer.MAX_VALUE);
+    buffer = VertxByteBufAllocator.DEFAULT.heapBuffer(initialSizeHint, Integer.MAX_VALUE);
   }
 
   BufferImpl(byte[] bytes) {
-    buffer = Unpooled.buffer(bytes.length, Integer.MAX_VALUE).writeBytes(bytes);
+    buffer = VertxByteBufAllocator.DEFAULT.heapBuffer(bytes.length, Integer.MAX_VALUE).writeBytes(bytes);
   }
 
   BufferImpl(String str, String enc) {
@@ -507,8 +507,19 @@ public Buffer slice(int start, int end) {
     return new BufferImpl(buffer.slice(start, end - start));
   }
 
+  /**
+   * @return the buffer as is
+   */
+  public ByteBuf byteBuf() {
+    return buffer;
+  }
+
   public ByteBuf getByteBuf() {
-    return Unpooled.unreleasableBuffer(buffer.duplicate());
+    ByteBuf duplicate = buffer.duplicate();
+    if (buffer.getClass() != VertxHeapByteBuf.class && buffer.getClass() != VertxUnsafeHeapByteBuf.class) {
+      duplicate = Unpooled.unreleasableBuffer(duplicate);
+    }
+    return duplicate;
   }
 
   private Buffer append(String str, Charset charset) {
diff --git a/src/main/java/io/vertx/core/buffer/impl/VertxByteBufAllocator.java b/src/main/java/io/vertx/core/buffer/impl/VertxByteBufAllocator.java
new file mode 100644
--- /dev/null
+++ b/src/main/java/io/vertx/core/buffer/impl/VertxByteBufAllocator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+package io.vertx.core.buffer.impl;
+
+import io.netty.buffer.AbstractByteBufAllocator;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.UnpooledByteBufAllocator;
+import io.netty.util.internal.PlatformDependent;
+
+abstract class VertxByteBufAllocator extends AbstractByteBufAllocator {
+
+  private static VertxByteBufAllocator UNSAFE_IMPL = new VertxByteBufAllocator() {
+    @Override
+    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
+      return new VertxUnsafeHeapByteBuf(this, initialCapacity, maxCapacity);
+    }
+  };
+
+  private static VertxByteBufAllocator IMPL = new VertxByteBufAllocator() {
+    @Override
+    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
+      return new VertxHeapByteBuf(this, initialCapacity, maxCapacity);
+    }
+  };
+
+  static final VertxByteBufAllocator DEFAULT = PlatformDependent.hasUnsafe() ? UNSAFE_IMPL : IMPL;
+
+  @Override
+  protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
+    return UnpooledByteBufAllocator.DEFAULT.directBuffer(initialCapacity, maxCapacity);
+  }
+
+  @Override
+  public boolean isDirectBufferPooled() {
+    return false;
+  }
+}
diff --git a/src/main/java/io/vertx/core/buffer/impl/VertxHeapByteBuf.java b/src/main/java/io/vertx/core/buffer/impl/VertxHeapByteBuf.java
new file mode 100644
--- /dev/null
+++ b/src/main/java/io/vertx/core/buffer/impl/VertxHeapByteBuf.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+package io.vertx.core.buffer.impl;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.UnpooledHeapByteBuf;
+import io.netty.buffer.UnpooledUnsafeHeapByteBuf;
+
+/**
+ * An un-releasable, un-pooled, un-instrumented heap {@code ByteBuf}.
+ */
+final class VertxHeapByteBuf extends UnpooledHeapByteBuf {
+
+  public VertxHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
+    super(alloc, initialCapacity, maxCapacity);
+  }
+
+  @Override
+  public ByteBuf retain(int increment) {
+    return this;
+  }
+
+  @Override
+  public ByteBuf retain() {
+    return this;
+  }
+
+  @Override
+  public ByteBuf touch() {
+    return this;
+  }
+
+  @Override
+  public ByteBuf touch(Object hint) {
+    return this;
+  }
+
+  @Override
+  public boolean release() {
+    return false;
+  }
+
+  @Override
+  public boolean release(int decrement) {
+    return false;
+  }
+}
diff --git a/src/main/java/io/vertx/core/buffer/impl/VertxUnsafeHeapByteBuf.java b/src/main/java/io/vertx/core/buffer/impl/VertxUnsafeHeapByteBuf.java
new file mode 100644
--- /dev/null
+++ b/src/main/java/io/vertx/core/buffer/impl/VertxUnsafeHeapByteBuf.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+package io.vertx.core.buffer.impl;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.UnpooledUnsafeHeapByteBuf;
+
+/**
+ * An un-releasable, un-pooled, un-instrumented, un-safe heap {@code ByteBuf}.
+ */
+final class VertxUnsafeHeapByteBuf extends UnpooledUnsafeHeapByteBuf {
+
+  public VertxUnsafeHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
+    super(alloc, initialCapacity, maxCapacity);
+  }
+
+  @Override
+  public ByteBuf retain(int increment) {
+    return this;
+  }
+
+  @Override
+  public ByteBuf retain() {
+    return this;
+  }
+
+  @Override
+  public ByteBuf touch() {
+    return this;
+  }
+
+  @Override
+  public ByteBuf touch(Object hint) {
+    return this;
+  }
+
+  @Override
+  public boolean release() {
+    return false;
+  }
+
+  @Override
+  public boolean release(int decrement) {
+    return false;
+  }
+}


Test patch:
diff --git a/src/test/java/io/vertx/core/buffer/impl/VertxBufferTest.java b/src/test/java/io/vertx/core/buffer/impl/VertxBufferTest.java
new file mode 100644
--- /dev/null
+++ b/src/test/java/io/vertx/core/buffer/impl/VertxBufferTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+
+package io.vertx.core.buffer.impl;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.util.IllegalReferenceCountException;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.json.DecodeException;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import io.vertx.test.core.TestUtils;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.function.Function;
+
+import static io.vertx.test.core.TestUtils.assertIllegalArgumentException;
+import static io.vertx.test.core.TestUtils.assertIndexOutOfBoundsException;
+import static io.vertx.test.core.TestUtils.assertNullPointerException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class VertxBufferTest {
+
+  @Test
+  public void testAllocateVertxBuffer() {
+    BufferImpl buffer = new BufferImpl();
+    ByteBuf byteBuf = buffer.byteBuf();
+    assertTrue(byteBuf instanceof VertxHeapByteBuf || byteBuf instanceof VertxUnsafeHeapByteBuf);
+  }
+
+  @Test
+  public void testUnreleasable() {
+    BufferImpl buffer = new BufferImpl();
+    ByteBuf byteBuf = buffer.byteBuf();
+    assertEquals(1, byteBuf.refCnt());
+    byteBuf.release();
+    assertEquals(1, byteBuf.refCnt());
+  }
+
+  @Test
+  public void testDuplicate() {
+    BufferImpl buffer = new BufferImpl();
+    buffer.appendString("Hello World");
+    ByteBuf byteBuf = buffer.byteBuf();
+    ByteBuf duplicate = buffer.getByteBuf();
+    assertEquals(1, byteBuf.refCnt());
+    duplicate.release();
+    assertEquals(1, duplicate.refCnt());
+    assertEquals(1, byteBuf.refCnt());
+    duplicate.readerIndex(3);
+    assertEquals(3, duplicate.readerIndex());
+    assertEquals(0, byteBuf.readerIndex());
+    ByteBuf duplicateSlice = duplicate.slice(0, 5);
+    duplicateSlice.release();
+    assertEquals(1, duplicateSlice.refCnt());
+    assertEquals(1, duplicate.refCnt());
+    assertEquals(1, byteBuf.refCnt());
+    duplicateSlice.readerIndex(1);
+    assertEquals(1, duplicateSlice.readerIndex());
+    assertEquals(3, duplicate.readerIndex());
+    assertEquals(0, byteBuf.readerIndex());
+  }
+}