PROTON-1965: also apply optimisation when the compared span is within the current array
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBuffer.java b/proton-j/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBuffer.java
index 409b192..5310faf 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBuffer.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBuffer.java
@@ -103,6 +103,7 @@
         return currentArray != null && (contents == null || contents.size() == 1);
     }
 
+    @Override
     public int capacity() {
         return capacity;
     }
@@ -840,7 +841,14 @@
             return false;
         }
 
-        if (hasArray()) {
+        if (remaining == 0) {
+            // No content to compare, and we already checked 'remaining' is equal. Protects from NPE below.
+            return true;
+        }
+
+        if (hasArray() || remaining <= currentArray.length - currentOffset) {
+            // Either there is only one array, or the span to compare is within a single chunk of this buffer,
+            // allowing the compare to directly access the underlying array instead of using slower get methods.
             return equals(currentArray, currentOffset, remaining, buffer);
         } else {
             return equals(this, buffer);
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/codec/CompositeReadableBufferTest.java b/proton-j/src/test/java/org/apache/qpid/proton/codec/CompositeReadableBufferTest.java
index 5325f17..84c63f7 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/codec/CompositeReadableBufferTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/codec/CompositeReadableBufferTest.java
@@ -3696,6 +3696,68 @@
         assertEquals(0, buffer2.position());
     }
 
+    @Test
+    public void testEqualsWhenContentRemainingIsSubsetOfSingleChunkInMultiArrayBufferSame() {
+        CompositeReadableBuffer buffer1 = new CompositeReadableBuffer();
+        CompositeReadableBuffer buffer2 = new CompositeReadableBuffer();
+
+        byte[] data1 = new byte[] {-1, -1, 0, 1, 2, 3, 4, 5};
+        byte[] data2 = new byte[] {-1, -1, -1, 0, 1, 2, 3, 4, 5};
+
+        buffer1.append(data1);
+        buffer1.position(2);
+
+        buffer2.append(data2);
+        buffer2.position(3);
+
+        byte[] data3 = new byte[] { 5, 4, 3, 2, 1 };
+        buffer1.append(data3);
+        buffer2.append(data3);
+
+        buffer1.limit(data1.length);
+        buffer2.limit(data2.length);
+
+        assertEquals(6, buffer1.remaining());
+        assertEquals(6, buffer2.remaining());
+
+        assertEquals(buffer1, buffer2);
+        assertEquals(buffer2, buffer1);
+
+        assertEquals(2, buffer1.position());
+        assertEquals(3, buffer2.position());
+    }
+
+    @Test
+    public void testEqualsWhenContentRemainingIsSubsetOfSingleChunkInMultiArrayBufferNotSame() {
+        CompositeReadableBuffer buffer1 = new CompositeReadableBuffer();
+        CompositeReadableBuffer buffer2 = new CompositeReadableBuffer();
+
+        byte[] data1 = new byte[] {-1, -1, 0, 1, 2, 3, 4, 5};
+        byte[] data2 = new byte[] {-1, -1, -1, 0, 1, 2, 3, 4, -1};
+
+        buffer1.append(data1);
+        buffer1.position(2);
+
+        buffer2.append(data2);
+        buffer2.position(3);
+
+        byte[] data3 = new byte[] { 5, 4, 3, 2, 1 };
+        buffer1.append(data3);
+        buffer2.append(data3);
+
+        buffer1.limit(data1.length);
+        buffer2.limit(data2.length);
+
+        assertEquals(6, buffer1.remaining());
+        assertEquals(6, buffer2.remaining());
+
+        assertNotEquals(buffer1, buffer2);
+        assertNotEquals(buffer2, buffer1);
+
+        assertEquals(2, buffer1.position());
+        assertEquals(3, buffer2.position());
+    }
+
     //----- Utility Methods --------------------------------------------------//
 
     private void assertContentEquals(CompositeReadableBuffer buffer, byte array[], int offset, int length) {
diff --git a/tests/performance-jmh/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBufferBenchmark.java b/tests/performance-jmh/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBufferBenchmark.java
index 4d58a68..4eda8fd 100644
--- a/tests/performance-jmh/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBufferBenchmark.java
+++ b/tests/performance-jmh/src/main/java/org/apache/qpid/proton/codec/CompositeReadableBufferBenchmark.java
@@ -46,6 +46,8 @@
     private boolean direct;
     @Param({"1", "2"})
     private int chunks;
+    private CompositeReadableBuffer compositePartial;
+    private ReadableBuffer bufferReaderPartial;
 
     @Setup
     public void init() {
@@ -62,6 +64,9 @@
             byte[] lastChunk = new byte[remaining];
             composite.append(lastChunk);
         }
+
+        compositePartial = composite.duplicate().limit(sizePerChunk);
+        bufferReaderPartial = bufferReader.duplicate().limit(sizePerChunk);
     }
 
     @Benchmark
@@ -70,6 +75,12 @@
         return composite.equals(bufferReader);
     }
 
+    @Benchmark
+    public boolean equalsToWithSingleArraySubsetOfBuffer() {
+        compositePartial.position(0);
+        return compositePartial.equals(bufferReaderPartial);
+    }
+
     public static void main(String[] args) throws RunnerException {
         runBenchmark(CompositeReadableBuffer.class);
     }